From 9b9fe1329d9d4e850b24b0783e431cb397cca040 Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 11 May 2012 21:59:47 -0700 Subject: [PATCH 001/842] Added ignore file for Git. --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b737ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/build/ +/dist/ +CVS +/logs/ +/mobibot.properties +/fetcher.properties +/log4j.properties From cb885254b25b77854fb25e269afa16438d50aabf Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 6 Jul 2012 01:47:25 -0700 Subject: [PATCH 002/842] Automatically fetches the title of a URL submission if not already specified. Fixed delicious tags. (comma delimited) Added submitter's nick to tags. --- .cvsignore | 17 +- buildnum.properties | 6 +- lib/jsoup-1.6.3.jar | Bin 0 -> 276135 bytes mobibot.iml | 9 + mobibot.ipr | 43 +- mobibot.iws | 487 +++++++++++------- src/net/thauvin/erik/mobibot/EntryLink.java | 12 +- src/net/thauvin/erik/mobibot/Mobibot.java | 135 ++--- src/net/thauvin/erik/mobibot/ReleaseInfo.java | 12 +- 9 files changed, 443 insertions(+), 278 deletions(-) create mode 100644 lib/jsoup-1.6.3.jar diff --git a/.cvsignore b/.cvsignore index 3369402..53371c3 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,8 +1,9 @@ -DevSuite -build -dist -log4j.properties -mobibot.properties -fetcher.properties -*.ser -logs \ No newline at end of file +DevSuite +build +dist +log4j.properties +mobibot.properties +fetcher.properties +*.ser +logs +.git diff --git a/buildnum.properties b/buildnum.properties index 8640ea3..e07de5f 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ -#ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Mon Oct 04 00:59:22 PDT 2010 -build.num.last=2 +#ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler +#Fri Jun 29 06:58:32 PDT 2012 +build.num.last=3 diff --git a/lib/jsoup-1.6.3.jar b/lib/jsoup-1.6.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..b3421d59b99b2fe6ba2ff28f028b7ad476973ad1 GIT binary patch literal 276135 zcmb4qWmIHak}mG8xlZheIAPybk_Cr=1Np~GBDmx0|NZf=57^Idd2uyiMrj2JW~G0@U_it_VPU>D zKgK~pK-3{YKrsFtCNHcYEg`O|#w0Hhr7Ewu$AZ$Od;6o-aW(0Sg^ZZ6Fab>^M5%aa z&Dj{fqBKbz`I`9T&u_y_3TqULL;`n*U1>*Ylm~-wiAY;t%SEmeCJpY(D3`Cr6eXPr4XiQ+`H%5$+ zMy|)t?kayL5%|6~N}9F<`#ooYnvbJnxqQdLf4I~BjsR%)@fOM>;2LQVNTbSj#1L7) zEu%2K`gT|ucHQ4?kK!| zj@D5;@r5CyP^H56>U__FjGg7QJYvY00sX-L4-Y5css;R`w2OocKrX z+p-5${c_2mUmMhyUYLR(|Dk*kkgtEU5r|La+c}#3r+I*Y2>%W758L_&!qUmk#s1%L zB7ei7{{;th2iVwK1D*bd3sC=ZfjQ9H9_aW#z)}7Jx3x3*|M+uzfTPp@_~(DU7bk!z z(AndExR<}df!07{=l_Jm{NF9i1U=KJ{PZyU&mAWI%dLyr+1h^ME$nP5L;y}eCSz-W zlT(!HnnK@K6oCpUNV{B;-8X+7BclRlD#J(_BaYrwnC|E1U8wLTtM4~^Uf1BSG9F(}@ zaLqJ7?;V3NA5xkH{Y)@9lTofL-;nFF@kV7xUntyocNpdyHzKc~r z1J1970tg?Fx+xC=m|lNd3KLDtGGpe5dR`HKp-)PMjqbK~bXhKiG|(Vi^OQ%V5--53F+;r!#`@6CFo)dJe|y-Uo7Sj19CV?e zRZtzO^@}rsGsYN>i6H(DZ~CXAO<-hx_kJpv^;6lT|DB>`fgTzF>wl`+Td@P~Q`MP^ z{2W7)05hw+DAFe2R}^q8&_AltvF_{A2HQ2-&!9eu?j`;~yI6dD=MxN`9p9=!L=g0! znO*`P<8ol9f^*#DP+pr7J3}GpWew2!v@;umA^k@=q{_(>P8^9U$PjN;7E4G}bbLzL zC`)PrDFOT$KQdrk`^6?<4f0QSdohwitbndoS~`}Z{9D+1Lzr}2j0A=FLB$|-4HX?#!=K}I1GrJ9 zId95CP8|}YXsz;7qish#(;k;yB|m+B{DIAllz|{48gUQp3``8nM}UN0=Z7yM+D}-ur&+|$h|cN| z*2Eofvxyoj(mN7A^Hw!pd8^GQbFS7!SLjM%uIvj(JX$aeZIWAa=fibHeA2qoX(acc zxQIbG@UqIyIV&$(^H#_2DBh^S(wL#rRZwiu@M`2z+-#v3+*HJdTvk)bgWh7+$H! zRTXQJt&RBk8TL>q=w%5ZfA$OP^Zy`bMHWs71zS2x6Z)%>ZLzY_1%Eo24>$-2*?;F` zDnJJppp)~ztW2dtq5sp%$PJC9q}+cJ3u7n*hzBY8fHagK4ZgVM{Xe*Csk-ZK!^2-tdt9_6hj5#sR&&*miDuYy_k(1R(quroyksX))YdRgo6QT-wX2S|SiQ>FGb!`nuAkEL`EFS~+lk}0MMo@I2ceRQ zH*E4W9y7PqM5`t$7nf^sPcZKd^$A|*5`*oe3f|I(gWqOd&7z{%`elTWU&=r3SQs7= z#F&2_B_gnIv}1wyqX_W4wA}SO!$pF?MpGRdGW6d-+~H1}b;jO^3{kVB?O?PiB%Gm` zhE5wXl}#IETeej;gcd=h0MMC~vuwe}9hE2;P!D0S8IJJ(s>L;%P6eG$Eh>CIF3A6T zEjroT+5Sgd^j4nyjEgv#O+`hoFko~v?zbS=_|rJ(!tzpTXg7ytiSmrS8^s-*x0Xa+ zAjD(On95Bbbd} z+m7ZGiKL|;*9RM>$q8lhtT}Ke>M|#2>4Vr8GpwpZt=%p?5vFJmAY8&}m(D#bz5_@g zV*aozmZ_z79nj^|q_0;d5=NJ-FK;9fc}oil>3gO^WmOV;7r7|T7n7$A2>wJcOEh>< zCM@!_Zp6{~2xdwA!DVFrsvVZs9vd=8i13p+EE|y3Z)CWVJDr=-B;QVC0wMzc2-YC$ zoSZ6}y#T3_5h!l-B_#oEYyfX1{{yPIuAxU?4VK=SO-XXlaYS8m(oqoPuc{f%Gqkt( z$5R{%1O)HDQO!RcD@jdRb^a^LW&x)Ky$%Wns?rQx9!wow?wo_zK%FrkchyU-Vx7K> zWqL$PL|?&=ap)huLSqh2(vYktQbe~K7v5W!o12?&uMcm~AZoeg{osU4E-Qw)U3~>^ z$^C|nhwU`31mjP$%YX#o>agx%ePS2)k*{{UMF z9H#R)#r+i856WQ|3W8;no~smEHj%hu9>jtyr=!E0q}x7nn` zkj6hF;CS-*O={u_+k$X@{=yD9d`KUw1JR7;8^}RdszEwJo73%aQnccK_u|6Lt@LOUUAhetS^{}nF0 zAC)M9#}fQtj}g~vS{}n)qDCErg)D~uZ3U@~mWa732ztnnOB7Pe*@7ETtqtfh_ek zCvpifOIWR^@Sy~Qx9U0wH5m{xpBufjvLTnM?cAI3=JyZ00d>vE>%$wjR zD-V(3wC=vh!f3bxX2Iv}1jVW6Qz4S+Q#S=8G8+P31*)y=tIVt0Kek7&_eWe$AdP#$ z#FO00!W5^HgbKHdg~%nZ*ZXPnKKv2vf^xr=L8D|9$5U?#&{%QXBOBOEy+g-YOejqF zsrwV-5&^&CM0AFDaB*>)k)tCST3oWvrX1Lq_dG6-z^Iw8v*6j7Zm%f2@5rcAyxm7b zQgHVRzX$;aGP%L=4YlS|>a8UkG94x-PHn@1fxYZ0*w^Ol?7t{PCk;jTYMRp0IoGfp z*n&AdX{=IN1?DHWU3P;vS~Kz-Sd!9gWLL_p+vRhX<1JKM)}nc(oj5p+EQ2}Ar}^}{ zhief^Qu1h9@q!K=pvssr=W&-1@sb4-qGY(({&~m(x_v%7*0abrm2Z+j9E*Jj#`9zw3J?xMQqLt%7#cu4-{{R2I8wl zEX#_?E%LL4DAgBh|k*G8@^?^|H_cgyP3cP49gI zG}|oG2AHDtdWp`}XZ#ep+skti84>V53Oq2+B|4C6=vspT!SdtqgOj&LwA{e}NXZ$+ zP}=x3!~$>-Ew?=d9$XNVlf!W>$h zzFK5>1kZQH81{vv1?vq8jC{L@;i!r-TtB#59nB$dclJz4D19z-W=UK%lSvGU<|ip{8GpHT>*NPf!ZrlgZEy& zsn8sz)ktGS9IwmqDYjwL($QKns}9V((y z#GtDlOdWr|{cdpDdq)0QO82WCH{BKY$YY)Bmhyznxvs{`zP$ljlaR(bWPxqBCD3~; zhHvDIVcmK5NViJQ&}t01Y#8rEm^{xZ*CS*L8tS8fNiaH*Q=L^>k8;Yy9-r@`9iyVi zH_)|N3|xD;*LY*POzsC20q!CsushP>tK#W=_AuUosXO+l^V8R`lYu=8<<;nyNwIh+ zzC$|Tn;oT*?dqIpNed5grIJ$cfN*GY%U2M(|b>kpXvsI5fBcXFrY7gX3bKQA|lkFmi9MjY+*Ina*pl~VDF z3Gm8k6Xm@UlpsXS*&!r2^9H~UIDk2Y?vg3Y0R_XR9pGB*@wRpld9` zI2(m*3yY%#!EDk=Y%?yjbLcy6s(^5`48y;KNT(|naA6pLb#%#fsG2VyQHFDOE|_KQ zuG8P{B zoM^Gd_I2VV&$ot6Bd+&h^LBHv_|`ak*=!?dEZFO2Ri0|i-$a2xOt@^N?1UeQ#YI8{ zFZ9pf#79aTyOc^{=2 zdq#h2YT1o-PR?k~s+F-Z^VCZ)Aiu|mFwpD}7B|d~2vnpd`{Sx)u~1keSCZ4rpK{Nw zFLX#@SV^R2BH#)xH0KZkS~2F{;3Qy`rPO{pT0B)|Am{w69TVq6Q`m1%HPGr-tUM9h z;g~-GPbDz4_Eb7e+rgq;h9+`6dpaB!Hs7j^6(J8C&O(waR_bX}U0=Uy?~CrGUK zm_OmC;C>SwT^gk}=*TxC1IGNQIHx8S#%4M08fUZ<({%_L@wSFFcAbo9hfU<2)Lrce z7xjB>9SRdDCF`XaUkb~wL%Qg_YRSDdqbgHnda%?RZ#9t9swrw3eNB27q91w9sB66- znY2`|PJ^mrtp#F?vtr@U}09BvZDA&kI1CZJ{!!!K9|#ty2_Ws4(m*|&`v z4cCwq-nQQ=8z(F0SI9~|JGSMAm?r4|E$MQ5&quv_|KGA&cexA&cfEp z>7V`_rD5fXu8!iP==6(HKhIh;4}(<;Oj-x83Y&z4hKn%}i$+6M{1;WpXde~6E0@wW zlsoj5n;HhDcqf=84GI5CFY_zxkHD>0lBr*XHacLRkL~PVJnUDUW>XU;HLspKY`%OB zJRrm`Cj4kU1;;6M@_k=H+_vjLn)hzL^ZOC9HG)9J#m6Q00udl%g};Ch@&z?k5J5Ta z#-$iS&4l_#Zh&gl0=e13MVcgsuy8$r2=Le5Y>(saDnqJl*~vGNk!C;w6j$hyZIo>E zscGnIFawM^Su_JAl3>A`+wfSVac;6DP0G%p?QhjbE@qGc)MyvR*VSL+PyIuMByxJS4sTmwqa$Y4iSnv*ll?QN8Eql@%t}wuquE=x8 z^lUTpa0CiBOV7sEyaV1QiE?f#k|>2EW1QuxKmZ?XM`n6vR+@9^ zShdv8q9YuvrLSkWhJKMR{`D0 zOt9f0jlYG?;W$prptz}M!`jifqua%u@!lw~iU2wa3Ig%~Y`T#6v5D8s{%+URflnBn zF~~IPt6!IPMb{|3O1EhQ$-aZD!LNm-OL=%C2*X-ql*tE@?KrFT4Z{icvcl%jJtGWb zdPOr+c$IBKbgNu*DzHs-Dp1lsLzU2!eP<%y(S_XF@uVA9igc$I8Sj6DLksTzGFVsO zzmc0o!VBlq6SF|4U2RtH>`gRDQb=TNR9bUe#1GLvruofO#(Up3C2l;mz6WEN3?~AA z*V=YUQUGs6w8Gv&ALBJp6-|MjJD>s1XNVciM_s9p2a=py!Ys>cwj#Ve?l9T^t~Ga? z%%@=dQpsLA(`zb}#W`S8qE#cB1$=Q3K|vhifEN8cmFtW{ilr2toalbu3^D^H&uE0FmtiI&ox@#b zJ(L7U&IBFu;AIC|jgVC{zRbgnVTQS($bEAgTQt$n8EP`Q&_}Gr0SBAb_XN=;Ps28BD~@!F?W_m*og;rMe*wVYL@%kJw+!G{?tSYiZ0 zyik4^u(0Nxcwl!8S^yhgjE;JyVwFNVPEOpCR2`57lIlS(2aR+}3}KVwF?p6R$oOy# zC5=+LhDkjX`_7clB(}pmcoWK(Jz=|j6m_p)UTw%@r$kLxR;m?fNoO{ym*(TIn=8V# zyiXZ{XQpwT#EG`vBoxi z>>3KE=`!YYQHVfVGD5k!$$3xuLUi9o(xY_{sXP@m$EjZrs z4_<$zGBAJheuVblR1_@e6*G~76Twkf1U&?=ekkuE^zTDnOTH|+{xEC4G~*;gNCg{= zL2qgZU%dUPY7x-9%Zd6N*P(yb4#R0aAy)zQgRk`n*W@7gPQ}U$DKDuDN!B8uL%~Jy z*ZdZ>L@W8Ug_ev6`K4l~11LP|!d-SqlweOn6%ExjG=pTgc2XXRvH{($~#Mj7EPW03*_ z0%C>s_wnVw2DM@UXMno1#lL1+O`5+m(9H-xCJs|s_|8R|$I)mw2!IV{YNbmDbRE9> z9g-JBQf|Ep^$8ojCRB1%1Wh6q0y>y>YBO}%XX>&M!A%Rz;r>g1y6xUZKk^>#rYL^Z zQZ%0KB^-I&b#MLg+48vjO`QXE?f9zNJI=yrEFnyY0O>%WbS+Uh$V6^v3X|;5fD}Hc z5gR}bb4yB~Vvi$-__U8iU^#dhiwt(r$1~#J$5m+;(n*T! zIaM9Oi+c}#v_2vS9rltv@@SjX`9hs_vosz__ACd+tYu1?FfY2IHS(b@eyKM!e_e!m z`)29u!POzk=RcLSMtIN&s>EYvXzc>!lymK|wvF+b+p z`;1?mdwC<(=e+R?4bi2VTJb}8s)U~b_z)yy*aZ%xN%lmu0;=IA^bo}0b}oVX)OnV8(EzF zGG(hxOxUbN^jVDpoPaR0ZyRDYf#Mor!hP~U9)CcHjg@4`odR&`O@yy2ZAtd6d##!6 z+$LlxifFPvnronZQ8Ot{DW}pTfo*YUBKMHOOs_&zH~wTdP)VY)GiyOyl4Yw1=?bfp z&UUbZvT@-U&+ol4eA&1ra=P@HL9k?3p;KY7avLr(>hiK(edtKt3-p`X4W*a%Pyt=H z|FBwS|6XLH)Zswno=L}jfXCj^TKITHXcSEav#>slg3aog%w^o}3(@6SGiKK$p`lH@bO+t+ zwFfc1GzXuqGsCa;B-wl_cOiVp0E;fN;t($-Y(B+CLO#{I*w01b-*%Sid;+%0I|aXy z?zz$V#0ZT%7laG#Ow+xSslBQPzSy}(-Wsn(HIItT_EsgdJvOZwqyM3H%2>X%vQxcr zdkuh8b=quDK9sfPcJQ5&Hm7SDwfAt9(Q4loL0MLmjaqVZN|jDQqBpi##ufK7Sskkr zOG)`tdo16{Pq?25^xXT|;ku@_gJHv8M7_aR-|!MxVTdJn@Mu>KX6nh4T%y*8SXX{g z-g?6wzJewq8VSA^JSqC!eRfxcM#4xeiXR)pV zK4>y&-bNVRZ_cUNaAq4VIVsMp9a<=Bv61O&x_%Vpeqj$|ezFcGZIIO-pI%;=Xx;3y z!ouU5VU#rAmAGoP=2ydLEaRj_NjmT7TK6kP#@yRt)EFN^{MQ9ai&o{&s+bgUrkoK> z0ZXbIx8D5#D+ILHXa69j?LbT>?shiGJ zs+~LcgPGKyl3KELVFqy{J%BZoog|M-x05r%3VyVp*e?#_FUV^;$*^d8oYe=(c#76z zQ|>Lffl*0JVUL6W!V^;!W*5)JiaWf~07{SdL|yqx>0~{_Ra312 z&7h=m5>#0cJ|;+IZ1!vGtOBEh6hRdjTi)3^L600ZoSNcxLSZ46$Sf`37fsKNg)|9J z_5_syeGquB4*l5x(nO5{8;FfPqx98Skp-SI+t;nWpSjabVR;*K|D$-AM@vez0>f?CJ|2X4$F z?M?i+&3$>u^#bwpgW4f{x{O`wdMACLU_#MvVcKX2@tp#kcS55h!Y`TDA+lZq_t4a& zb!%UvpOS0e-mPdskFMlUg_W~JtA{8gW^UL*)VFNApiK)q2II`gnEOS|e4A|P98r?l z@WPPqO~1uPeM1|tp}I@FaZ4VAewxu-4KADXf>$495{&m(&kDvDn6*heFcYsNk8kMuB7N7-NL z6L9f!M{P=GS9uiby9OLqJs!`-G<#i&xFl9xRE-cJJLb`fG&6St4?fQJ>fF z7hu+?n1kLizL^6cL!S03o@MP!4OjR0bzpMQgs3>Msmw`PXI^7>tbAAJRgHlh45nAv zs=7r+XFM6j)#!7OG{{5CV*^w1;kDEE>-)3979#;7i?cw_$iYC?l}~Ab{p;WQ2qg+U za|55V-M-IWg3$k7Zn1yv#^*3e+8O8w_^)Y_nvLT8Cx7SCfF=A1T0n9e~3?d_vv6bdR7) zk6*1xR(1JHAv@1%x>UD)*_pflSim;uZPF{wi?36(KP%OEB+;g%ny}5aA&=0pp_kAm zphMku@g6)O1@Vl%1{MTWm4%_7o^3)5i4I7w_PbrbApX1bbW6rC^!hJsTLf=lM|lS3 z2I~Ni8kHE(1RfNz!bh7gK&77HvNEIn%<*0TFC271b961UKb|ylJB<1gw|W#(x6; z2xG$9;r1wnP5G$#IC?lJ*3I+O?GLa`=SswwYSCL)H@c}~nVl6f#+=HWH8LfTYQtgYiwDF~C=mP&P zq1_$G%?LscWfaXH+ zG_yq;ys0J=E9*Gd@{j=ZRp8yd2Yoxe=P^2k+&#S?L8h3B<0p3^h%vvo(Z_=I3KL0* zD^^W6q(O*bg?OH0k&xn&sTbMyyPzF#7AolTch+r$f~yrt&F={5kKTr*Y(e)lB_ zIWB1706lbyRBh&Cp$xWTOeH1*K#6sc5Xx(5T@A0TWFah9jtGMD4UE}hykdlQOdzhJ zH_8q?Lau1b_QDJ1;4)V{ITawzi?Yt6-02ecLoV2{T=qi{^9tL%h zOFupuP*sIC2{J+>$?%9mGfa0m+PVbI3r?s9>q`{`8gY*#>EWtEQi|A!(dBv?&yoAk z-s}6_9=0!>0Wi)lT^x@RFxF=cf2Y-_fJJ*QGedDMUCQDsY`gl!?TNAfu5~!}$9UJ5 z7QW&Qo0oM?uVJ9nqTJg38G=KTb6@O=8*e5+n9Jtf)_!yMvTnAXCVn}q8IXljO?#=# zvhyTIrr|PtVOf&RerzRR`_NV)u96>FI^{0OCGc}h38&=u09lra^$APB0BqgOdD{v3cZ z<0KgMq6&!O6X7@<{aqHWC?moS_*P>hAk(*k9lbxBu4yR4;@1m|(RZX@4C8Lm+V#l@f0-rcL;V(<8C9jNJvc_PI)yC1blyR?=g9hj=T{S95rXeVci0 zoOz*(>6KuoUnc^(Y}lmqFGH9q&TgHK{6kU_+B6$@%yvwJTp{uAXMrR>(l1ly^(L6@ zoW~|)I^AG@6ch-!Mom%4D@cS}_&pr80(G?toh%qse4m}|3Z!s^f5$2KDI~Qpqh{%V?#Xo$ddPYy6*V z2>H)2h7k0vQKt$Lp6Dy&GLnGkCmVu^zgH{don<0%YN5aF8iYtj&f#Dzu=Bca{OP+; zHi*gqX&(wIY5Pj`kivB_9#h&KBeAuC8L)uQ@3yv@*7XdXjJ61IIy^-T;wTC4dkAgpX2Y@zf9 zSV+I$eWoA?^1a^$DA5X$`*tlc()AYK-fX1leyQNFliB;9>5m^7gZHk|s zl6*|Ve82ZPo)0$AHWR*LU+(OBw(WOaxD#(~MI&zqygKFt{%PwA*?qPrAP(CrSic0< zyE8@;B;83s6C6aTRv$yaABv`>ml4*S>|;zY7DcF|Tx7t7M-!BP+S|<*7A!ooEYi{_ zS8p1#|COq&%|K9r*K{Bn_(?QsjjU=U)VOw_Km&C5um4C+|^8LqpUfh zBMxj`>fo~$NXGQ8V*zS#csq%W=+Dhbcb-&8n z)@jPT6(6*7Vj)bHc3~IkEqIo64?X;AsIE;p1A+Z~pWt{bUP`rRQyx^pf1(?)au5?-Ss#r4wYFg1yX{qc` zczdx1%zg(Gi#iuQwS|Crm2M~FEcHMk7Y2o08=&3a9yUH>seEQJf0=Vk=dr;>nX^g@ zBW#njtNc`bazTUU_F~aPoLD~tUfAQctVHpf_m`gdaml=${V=I8&SS!9FH;8aB5oZF^j8 zm6pS88teStFhsp*TC*&$o*ijr&zK}5V9OyMQvqKf()g+M* zi_Vqfz;2ly+v;#4tS#Z*A!zE=M++?3WZ@Q7GSX0Kr-aT1h7MI*f93qMmc~n?a7EB4 z(pb^Ns=(~xJ}0&XJIRurhPAW3r;W}>DN6T;Pf<~dy0_U;FMGKq5Vi!Ek{3vRrNBO zyiL&IA6V$HlxbVFXRDsgv~e_25m`M#+)(4_qVsRvLvs;}gDLNq3hs_@n|s~}g7J2p z=Lh>JNF&!sS8HrZxfF3n)PM5GR&_2?mW*GHH^~;9wTL&?y#y0!!$$3X03(|w)%h8f zRdm5pU0@~C>giX_Y((B2K;(tJoFLXLx9x(H@3KPP2Kt&gV)XOkno8%l0f!sX9PY5Q z9L&!vHtdJAi1vxDY%P1ImRY>~Ci0wcp_pd9cOI#>_FssNU_umkLLz)jaCcct@Xa1R z!{8%&(Y)4@8Y8w8+6vpY13BgcAC3{vPTbqWTef?O&(|NR6CZ)sAC0g++0Td15fu2n z!uTJN7fMqlCOg+)yr=JIMptJ0SC}cxziue^Ognq`WL>4A3>Tv>*?%ttAJsXK zBw=Jv8P6++>u?CwhO6O7>htr_a`#M+UNMjCGMCNyhzni$FtV61=pqG2M)_?K=6A%x z%MbOxMp5uZvS$1y{BqYAB*XF(hSm`;?wTDD^%;@$cNIOD#jxipwqGMUD6jPe4F6Yp z$qn77CJ(P|vM)~WVnUO(;f#5{IIq$CAK45apwh2#&#$AH0S3_GHBlnPEz#SPt_Td3 zU0;Z-?BY!{IEtly`dZ-Ubwn3Lxx5}FpRTZZ!oN*(A3UH5+>k0ajz^SuVrG$AUR2-s zrNm;z->sZtxrRN$7}&>h)!u(F41_ZqkEx0nUa=HZTU7Y{8O>#p>YuJng1wRUy%h}LL0 z&!}Z8Y$YOVvil;TLkBy$#(COIf}%dDsmN8jV- zV4#`6z6E}~b;r2w*iG`ShpflS&D19L%CEZMDbqB~`Crlop-!$dNb`7Trp&%s59xzE z*q5aNd@?!M@I>oYNPW%ln0DA_WDjgt8#K4>DBxVK^UzbntT@s$)KdXCkxjD}_F#7T z4+`<*xF7#^B;X%}Pz9`|6PQoV3+iWP@c&HO|CSZ}`w^TZHC@%ur2SpNJfXQzPlpLi zXH1-iQR&(T6cY|95@9N|A0fsf!*3yWfkRxG*h`7n>!#*7QcuG474)5ESU|e?D?S7j zyXRx(xz8VO&o964o|f%E=Cu$=F?(_5NKKx3nE-75B=x9`#&mL<99&!!>yae>w6?ZE z_8B#9?%Vpdm(s1wJEWrX^e!o+%_=IciIvqj8NWE!<|5_4W3DUTmyeNPF{szI-N|j} zYdS-D{9;j~FQHEg*8ynBa~5-DM^>dSnKrAjRversm#3)HcbKdlWJ+s6>as@xu(Ro) z9Zlm-o2t!&+jTakDwbp^Ybr8Jw3Y5%2IrnP`7q2lL>q`%BMl;lc`<8SILNZwxr=a# z&t+*&rJ{Z}ahqgRPqTO{fC{PYTwsWrOjhbZK9mi4q6*0y#*4DuGto517iK8fYSO#T zv0RuK$d|7HJvI9xBef}TUNT9JrWAv4)%86KLn85UyL}mH&JEK;&sPd&=p0hd z-ZTCFyuOENKK88t{TaWpK(4VEnWTu_6Q*eR3FTcGJQ@`g<_nkzCIA4<*oP0ze^Q*6 z!_RH74thkyOdhMqb(y5?8VEKUN#4(>HvLXm_anx1ryurlR>!YY)3+b#d#$z^GQ_+P zDsM3Q@4V|-zqkz$-~X@F`giNZf`P$oUr*6c5avNnY`XSL1#SP4+k7-vIdSX zP!!Q9zqr4$FE=6cU{UtpnE#TL6*z4{FNMW!vWcca^ejc@FEZk7h*^3$pM$k9l1~pk z)5F^tcym}~3KA_LS9qnV=2oSQeTQ|PuN{VI(M9<98;cCKvDZo9vqu#AdAyPFe^F8V z<-ARj)|n={B*q6KtTs$VCq|JuVv-En0E8F57^+gKFI*_*L!&HP?4Svk?H9U7;8s%; z@*mra4&s+0;>Hq~=A(P$W8%emz6?mwdcSK@H`B|Bx5=xibx$+J_qVH>A0W=#n#|dt zPe#^fpm<^+`7quDb(~DZECM?8>F9%_abx zaZ2<2VGb|hxdYU4*s`7!~EeIKvTp0V5 zc#5aY!_!z>17mRbOMb5h#v8^$b4KBJ!BS=&LNsm7c3VktnTj-62qcg(;%coXGe{I> zY+J$Tb3OZ76u%&fa>0~;smOnzTAsLR!zi&)4Uap75;aCL(!l1m;tV`EQQ=%XH?{u_ zj=ry5LqtA*N~*p`p0Z!u(vb}Zg zqC$kt`T;sze6*KL)e8D+&d=kh>ftb|ABIUIvH9&j>X;5X9sb9)@o1z(nsZ%CV8N5` z=guOtrXt40d6w*`4%9%delmNw!{g`t<`X7rRcd(JsU#fiJSJD7TBtzuZ>JP(xXYi_ zOYwW{qS?w0GaA}2$iUMv9On>D*>{U|ge+ph{ILF_tlGUgB1VA;f{P4ID*t{w*c_G~ z-mWIw4sx=&?`E)PG+Bx?7VQZu=@EnNWXkPgb$s>78JnyE#$vyv)z#u(lK9S1mr0F@ zAK`EOBUxVAx~M6#2bplIt`jB`QqNftfVNWd8EjxMW33^Iz({xpq#R|QvoHydG3%@4 z{O^&wVR~6@Ma2|eOndNNv^(^;-;BRXP(QMr<4L z2hFj4HbtmTU5~wvinMdcVavY!CD}x3m3NYylq^kQ;uUg5`AV`U`AT7oGCj&2MT%=? zuYppuQ;HKEw!_Ud;!cDOS)$rkfzH-G!?yo9EjnOBsUk$4T?-@IrWAu~vas&DN| zifV3Djr<`AklNXv>p_gS5JxNtk;!mJqRM!dDxlg3N zO=v|R(94^}oqhstL5-P;<*r1f65V|`Cbp!_ScEg#r2U5S*4NzKZ%Z3dYEwu=$x_At zwZPL}8TlBo9x@S9|IQwE2Wd%zajJm-I6H*;nX51YzBE-5c#Z6Sq>TBB?zj=$9u=c} z(HFE$W_hUmXJ&PuWR#>+4a%(`^Y;!ARIS04qgKG&i1geD(Aa<^rbVq#+r=$*cEA8h zAOVr@s5L1`5LXC%ZHR};aWI<^yw%)%w@4&idy7E_XxnTr+hM}Cru^YAf~HOh5crBn z_%%umg1jB?1XOcu=%eknbxtdBVe~npx#S78?C6@H2>>HDl|bng)483Ifvjnhg*fRD zc62w;1Xv@-JeUNe4A@0u{PRdejV+B93)GA-Mc|q-N1}4;;kP$R_Z8qmJw4C%!}#_C zMymH0ccICH4(;D0)AED8lh!KP6odl!keAKMj~K1vqZ=n=m*H}zSS!B$y~~H}>5L|7 z^W;SBV0)0(qg=KQ9L3OcUj&Uhc3_(s4Xxg@TK3(ywI6!|0Fl0^NL_Ub+)+~u;d_}* z&WB}lnYb>l#TH#tkvx12HeFLQlEDG&YqCzkf(itYIZTtS=m|kY{C&}L0xMP?C_R+a z1m8}$y|EOqnXtDx1Vo%pxYe4hNCY)(GW!>je}(iBRfDN^Bd98{Fwb?4E9}vVRecE- zSa6NMuw57Lko@FOhJ45crWl!xrvaLaA&`gpvKoipfm=N zdQgxjq;W_1+UT$9bhI^D;+N`LebOiE;z?=qn>89et>`r>nyRWcDmNONBIcYnJ=xQw zsX%}He2??I>UdiBoaTATyvp>1?tYtECW2&9th(JcNLLQk=1>jS#!G6mXqEs){*mNq zPo(a?oh$P@bFY{Vd9uhu{5YXbCjF||DbUm}W6EV-1XqtDAOhY3EsMJ9luSoN7Wl|U zYst@I-WHk7T!lHONngcFYEY)!1?{6lIcUjM75BZ$+S#v>X~w)u&OA0>nTkWYPPloc zbe4+CCOOVDtyWy3Ib3Qj{8(hwK#cD=pR7G~lYG#Vb5%P~yiv9wccCMwUA$B`2v;;* zz}7P1V08ln4VQV=yhZ|mtf3MD?V_g?n9o+C8ZLXlPF2+!f|i)L1XMY9n)lIu_sw8R zaoId7e(E>@w`~`ZsKGF;iNC1;`XPTm7QF``>x&ZK%rxCABYQ^*UKT61o+Qk5Duka2 zID^v%!rCQ{a#`HdBGcdR_1%{gwQ47Ki0ib-+~Cp*(tGRYhZouM(l`>vUrtLu+03=; zFSUwlwW|tV#qZv!cnODh0Ee(6?1^2Z&xmMsI8I&XyTn`lndXiKk*kYkxV)uL|A@~( zuipcIyU)mfi@WL<`5lY#E$IZ=%6TB*tW+{O2Ai9X`16{$7yf%rUaFiJDF?k5q9lzu z`Is%Tv*f|Gh(yr@1nAmnQv7I-zZQHgp(Zo(Bwr$&<*tTu^{oHu2`*#&pM;vec*@a^BRyrjwEJ1a;jN8ShlofYKN4-#Fk*ZY>cZ|xK zJ+|lz4MxMbTvzUPyeN8YJDm8TIp*y$BhHN!Nzw$~FOmQI!x*-=pC`bLB&RPKrpt6^vBaBD3p;20}@Gs=3cCPLs=4+RlMgTCL`I5 z=4wD~OYBe7Mqy$o;y82O>5;S6s1H#oCW8hhAuh;YBrCNe=5T|NNx~WOh|F{g$6DOp z@eUTVp?A*SHYA1vr+vNd>-ym%XZf{FWm3B{#%5?RC_5gx=EiL@lQ`{3!9=;jaMdcT zp47btVE{{S3PTn$E?1n|!|lr&O7#X(%uVdbp#8N=XOdN+jxf)7q$^_?2f3eC-LAyz z76WCAB*>W@T3QVqZYC?KN-$1?nT&gN@+??aTT@Ei-Gj%diibk=i6SjI*6yul-8if$ z#Tw^2kT5=nUJ8>06(~j(>J6`1m~cP>cE%CBGnm?(Api{ zhVsJ9rC7#827}QEr5k9c#Zx@(RvNl|Jy#Z(%$v?b4H|~#hD=wnpMJTDX(7}Y;Pghr z3R#a?5oeABE=*XtYpyKL2^%@Vg;cJxW|6)OXmBq3feQ+!acp1Zxy(5Jh~bt@vX)a8r}pf7?ORDl?}9*a z3=j5~J9!3kbH?j?Q)K-WQZT|lRBxI^51zi?OqJ<00d+;AREEjlDWJKWn2S~ch&^G) zMoAD`+}e7`8Px=c&^GX54z94$q0k%&MWzVR-NiRk;rCRe#2RpH-ZRGrOkY>QzTRt6 zuH#xp)G!ax@V|XgXrZdN(4$LY;^ZOIm(^iZMFjU-SV^|KDlyGmtud`NF0t!d&Nfck zN{`w}EY`3qlw-XO9d3mgsj^tbz;5((a)4Zp^(_7FwITYmo#`Y;L1vhl?p9sc&qT#( zH(nh*Cxff0WwaIaFq$Tr$#g1v-pW9+kT~9g$ z!Sdkj8KxG5X(0Lt3L6l?Z>)G|mGo7U94QNGiw=+41hHo!Pf3e2{%9?$J^$GnnEC>K z7gpL>49vf%YZQ=I-Y?!DrUiIqZN?dA%~yvXF#F!Pxi%a$sfUm}D1no7Uv^rxr*Rg_ z@CGNhk`3vA6;00{QIDvlB5|lDaf{*TOOM{W*TLfvKVc7lOQk^3NN2)=H_&xrIlY1u zg?~0WA(;S2di2ff1e?XVi7LOS9s*2c<(RDH+Ldp&ln!=@{Zj-b3)2p zHa8WtZSoov-Fy=*+l?BhH$xp*A6(;|By#wX=F>lVhf}N0VtOi|OjPzKSIy;>R(Jl` zEwGMRX25uZ+~pm;d+y}vKK%MiEK|2XA69Ecb+9yvgp)~axO^XF<5NQUqfOT6I!)Af z@r3?fE$XvqZtDatYIFXCpl*}zr1IY3NEANvlT>IB6m0Qi>pm&^YTDQNeite8Q_Ak_ z9=>y;!DVqHw~luf*#=-iKCR(pZj&7HJS{8XDY#l!(H=6Bme|gi@WkAwRHk~R-cD$h z5u`YcdLmg0f`_nE&KbR3Q-w=(-OpY@SzM*boLAlkJ7ZSj;^KhD!E6$h9TMT zcC@@@QcwNvgh^X?*q=T(a#L!}Dz}Uhh2;}=Lf7 zc!pT^=*D25czEAH&e?Zh`c*|6`>0<47?)mNqV@G>R{C0D-Zgv6M_ThZe8#a(n$Q73 z`vk^Gi9Fm8l-(@gI5$y`R&RV2 z&=5%KR1TUQ)sK=6RT3jy0J1O609~2sd0`!yKI*o#iXwY#ZfDCA9f;6S613^@-qNp+Tk7H%wwyM#2>&zjg(khTVe@7L`P+L%R5F zml~(YL=@cA^nOT3?66U1`&0($(czU|CY=c*F6BFs#0Futqdze~I<$ycQzm9gFbhOC zlV1gc6X~L%iS&!}9vnFE;jge1WtwVInDXKOUpv6EV<#o+|;1P%x93d zaYqMG#~76anD)2B&mE~C^p`jTpwwIgSR_&<4%(LcDZ(i#tnTUWXK1xt)_=AAbt+oJMXw-GKxQ!>%M>vNLNF+HpZdU}tZp4}w#zQp+8N zgNr6Q<|xo}D9HsTD|pyN@J))+lSI_zI0n7K^p*bvR3w*RvRzFQQGzBTStM%RuJ)48 zXDg~^DJ(9^UC9d1aa<al0D{Tm3eZ1g*q>&ULrzevxlt%Y4(Z!ss9$g zw`%Bp_R|pX1KwAO9s3$OCM9wR$=Vxp%jqBCiA!dVBDe7~Gttn$T#Na0XpB0T zG+YU&teN~#=V0PNe)Qzy!M4$Nx}BI(*2uB?Wh+^NX;?_8tKmJao%t|r*%{DP-mv(O)9E4sj{Hrrt5vi||xdxUR zgiHFj=M2wGEjclsagI0li)X;ewrLv>&ysCc1?@iIWfEBlq+`lk*g{SKIeKV(@@MMd7~PJL(Wx)R zG^Hk2`FDccldgywY`#ig0f>?m=y-*$r=Egsd6C1_7T_>0T#|f6;u@#s$79G=ROOXP z2t%2jJoqwytu`=0JVOi+NDtDi!}NJm47htiJvqGr13~K@@^zV~*>qW@0rVs%UJc&f zLUo>gnk7&&h$14EGNi$BJ<}2?EU-+bJO7 zcE%E&dAoMGT&nbV2Jf<%#UGB>9!+E8-$SqQ>Pb5wXCRmN`2iNK^NzMHdXv)5u!D)a z)+sye(9(pFP@H3P8T3X0O>kzmW8IP(L|v!ef=ud+MmDpMB0(Sw=e=xfQPEgA5!WDV zA90P^jHk`zo>_cDn?BLnbX*1W(C5TnpDy+5WR-?cr|Hyj>7}VrwwDb5>3TmF*iM1| zo}WMz(aOhID9BA2dJrO4&0WkXA0{myCmN)>8G}?B5d6F@c_l#EJdFI>)7-n33-*gK z`pYXSkup*)Ugo-W|h*O{=aB+K@ z+*yBXopNCVI>_rE$);{He8NPc@ z(>c_1kKIWJfR=va3{72sf@(RArzkp8cVaU@^M=NlE}ju)Gh*KG&vd5lyVn>ffsx=0 zMVKaB7tHP_Y=d)~yLhJC2#a^d(9N6Li@68ukZ`+0dy8Q4yUf)*Ez)r&Xq@?&7tkw` z{q=`~Lwade=$_(XdM?4R>(`0NsnO)G6WUWH*$uBPHhU4UY*C#)F?|Bf)Q2$dUW|fqA)GdYvTJlWQkMqZlgwI5;%(kZ zvI0aMaQoz14~FiNidWjZdU!svL}A_d>U+z=t!~7~jl}vvL2)0?G~mw~2!CS|>y0zF zI^;C-rMlhrdyrS-u)k<|kQ0n>d0h02Y>jcfkiM^rmg=qO|Du*t-6ZpiCRSe!f!m$L zF&;!oAx(5Mf=-7wOkLTuZaPZ6Fso?7(^@&3`#qLzCCppyncg@R zG!W9|-5FU}W2E&P=`|0PPIiaq9QWX3RvH~9mmv2~R`1Kb(Rq=X16byO*fGbtG~Yzu zk(Lc*bq{+-q~88E^G-5WQ9d3osCms-e@)J$E39#BWG66 z4il-bVEmnIpJAL&-m?U;wXt9OH(Q)0X`^e7DTBEzsLj0aM&7MpUzFz(xs@G1gIrIb zB&JAH7dI;~cj(Nd5uC(AfNX?!M?xL#68a^~ppzYarC;YL*5wV1uE)b!ey}B}6#=&} zMRjx6zs)-p;nM=8Px=4WH52`cyt&7SrtuxLe#NPNbE7F{tBKnjAbW+NEAri?`URtN zZ>RJQ6yc7B(_0Qp19r?G1V!UG5Mr&5<##57n9D-4d{58d-5pEuV$-L(&cVGKmmvhp zycr>k5I5UV0ijixwH^Ro`t*>aaQeZcKDOXD1OyQ=MlHK7nDpVjqFL(tjN7D}Bsmr> zGx>WeRaFN1iZ3)w18-K6`V17e4ByxMcb#l*=mCv_7n>^doHFpREcqc7#-vIRyPOB2 zvPdW;-cq?jQJtlF#n1BdrwSVK#oS5dq<-n@*_R2prn&+bknf)N_@L}NQuWQ|)Y_+M zjG5 z$=2|Pk(qhjeM1Y{hC@ei{mG9BM(xgxV!S~J4#)OR0?D0zO6~rgg^Ley=q1Uuy*qBO zi8lpX)T|y%Jp*Wh_DmF7z1%6Ileh1BE%t(!E=L{vn!{_-pVv!JgZx&asN|9_wvxgG z;=wSSN`OB~Adza&%LP5D)Iq({YdL?&s5CZyb9S4vM_(8$Kz!Jef#*IfK%v!;=f`BUU`EeC?h}iP0#%ioPHZLvtzVRM13ATF+tc zo-q&WiD`O}8xhauiL8|)X3ENDs*;Rx^KfAt!k>$V#Fi5rLJ_~AgNevQ5F8roL>2x^Qjl*sP_2tFX92RAX>a)=KS# z8XgGnZ_PeXUR9o{bDYt`W-E&DX)7Ry!;p44#U!2`_wfQ$bCrSHHsa#D@LHLIO)4~6 z=TO&bFXW3&Ol<1AbY42=RqXS1OSvWHi`K} z)_&H^u!irc{?xRCnF?0um5w&{>4a$Aek3e^&2ZizbG#nDAZJiKwoShBPrKjvxs;=N z1SqNM05>_K9?lRp;yKjG@9U^oH_&0Mqa*rY8^(^@H3el^2vsP9%3lzDe6X=L$H-H) z8J2WGTAd#}zq%4oycpnZgmI$)MJVNQ7j86xt&WNZ8oOrAV>EIfwgFg)&zRrVDT?g~ zP#FYKN8chW2PZSu@1otd@8lF*Q(WSvpo)T%2XTAQWSYoTH7OCR{jN53gBuSd))+~m z2F0YqCg+g8fqns_+v3cgni~9b_BSTeBk-j!6^fa#o58JH%RqO6ejH<~k^#j+65rsG zgV{jxZ?>7^6PC2;TTRcAb(-nooI~W9%z2G>Lf?}1ZT0NZ4pcN@#Ic}Pa`)BVCwWU( z?3IK4$QQZz%>JEsOY-83a!5#5|2>8496^gORg9k0l6?X7`@boH{}C1N7ns_+K?4D~ z-~a)U{=c_C|4RZ~r~&1Ls)qF?SGTfa9gLV%?gu0VRbwnMCr@Dmq8|N2(UBiSAV)LU zIF82JwW+=xw{lgJ($c0eT(hK!$}(S-w(>wczXAWDWL5L(jn}I4s{H!s@nEX(V2rXI*L@ppK8fV_Rf4{8T#*ry-0AYv*V#E^WO8sqhzD(e`+ zjM3pSZi)J!n|M8*{_N3DW$t>2C^tT6Pvw~MH-10K_RN6@Q13zpuK5uOz1V-pK)%L+ zU*~M|5e9$%XAeXGvyD{CO&P3Y$c{H)nSoT^3xrSGi^!|%FSl(Ch2Ott_*@V!m-sVW zuKYPNB^z?P9@tAZ^w|qY4{}?g_k9qki{y|VBOgHyiaMLFSJX?yiVy&Qky<~S2oG?wsznZU1d3I%mVOG_~ zjp{o>I-pKDBsKHPSMC#EvbM2%R4rl2j@H#dk{XyTry#5=iH9zJGo%D_a(aG?bf2xU zWG$E*;id?}4w>q2Jifu7J7sGNTeHs@6X!G@OieJTyaXWMXQo9z;Ft!N--^c}3AgioIhE-aQQ*EM$PVn|JH zU6JO>jI44C&~qS6swO(G^SXpNq`u7AdT<`f1E-C}0NXw3F*iOE36uz9Ji($G3Oiv~ z2}%rGF+1KM((;Dt%*K9emv*un{{s-C`FkvPsw(+s{_wD>Q=?u&c9S!~vyLI^7o6@; zxHz4YKPk0J{p9)RJ+p>5>bS0%>e!XSR6#0o@(R1Yv&pQov+aP6yDo6!dEHprjtq}> zO7(q9UO#G9)9ob!AKVxZBW@h(u40niNh63&Hm(Nz$Sb+N|&c=^)wMGPKd?Z*tL3OV3(88%$?5Bk~ zVKuc=;@9H$hz9j$B)@~5Dp*ru!KT1iUgIqmUsG?VO|dWbgxr%ztV~86T!Mo8!}BP? zmdkgd+~bQ-cU@&}M&t@9rnZ|lJdLYa1}}nZbt99P8_&&$^06+#sfURh+WW$5p`i($ z7_GqYn1|N*GQb(@i%MyaIB7y7mqb(+7wJ!x(YoDL=?eVfn?mDwp_`S~opez5fP@zH zA-r~i9x!R-ls3{dFW(-FXT_)cESIX5_PQbjGgvQZ$pnm93_3jtGVALLrH)Xm73S?#BCz7QVzn0O!Z8x;B=RMu4Sqf{ENxPez`j8sty9u|K&so#qikZJZBf zc~t5rxv4_TRVFC6R6jxKamL|iRHd=uJGc*2d0Pvh1~R(hWQ_4WWV^AjJI5lA=aedk z{2Jc-O+w7G_QibaI9>!W>ZVpGI1hm^GF|7+V+xzp4H;?c`gsDXx|VIJpX|mS;Ix)a zrRXf1ODhAimvn{om>=V_^?rz|a6LJi45K=|$X9G%NupkdRD$=U(*8KYdY9?|tK_!X zQ!6h35)_he$`%!HYOq8M0$WDsA@pE%pb;S)mO|v`82#IwsyAm%=SqT?0Sajd(}b*~ z?-m_&;nbfc6UZ?DAJem$6r%G}lMbRCkGJY~c-b698MeP~yCe3g#E%xbqMB`q1nz|I zWCu|5drwd@OrH#0LLx274{sUjMh^KxV*>|wgIt-%pWhh~d!dT6+EpH(P86!lNjYm9 zC@fK4H}+3|l@IkGjL}(ME|i%()$b4n2hV$!QR{c$K&@cJRo=jn27O8MQ%dQG+b7f_ zdfq50{CuxN7Enf%cw8RSYn+k;cd)1G{Sn2qdRR4s)_H2_Ftqnpc=~wkMH4_pd@c%h z7kBxNsUl2S>5Tjf);J<|u<7h`cpvxPMaqKGF|@R4bf#O+Jk7gu-|F>}QF6MR8N6Xq zx}4fvu@$#hnNf4#xnMKIzBSulQNgOG-{<~>gr4ge3}(wv2K40C1t@7Jf#?Y2_w2IA zgZME6X=g<@rk3lMxeoM?l7Cx@QRrL(&Pn!J5&pL91^USS^|1ZJ1?7j^3?}=l zYi{2}sDIL6fO^3;re#Zs;x&6-rC`LV01dj{g0?mphDIg$Bo8SX^>`@(c)&)$OJvWl zfdc>Uoq?&*M8V($LfsDUW%?-dQK}7LGz9sXfavV+;;PVM_na)NQ?PA2(H+pOckp}$ zrHJZa8xd?7$YB-{_r(7I*Q18n_6}3{LVe{_X|Vjj38}uQTT>o+0Mj<0$Ph zEbh2)l&ZwaRFbaDTaH_4*$62ufV?ychk4kyT#R6Fs~Du z&`xBYn3OMXj&}q; z8P)ZZwPs2Ei2g9Y9ogz*w>wdl$W?|T4^uF0i*MVF_=wL@caPrZ--bN@$y_;VL~**m z#e0pg|A}G$*N}{ov*Y(D=l@1_M5#`>p)8?&4y9p5?E_N>nE2Q9>%;!7i&9!mL5~DY z`NPggRtluXN`fU7z->wsmWt$8AhViq=(gJMGQ&B-aqqqd2Q!;xG+RX2PWKvO9B=X{ z)6Et~3#tSUv*C6$@o}^9*7YX(^>W2$2l|Y}D`m*$GZPJB8h6}p%{95x5qvcG>hJz! zh^vZ>eHcgqt$^&m))WjQVJ*lZF#JN&)Qe;&5E?<0l0HC0)z?NPEYs0~8dBW5glJx3 z)l#uT3a7@Ry>ML@tj6-r6s z2f*tdvU7&rt7X?+y3x{(2g2*_$Kl)@&_Ztd%$`FA-dwza-$dKxf$F5~hf=JxZWZff z($ay-YEf*k^7lZ-henvzu6Xm~FiST&%qN*D&A#2V2RbHHbeTKnFoU_MKbqt>N zKh@;AY*0Tc(&xCMUnpBRhhAa7=P$60I>vjP=yWMX^R!i=e|J zsmYHCG}157E_IhzHS&&Mqbv43#YiNJyVofhrHp)?QNqxAOt3{KM%86V|IjQjATXRO4d)J7!MEitb{L zn$L@?0lE{6a(LHIEX5U~d{sidbKX^9P3EWPwGyj&d=j(l7r{i1b3Q@gvM7(A25g6KZQJ%yhflz4Y;|KW&|nKa$5k(^GvZ<7@jj zQcbM!k7ZNMsv0G}FFA$JI}g$kV$fT_6mLToN>_AY${An!h&=Ff=>rq4H%)pa0@Y2d z-yl#6layBO$Z=mmT47r$I@&yTN7ZkZ+?p?MTxXGc60a(rE}MucH?81ti z(tQ_aW5rh&5~flaUlDCxKU^yVi|g(VgTU8oV|OB`N2@#}ty7XX_9yfor6Rcn01G4> zl-E#P9EDj)P2HhbZB2s%VoS|~R2l=bCu9~6YcQ_4GXBE9Mxc~VXgYJZJ7~wC7A|O7 z#YQ33l-4tBJW}G0oX$k!jmr-#XJB;58$>HAgl?9`%cQQudx+1tcfWN?Y}j`UZ30?xMOfUY1)>9bD`CNLTI6N=@rrdG#1q+54}5!`J^4{YHew8r;8!K_b70LH?)P z&cCAHxBH5Pv4Qiy+;)QEw`_4lP=~r}mvo0+;^G!<;}PwpBr{|}Eu<99B{g9RE#+6K zNW$kEv@15E>Sunjt?H=lXQ^%HX*>1<*iikHFZp)??g+;1fovJ9;4?FqDL# zT#EnU_*4Jq{2K(G$|zU=x@c9Bgbsj}1( zNZn#;m><~QLT=B|Iw`M6PC0*{T2E>3(|{+W)!r88;hKv_P)c4mh~XNIApM#6+@_9k z-FjUU1l;qMnb*#XfKb2aD31u~B+%kMTS<{pku0!$~ z`$DUfshpBY?=xh)yl;$qzMr8N{PWWOB=2?_1 zV`aG9OIy~Bh8KtZ4YZiPsLuNCPm+1Ojd;iGU#?cTf(RbDkNj4n*i@BS$BDkeTl0Ct zBy(}&u@Dys$2dwr(^b^f2wVU5(f-3@A<|>ahWgkWB+Qds;;Qw zh#_{;LPcQ)FVq1ULHyl?l*rk}i~}NL)#@>bSb^0LGyD@AX^LtQD3f%Q(QyQN3+(aN zl|goLL@FtS`v^47OI)^0%ZM-v9cP%g*t4zuvQle7h4%CLX*e4=a+euvqlI=)hUU(I z^tTF@m3s0G`sLr{90czvjd3UhZ;3Z_oU};XnAK zZacl9(gH!jzY=UuA6Wv}xEm(6(;FWF!H*^z^QthZfk|u1DBu^)v|17i%c@SCdR*ze zT$M*yoXM5iR6Z%q&@0b2ys}IzCJWkG(ze0L2n)mj+Y(c%E`uXyW`IT~v+|IBoWGaX zq$E01ngvQCy^R@cRSxvfPty60ro=fc*X+LvsRV=!7Z*p)DT$S*t_BvZK5((P~DtwMP6N9$h&I){kj=rf4ltYDe zT*OIHt@$l?qT|Lh4~u7(TSW>#Xyu$cPhwT*V|H9M2aH;!9uaML%*QNcC7LvjZo}oN z8h9V9sxe;L)Mj!vkHp)rcmkeqs}`hO%<`s6WQPT&rKNVkBE+D52VdQWU3Q6sUC}59 z*mgz@gQZ{a{Vf5%;MQW${1}3h1p6@`9dfpZqxdw>mH&P-9F$uf+~gtTbiLscjakJK z*B~~Cn#U*e-0kY_ZV-SSuj-IiS}}v_0rW`g?LIA*uV)SJkSbbe{+rr8_|S9{4&L3r zf@hr*nJ4H72cu0dM@R$)`MSdXYS`(1@W<30{1kV`eRdi(dRSJ^85uUZFsMj8a_m0h zO*MvnB4Wm^NQh&?;x5hXL3yrC9!;HC!bC@P`K)6&8Jl}45iJtLdJKhzN+3$uknE0I z|CMSWO8#~DPGmygj-Q7(=yrZ)@{gAiAzm5wECmkas0{wH-fXI|cJ19{yidtRD*!3P zdV(0;2t8ly{u%rW=f#UTJmSkWW!lDhHq4{=Owj;7MJ)ub>*t`|s@9HKg)7IXExn#< z!k)VdNAyBzCRJWFm8DsS^G7xN&gINF3R=Ocg+|x|PvCB^>^Y$()?~|S;Dnnp|5iR4 zUjiw+c&WQeD9kPZI^4&;)PkJxK$78uh26jVXGZp+0KWU$soZyN$iqJzSiQvNg3c>8QNa1T-Zyml?~Er(RYM6?4fQh(tH^8!9TE4(%+Fwo@!v)amPF?Mp<%J4 zNJ0A24OpDgMsqxmASogfU03C%o)Uv0&-!IV^0`!FQuw#|iX6r5nNdS&X=QU8Pg$Om zZe8{3FHhZDUqIJCeIoaOwL^u5P$Q&qZBk5g2mbWH(3lDv?}gT>qFSQX6VZw04iXwf z2jPeKX`?2)Z$-{QMW|UL!TXMj)%Jj6XlR?>5Je;}-;5FIA~}#ozu70cREN1IW>?p` zXAa^JZKB>Zg}VI+q@zj(n@5-!y>rxze@MMgu_=3%uKs=Mq2YuI9LuYsc0ml{>QkHCQ^1@!sXH5ze!1p#Z7MPOpp zDJ|3uY(A3Hb+UFCb&U+npFVTiWe|7Tcueuw$-Z(YKe|kY+Vm=oJq8QQOm^ntQ8AtF z+>%2m9VBfDFuaVb#cwv~8V0;}9SOOdz=*b@8l670o|Bd9glg(*lGdIqX~c-*e!P}Ung`2jt#MuA`Sm;~>|6ehiNbtLi66r%s+2~cGfl{-#QtUHp>e1% zc*m)#QM(aEd91v!s|#%|ojGH&#t0?)kYk%Zusg45V>ei(9F$h zD>lS!%5EykQRV2gxR15czlyBrE;sOo%-iDwMX*Z@MPQU2D5vh6qa=u&3DQ)dRQZi7 zF+<(n$plGTELQ{8=QI9t@*>=og3{Zu8XcB}0zE%OjIguMY%|<=ul1uNvP~Z9=d1AF4_IyzKEdI`V%T zx-37=9NlosDk8s9A@>#9ciog2(6M%clg#qBlkX8)z9PVPGy*-vbV1q$S;oeYCij!c zOENU|jM_})?%^cJ>8Zp7lA;M&K!J=%TjBFsBu_91TJEuD5_W?(|W=%yr{sAXhp+0aGZT@DF z3|Xp#KE}L;A`gGdGld)PhG!%&BDS$;)Y};iD~sY9-;IM;z?VfV9K>n@VliCoCgrRO z?SuUkoE5R>6B&E7HVaChFW3k*!0j28@M#<L12v3ORnH%7kT;4%98{T!+Hoqh$^E z{jp{2S0GDgINTl-^iaL;ESS{bi1NIhhTxnF%dZBOJF`1E#C|p;_zfyhUY#Euw0f-0 zN6*2&H)EzusjpZIJ6fgPu`Pb5)0Ojj;;{80zBQ=?I#ujG)ePuRSW=aP2&i zI6O?6DFIz=i%AtBk;x*yFM@hfz>!P&cji4HF2{xnF|Kbw*pSzA3F=kQZBpLNd(P2~ zrxvz0#NoCGmAhRd^Gupw&K&3#+&1(#4ne9_inZEfZ=o|+uo}qgMb4cI4|f-Go(G97 z`5nk?Ka_WwZI7l2#F?D_xZmaAKVpTGQYL>pAYoaLMsOw-^N8jLnv*L*K+ddgwseCC zJGYQu+k2Ak1YTE^Kj3D$Wgk(ZnwEuwCkqEqi?WStv15dNApCDG)jxqLtlpF)|2vys z`2G?7kF5Sb0+Z8!lNDVezmZf7h~G#m>`Jh9bPPIeoL@hXz%X|EQ0~o2XbEFU9_;D3 ze}^C!x7Z(Wn+CIWyy#vHqwog{3rY@v+zh%_6$s@^=y$>O0z@$W+O584-I>@)+ISnC zq6C#!l9+->omnpYTH8pdMe}&@)G`A>6ptSIvv#uXc?)8Jpv1}6$caIlD4>6n9b1*c z(9;}DQxn0ki)S$IG>YdWdQX?>^&f@HvDgo&ukUVyz_-IT`~UqG|G{KCnK+aD>-Zle zMO7^|92L}0S!AXWW2#ueV=ziXYoQuAiyLgoLVi-#LaIc~>dZhRCg+siRH*U~$`1tZ z8mmNo7uw{h4+P%%#`%K)gjdk{PI4h^!O&x&NfW)U&Z*`p-gt6f?>C;`+)c^__LQ>y zYgECE7{i!!>6Eb}1xH!gX558Rraqjg0$_615(5XF>713pEY5SN`WFLOSo^{vgr|cLg zw}8{zZURA0R#2!Z64;djQ-K3uVn^=<2CLbt_H%Sl*}wPPS-1n|V=u1U!%LSvU?aJ! z_6CE)GzH6e754g#DCodpY%`L@x_)-+G5Hyj$2d1Xp-?h>UTs64f{3*<&@s%GXSD0c z4icOwxRniu1vDztbSlQu?Z_ZapLJFl=XL^08n6B~l5SKgbfFqO1EHC;7N)yIJ6p@y zO{^73bYL}!YzA!X-=|^I5-E9RthIG+*{A+hO+&4r>MXz2HG2Y6bC$bEV{&9HRC%!U zSwlaGay>I)lZP`2*HJJ;HA$2^O2pT{MzW3=&35j8d;C+B0yNjd{ZY zHoN236#+3qAA+U=IAZRNnc!E@8V%JPZ!N< zc0dXC#NcQ_6zQdyL@BC_C_;$!-d?Q5yBXmgg6h`TvPA{EAObpzfrrQbh}9`Yw)BlG z!!vk-MV2cQyGcf^Ya(-P`CNk{gx2|&&>JJq$>=HN)+xa)qABB#j)ctukaRbcgc<;8 z?}<*Wr3;tJ&0TTd1xxcvxCz$%O$2b0+Q7fzl;>{%jbmqoFebhoZNmY?wo0fmGFUUG z1=@=&Rp+1?D%E#(qdY*qg=Jr(3tBQH;T31@BAVW1Xr<-ag|<&kVd{io)EHwr3adIZlFLN{{NC^KnTfqDTUGvI@8P zdUdzJO27}Pw{E5Z%Ooqd`}AP1!~N5LhMEU0s0U}rz6#+1WuYs*FWo>d;XN5=x*ObrAjBC#af()q7CtJ(Ov@T-qX`8;k z*{P6WTXDn_1YwZAEEI`xO!XLnL@GywQa83)ZqU`WUOQk3?{pwj?#0^_YYaxe-Hy$0 zp58IR8Aw(bB8?*BU@=k^vPgA_-KoEDp)ZK;oe4(X;Nt@29M}6J9j#%>KOCjWV6M~P z6Sz)Pi*<>l-2;z>@`ks%qj^{M2t-3KAU3`Ci+CB&%p&(9lIb@NZ|^8^35F>r6ZX?B zGJ^XkJ(SQLX9aZ(R4(w)%5r`>2 zf_w1v&PE01r);w;B3*I^6D9nc;PRiho?2)d_V_!L>VAjP|LI)ze`E{)3Zehi7K~DT zQ~D=#+Wx&Hb0GHf7Z=d93gpfKKFvp#jPTPWDO~s|?rj?f2pkGr*>FD3-qqWVS;Zp7 zOPzU5If>jmqj!A2L0?;;ylmb2m~1`D@MQIUf7+7+GOfYuqYki}?!U{u0 zoiQhds(qIy&yWN#By!cW$2)s*N4zsPl)Q5{9Oi5>{yp|;;CLb_ zx6>G$yD4a)4SKStC?LtQZi4zXMv!2pQ}zK(!MV94_Er56220nYBxBrUcivrGp{-2N zU~^=@(h{GF+?+;SM-Y`RV=Bd!#H=H?bgnorL=R0&S0d^bie-I4LVR>%oAIxv#W-Ez zT-Itfq8XM?26%?rNtF0DcRt*_E`a;eqfCA-&g>4XXyV$T`V{fn$LbXW`#cDf5M^VN zg7LoGF7pkwXoh54>C?o%19u67@{9~$^NEnOBA7^7@O#7B+#wPvN6S^7 zoHZLOa+n<(n$zC!cqw_hUs}OR#OgW~@(H*jO!()rw@|NSFWn)ZkK*s=X`Svs)~RrN(D@YFCoYm*kml!>@$O-; z$kk+VaG_%Ok(Ln(qWwmeWIJ@qS_8da=h5GorBXdC#cp)je{wJa`5 zJFRRQpS+u!J`~ zww+9D+qR8~CdQYi&U5OV@6=n>)jzLa-TT_T*4k?wc%7Vm@xOBVlRqmUU3vJi5k5iI z!-xodr;)?$CJT8(5h4^|AeMOm!hZ0R#D)gH77E|jMZ}KX60Z@YER7u9LVJ2E6NH95 z)K$fdo~y7uv&KL;!HuYSQ-o3MRSj9#+(5-F?}CXRGF9xW!kh0YM#a zyMd6%ioH+nHpaq1$NKLoQ`YWE5h1%$Yz3{~o99a=RzY~}LxuQOGGJ_6cviGHHWv}Y zPgoxs=j8!%zXPL|_~!JSwxlV?T#r0lDlZIT~RM&az)ghLeOL6u5&zJ{E!dn^k#!FMopOLBuT6g zQ$D(~3`Zd0l@Je$OU5ozfmaXY=v`xV$lH?ET|ItsbR~xB*rdwMyMn9)Opw%KDJk*a z1WKH&l~x++XY@7yoTm1j;Zhrlpu#UPmEd~fSiMJ&bB=xWgqoKXp07j~$ux(lNA@$C zf+-jrnT?~!wY!VRVudk<3ea#_<7(RC<1dBl<)6tX{RP!ffi6yEAL;YCX(Jblm&GAo z#ky4M$Zg#qHgR0@tI7?c@4v|s2p(R=ruWueS(hEicvQVL`husdBGKnfG%;c|%Pt~O zSqBskPq-6lus)8xiQSCVGnCz7XEH-yY|>-+DYEK0sz;Q;j~YBj>`9H+jtbIVx)%5v zHlpRbK$bWB7fRpb)394$+gH;%35w!Q3-Hpj`&aC=`OZZ9Wwph>UO$jYjl`xrD9YT# zP7eNJqdb(U%=eKZlO|j1&u=pzGwLjwy?PCOKI&>=(yl72FSizq(`qHCHK@XM*R1wr z;dex6#UD-NA!+O(CHoV{13f5b^I_P}9cN)zlDMR4 zp6W>oDFm!C>>c~lsJunm>p7bJsXG2weu7o80ef6opv$8dzVB2#z+)HT@h1@fS{!&swxR3r3-6>6;qv&sY)Ey3mlE{f#&s=GS98)4*&F zdL(4_Cwr3~vsf8so}sJMbzW4@QR)aLsS(Jj&r^9XZ%5m-4P{RAq%>;=#vQE*vy`A1 ztvHcAl&6{JRHNh^Ni3)Jo!|N_PR9}EODFRV_>vhM>(%PA8A{g>m%VttBUQrS3?tV3 zjG~mww>;*20*^Fx2?CEs-3C@Y4ufblYe_Gc`!P4SjgWn_R5T*v(5iSXXY99JAj#hH z!HCgi!&a{MaxP`{*g8btLp!_L8uK8|&_0~rDBZK9Wt@=}5MNAMArgvgKmWn~%*5ae z*)2d;MI4wBQCLEIe#sJ3b5RUxQ{9Tfes4uhZp5#zk=FXRiZD&Iq~1C1mWR2a%VM7g zNBDIyf^-VYh$B|Vv&bEoA>4wo5YE1c%sq$f?7@rwuLeO&Z$u5eywcV#xw)BNLnp}_ z{XJKqlx^g!eHP35?Y%Hf5TN(>;<<9it+RJaW~?*tS|3;*rNY8(t5U-GlTYQ?glDN? z>R!KNTkQ>J9!gCG!)E7}ZXPvKwYs=MzCwHCN6iShYuV^fcE4M5e6!YIl~drN9TTHn~W?mYn-XncO_~@!SG*Uh7^#% z5P<j3+{fNB5AMzXDYNY@{CE0Xdq^qkSoG*Yx(R#bn#~ckYH}<_DLw2)tDE7T zx2hSQ16x5qr415i#B=2wiUl@%b5`1`#|V$V_V-eA`L08Z7|?(IHOIQ%Kku4s?ttFn zvz>fu;N2dP?wVnDW?M24zAwgpmSUSih(b)PA4G_1ZR(qtg3p1)y`k8Yg+yh@LO9Xq z`O)G5Xm0bou-I-!A2z0<#PF>><&-j7B4E+aDiYas^&nIz#1Lw#g zK6)*IcoF^{qwY!SXab{Sp^AVA3$fvrU-X1v*corU4MN?6p{7^oMqL|_p8mpqJ;vrZ zdqJ1lfgtC+xWc{H-m2QcoprEHHp3@hUsHy;g-gC4+-(kbHTkD_#KVN4Fi3BpY>O3m zFksL}~v^cxRVWYV`T zyo_@?5tWx`Oof6#la=o3$Rp`Ew%&}-yrVt}O5UQdGy6raFZ(x-5shby?%|Td;4f#I zt&S;sBcT^Vmo2?3d9Y}EF}5nx^ZD@QPHyKsFKWnk@s6HC-)L{0)?Yc9>i8j(s4_)M zJTHlpokjfq?)5Yfd)l+1&cxZeU{YjgNRgYmdh0#DBS~vFXq7zd@*e{a|;pe`q;H9PI4O?EeeFM5|uN;|QUB0yp-*Ah zr~2*l#Pu>I@Oc|s;G_+QL%~_1pM5e>4H(dA#tzOQ7QTX)P26Cf2F%+GQFZrdA=fj8 zI;y5I8-9dSVdVbTU{84OESCyi4O5%kYA3nH0vs%#Gfi7r|_~ zD9OtaR%74{Qa+aZ2(kl+nu*4*Q2UT2ciY1qiDG8jFZ&-sZEYj}TaTHpgxO|>Bj7C9 z3nSHoiYVFF?qmJMV{kB4B7g4oRE8U)RC)eronJX6SGhmRfAR_WD;??{hU>_&>xQbN zf@6>xlokM1t^s_AwsDnrpNz&~BCT=*f%rG-d>PSgCz-+)J7u;O1~jU&SryC)tN4Qt z!_H=F6ik$mBQ?OjZM7;SG~h*Mg=o{bRu!lR@3p$(h`F%n%>Je=q7-4m$1W+;eoj4K zzU!~Q1TSHR)ph(K6{E%=NsS7R#=0c7)&7^fnZnDyavb!ykdP=%noAp)3+ZK zfg_0q9~wo?a!IG4Xa%|=(ldO3>AVLB@}^l4yY7kal(^JgNu((n2Q!PWM_J&GfbB{r z+QU%J4ijRy;#2?rX)1i@BzLQD8+9waOCm4_i zaBaHNQUVm1Ks2O@ujU(1pA_?ICrvRmG8EiLx?Y2Tncr^# zWmF*~zC>856nGFYF>nVW;a1U3+At!Ja1!ePO@1vAW^fxjHrc7B zPvU>IpQYkb{hK#D z8p-}!H~H{IG1?J;wp~)Skr}wctZ8=Yg+3}yk+K7zcRG38?r*D<$!U}d<* zwnwkNuG6-Z$AHIm%UE{IGF6FY%DICc>ug;>jEJonLM+wD?5bHR6MKB+)@Fv;kB z;9zMbv&gk9E}CPfP(Pq@a?1BgB52GfV3D#mPrEEzN7xrw5uAExJCL{86U&+Zs(a0EXW{uw5!TU@J@({1{P`&BWoF}D*eVKhO`L8Qmx)I8Qc z!SxZ-q9*{ohlD9Di%a-Ca+;uEryCH%s)k->pVmn`x22T@`iHH+i|T?`3wrCxc+n(A z9$f~ZL|~+j@6}p!Al-;F&_von=?otp9Ba>1;?znE%SISr9L%H}fixbRsfbP4fvy06 z!_O?b2M%-L9uG&R-x{Yf5vZgHN-s^L#l~?7KW?RBbYdR@DsLl<%%dy}f36g2QI=Z> z>!_qH2V)!%pueglUxIP#x_3jT4As#A&6wyS4Y8wLWG$$FhqKD%*qAxzhmW~}RX?Uh zM(il6u$kpQ;}1{dp85#z+{2sGrbnuFE*2&HoYI|D!T7^|x*?oSs!a}^A*r@A$fh?_ zW*+h<^Z(5XG9ImF$pQQELk#x+b@u*WD66Q0$*=$X#mE$XxjO!G{iYOLzh{O18!Dgt zUzQR9I^C3cZyR>jnA!@LA}OAxOHwD#O3}7elS;D_Ved!KnC!CY{CVwn)*I;SzQ`5I z_lSr`* zz?gszX#%jVPtJndA$hY{3pi^xz?#(o7ch7OBLX-wU&vvScP14ktV%2`jT{7O+1X4o# z#sm^NU7m^FZzTq|9lVU49j7!O&W6}#L+vaV>F_K^j|53Wm*gpT7l z&o%z!^Fnd8VYf&xN-Ube=a66$)~y-iB+r`EnN}`Wtg{VuC=!^t3pCNBz<$Czyv|^# z`g>E=%GG8p;+&LPm5_bJ0-UwIB)Tc{r*4g+{Z4xK1n=ThNFt=4(e0w%Qvv5*Mu*#F z1J{b8ZS@xd$7CuZijgrkk?jZmjFAIJO-rg1Gv+r>fxA68l&BS6x+Gqzia$w~6Y|js z2*4LQ&Ip`|LzT5>ab3)X5^>k#Rq92#Bg-*aL{och*Dt!ZWl4|6pk>r?NStpyC@&^==V90 zO+7X`Qk^&t&!N2ykpt?CRA_&-R0OOAU_+cGIzyNSsQ-bQnk%gm8=bt8GPPIWcJWHU z`Nz~inXR0erxmk|{jcH`Mb;-EMGtJTW|v>v9a(wF0eL;>Ph=g=rkE(}9Y;;Aku@Zd z(i@D>djyrQ2%>N4sGnk%fYlee>blzacKo2)`I$K2Tx_`v)2kYd?51;{^^oCH4b;9G z#D>5lp-QJLb+L#@H)P`aZU^jj)JYN_rFz>=NTuQM_e%#Qti=vq8b+qrZ6OQ>IVRA zCoFOaht{l}!B&Ngy>?cuQilvor!7dlmv)5B6)^9@z2?`Di1P)kg2r%;w(86xJ!GyJ z9e&xv-xQ?Xvilwrpo|`}?QcDSXx>0WL&69{a*oYaPBb_C>`QU@#%q{H`<4ta)Zb)$ zCk7}CM4z(C^h3VFI>*??E<1$>x1%zypJ{ZMDk}u-8bh3Qp*cQTBrg=X9o_@3-=WjR zL)RpeCCFxpqGm9qt9)<`Xsju2)OvY_ML#zC^ins|Bp74~C@crLONLs49}x&Q2;^5H z-!XGdG7Aji)WM3aRz3-)m3G7m`MrZb%k{a-L$QPo|F3T2KixWhY`lToH;EeYUF@j; zr*7ka7doY1R?apqa{s6CQ2Ae>mJAomH5XX+sMx!-4jYOTG$h!>q+!rwNxMm=Ounb$ z+1XpwZuC7e;w0jQdyN9)9@lzv$a7V4(`RR%(=Is+Id2CisegZ@)MJDO2F8$Js6|z_ z7Mh^R^e3?vxk_)Nhnc}+@BOtJh4C{O0)>d~@CvxBDzB-Wa*=V^Ap&x)^Xe9K%)-CzU0Xbd>x zXUG#}oZN>P*(A}I^Q$$r{i8c&rTZJQ=6D)SGv>Afs5+{&yPu`wqHsvI?zXsBZv&9| zw4bxfS?oO6_TebgZeD7VJYmvL)!Dmj>=1S21v-pcS>4b z$WhYi$5h5iG|zj zotiC*V~<0oh-4i&^(=;)D>-ns&Y8l5C|R}pQ(dC)lk#&Z(a7v3aZHZR;NfJNgH9vG zszu_=kSaL~2{LXbn4k82wJ1%O2x$ae)}L5nj{a_c-peo-;2be35rIW9HY^%*h3pHn zK<;luU8s@ZFwT(BU`Otjz-=E%dqELA|_c~xkWewE}g zCo{*uIUapX?ktfSYlL=l6`m|JVWl z_e}nG-a#YMPvx7kkk33MJF}wh3#6_iCQ2=u&8N~v3dW#pqz_t78DA`(%7-tK;^i{h zwL8CIHroCLT6!O-?rMKZxayZ1LO6>0fYB;toUZNNedyvmPv07fj+k?pGgv-l+CWHk4_9q(N)2euD4rrM@g((!qH&mE;=?8rA zXP%j4ygByQaH4kGaPWHXnLG0iD!KI^O!&Jgcnnha^57W5i&MLvftUCRV@9`riohzw z`lr36jpQXr^!29NyV=N3dS3t=h?5QdjlCYw6h6 z9M#v7ep$2ukB5R}+o<)%% zjGT(U(~{^e%O)4po94Dn+}>GdYJ$G@7b#4d}5_@cl4IG7k&V#7w{ z-N;%N^nK#Rz3Aw}W$`zZc~oASwG~M^3Y_U^Q!z~hTUkKehM8&I=Yq>x3)0d4RcIl= z>r;9Ja&<}4P91^p)KXB+vfxRvxIb>CXp;Qanv~v{zRt+rdg*V@k)or?4#yelB2Sm9 z^VtFwsprh)XAZT}t7+dhYxIEAadqb30=+d2oVfvuR+Xo^r%WIiAgP@2s!G@m*7J4) zcaRT_=5xDvYe8`*?rh?Lm`uW#p_u31KJ27TuZNhNFrH^_@?3}%OGKKiPmt0XX@T>` z-C=UX-ZlnWKmo&YS?eIGA-0FLLG3gv5AZ=tZ}i0ZbI_IES_9nc*LvN&hkKuHeDDPT zHzK-LyAZbXx1^u~!xZp<*=(K7Dx-DIUmUXmauB{GkH1A<7s9Y4Ns5iePeHWowBoa! zrQzPuobUx$Zd~4lM;PJXVS3na2<%CVlJjcfq;nwQ-o;)0B6qN#`L;Q3AiWKDW1mrH zp6JHzXCS^=hVj=OxOctT*HGCnYNJn?KlVwp9MU!2TysAKImSNaTD5>Stdm?p?DGKk zKiJyE^*o7g8WhRW+RfPX4})oVD#)~U+EZK3S^uu$J%)9A#6(U-Ry|d1+Oo38d6H|V z&cT`QppN$`$fIcP&1IC@Lg~Cq@4_)-EDucv?%* z+Rx3t6OSb=s5x%R?O_@*a?>n)E;E((ea1S_R` z!TbjOA02x~3z`e56;p?bwo3%Nc>5t?32^t2@n5q;wSDVyL%<(;kQ$NIb1aT*Y5ejA zS#1sBia-|={n{gW8F+{UaJT33o{tnR&uFkmXGJAOQ(WrQ7#K+Td5V4L z+d4mVs0xJ{QX!2ROQ#qp@3ko6P8SeImXkU{mPBVf(9WszZ$t>@T&E?YJj~^l;2mCg zlA3|VDW(x&X1TIv4{GJpO{auNJz!ko2h>V+b*ldS1(|;>iVurvcy^357y{;qcCMH| zO8lBkvQiKIidR#!5nEUvJEuDL<=U)4ZJJ9nUfpfjk#mtv(^BaaZE{<; zP>CxHBGT)Cm>Vom^jfNya6m_A$6A=$m~LItPED36aTBko35t(xi(@c_ z1fj5L2~oovM1w<4!HQi_f81hcz5mn$qN(q4SBYmBt$|s4DZ$`op>_UouclE**4=DF z`@`l)8~21aLYGtT!rdPVc*8ojJk@oDdvDl|B=_dh7=WI%aRS$KF#Ob)MQoQh^o`^> zoi^^{$C49zVxp@O{yTc+o*6?-W>80K!5hr9(B5E)LKb7NVBXQK1l<#ZL~eSl!@YMBNvQ!nk2cPBtzu7}KaJQEfXD0Za$GtXfaK*VeezvJA|nq*bl>VH`dWvs+GiJb z;i4vM%4|Tjz{sK^e1JfwX9o z1m|AaY?)Vd&IGR;`G#(=!677g{>9TdoqCniO^^zWYj)!HfIzqv-H`r6TQbW+X@0Nl z##b7%;<=5qPv+(0g036(Y{7%R65h)YV6NWc4Y3=&Li3+jog1(%ND29#(rAL2wMmfG zSR7svAeK=O|Hv|T@4+*#`X2bUDPPCORyLJ<7;X@!>ybZu#YfcqXTiV34kt*d}n2rPe)FlX21{77x_vh!M<0+B{mikQrW4q%88aHM5 z_C`^UdjyFMm7)&)RCA@|avX`YQ^8Wg>YwQd&Zc;s@#L=JalQkpoqeiWXOsmIBj2i| zpH+I`EzNCI@LfOZYWTzdye{3vN@q9%h?@Ps zLDb+&6i{JN$sbJq#tg$@?neb={-UIs&h?WU39^0r=8a7}Ht4y#jL+zIDD;HyKLo}b zEQE)kf(vBT%zt;xWbtqDb@cdtJwqE};3C8A%Y%7tF2#o#{%GVLc8$7dEj{oB`-W0~ z6N=qnDY)`3-O7$+J&en}0<#5=k#NBeM@dNm=Jtyj(?lSw?uDaU3?u*>9gonhV zS#%jP*$tj9IySeM-RSQI$D=EZuD-*`b7QlK>m7c>p&kJ{QUz{sx)WQ@I#UhoRX?~} zhL-aOPM|Dse+@a&PzGH;ccf_1FO5DR@bz8HFS^0UI&@HQksq{k3nWSB4$ZFIfV;1V zZfPHg%MHK+KjFW{BCChONI)`indYgj0y7VS(vhVFIojtEwS#NjavLU&Pja=7bEXP3 zQ%Tn&QrT!8?J$wk|Cru+h_aa+6?C(LD|vWq~xN=0|`K3G%-4{ z`Y3ekJ=ba_^x(XO`@{*{p=v}2jRYL^LXU7ZXc{XI^dXxh?B9tFEwD$*Ok83SXM~dS zQmeSY+xrsP1uks;Qndl@fofRw3CDj?6LcF{&l7bs^r0Ou_yf!~z}@q}u;g}9gtHuM z;a0eN%-`hlh(a(WF|n@L7u8&nvXm`(=`DbqNhF-%Pt*F zp)Sq>B$_`otiI)S%f#BD$bC?ALu0AEXw?H~p0fo-lDfrNC972ANy}JTAZF2t=&H+X z4MkPZx&kT5g?jrzOjkBMcA9Mzsu7m7oAU94(FcVBqqqfK3DIYA56WzEqN8#D;{M^M zb`#}9i>XzWqDTH~`mu)~*z+@(2V^Z^h=ReL)%a%yjiG-->;Z+(n?;?!jz=gOKT&m( zxwiGXG;MIE(m8FgY`M8ARHo9@X!8YCxfaOfsVJy3{+}wjiS_?%F;<|h)tt^`=&VfQ zD|=jWl~4ZIEqN&)lAvt=a$K_KJ0(zjLLQ1fIInCl8OnC573chy_vl zWrbHo!NEk}kVbz(g!xk?9>&ATn(t2r5BXdaEc*zR&9|#sS7PYE6J6U46)&&1>)6;` zDBCUBH$U1O@tkz?IBZXLrh~oRk97R)^7_m5<#N4t?Sq>8v7aviVo0%5Elkd1V1`)- z+nDdKcW8`RxoenO_Uu?b-re7QZf;O`UAr(~=GwLiZ*ZvP0M;X^Lh8DXv`=f2Xq z+Qi_N4PydNv$Ajm(Y|?V&QV^Mc~#Jzc{XL=zHwl@qne+kC*0^TX4KNq#dWE3x#8M2 zHDq7P;Xd2Tqj$+e(-C>)QvbK&(*BW;V+)k{&@p_kimY;MOy^M4+N!Wc6V27Ho@8>aSZoiLWfldoY8*uX<7% z)_M!oM>?WdIW^X|l#MadrHO;;pr+S03S?McAHOtaRa&-0jH@!=AbnY@Axe&I+ z?tEd&rXF=8{H7iyBha_%@ol3Xm-?NKRYKiMJ#_YZnaxW+>z&dGNSds%Psv$+*ZNr1 zQ?uCC)m*h3mA7yho$`-Jn2*612b$ork5CwkdcXN1zqZvA8qw&`FPHkSvbrtJKa8i& zXpDGqh!zhXTr^2892%!hK?GT@sbcO7?X6q9xbVT-XpZO_i6^r*(F87PX~Pn8Tb3Mz zB0ioEaet4&%cS|2hhWw82P4&5V^YgHlVU}J_7ALNwGeW9AKN{+;IkwV-DfXgIu8<0 znb)RDXEUWaGR5uC^(xeshe}bhaDJoVf4hRBtZD12>r417Y;|r=S+u^$p67;8^ja)z z;kDyQ#JX6m>bCRbY-bCmqMk9wGTfgv_%l#4DHe3=Lx|DmJ34%%NLo7P`SxPQobJ6Y zb#K4GEnfRGqFCMkyFi90)Mz9jW;gRaMX`mkZW;4&q0##+?D0h@Wge|U4y4NfF+sJ4!9dgi^$|4$jkOFl zcPv*(i^3JIlQboP%n21-nLVhUZQ;_R!?7(2IauG7J3{NymHqP=Ml-`X6A1 z)djYrAL4mS!0)xa9 zCkSm*XkA2rqubL9Xe*i-+f+K4<7*J#D*{9nHoc^*(Bn4_zK0=3&9ZUBovYr4GBsJS zd0a;DC?;Zb>2iSr7sQl;O#cbRL?``FnGMRcb4L-a7l*+{ujr<_gG>$>B^NMCC(m6s z8mmr01M51YW(B6l$)!+&z|CBsad`}OqEMJ(S5D^nV)e*1(Yd>mHCQVm7TzB|LqI$j zDij)FvZzeS+)SKCgBooKldKR?z0L8MwY&(kLBwrSxkZ@k{%#@>mFi22HwzpaIN`y|J2ram3XlEZRt3f69h`-6WFQ0Bx!vxSJp zIPM)2l?V3q!x@o|NnLujKCIp`^#J$`T00O6-_9v6>15UUy2<$*v}aivyLw`<5`?A+ zR?_tOD1MvXPA{o?Mn&{!cQ#^UatRp8k|q^C!49Z)yEl+9=Gt73^-|A zv!e&pf&Zp_b&GBvgUrmWIgd9}&pW5>AgWpGz)G8uY8I2=K=*fDz}m3hrLd1#!M*5M zxFI+qvDL86S%HB{gz6To6Kj7X6UI!1m!w4oPtB~*$tOvoCccFy(@AEI-_Imgn|k=|sYXvakiJ`}C36A|B|a4mN9_29)d znE~$Ow>qIbvZkK1Dj@^^3ON}K-6`=fRrn`Xf^l2OW(P!^t)Z0bwAbjIHmfD3sQ!_) zIM$bVcXqI!xBla#ddK6M>Rz`*=BRu7X5}SF7ij4AbSElErGd~Iyl4mP%aRy&`Hnq| zyRoNy;RS1PvbkYl=|wARCnLt*y(`rm4*F5waqWA?S~!<4X&7~u+a)8=B(MAiq7)Jp z6qgBwT9A1xEcxNbk*`ia>7a#d{?cTYl@c$kmj1^$SnX5E~vHbcc z>M}G--hS(PBWqi|8M+z*A{-0FYgRIXg!neGNZZM}K-fxa)S;?T%*tiSXy|&^f`~&< zsErNdDuAvo)nyuQ?;v+UaUfs|jSNAef!*sJ&3T0|KJ|+G;;L@g?bj^qJ*`dy=K`?s zt^lOsY!M=~#Y7Gt=%XGB(YKLReWMY$3Qq}b`*AJU?!)alQ*IVdLfeR%j6J^neaZtD z2}{6cHRJ69yJ+GuXEufP4Vi=E?GTQ`VX5T}fH#mo!*@o0AoJ#@fZHuOjsXm#VX}X+ zG&r`M*q0%hHZq(TZTka^l9&9-GxK)U?U8o~L{DHUt4Bx+;D_y+8+=YsuGI}hXZfxA zGt_`PPETK42D_-u4bO8IghQVL>oa)1lRR{&uu`<08Ax=?tsDF&RL|10v$3bmNpz_~0(lWoX-q>s@9g1~dw6yc zqu#QXUx@V~x@1$>jMd&!^aZqKyv(*>~p7z%%2q@@1znp84A~9GyV`mpZ%va=IZ( zw18z*fVug+Vt+T$$XyZBGytpCkRHdze1gpv0esKp#Zz-U51np6-j`n+#HN_n6~tlg zIV6eIQ$y1P=cvtMS1Z~exT`7}s4E+^A$E-#QUDR#Z-SF4>yWfSsvm98&ls&$#2W@> zzvPu-CHN`G~k zK^XpXEMwV-{`?TbdF6gL6gC)4sSBiqmL)~TGeB2$U(ktFbbTi zB(2puS{285@CpkL3o4}WckEEP#k!j9fVXdb5`)igL&5KXVx+qR^Ds9FP1+`Sh@Vyw z8%+g{dYOu$gFUjluA9C%q5Sq_i>7#wa`;+m;i`K3r-eTEH$(RQCvnyreQtz-Q3{a5 z5b}LjQLCRN8@Ni;wGZ>|2uqt!d*e~ky!;|cA9r{Jq*nbHGtT_Exv$Hs5zW{-b9t69 z2?|;v>M#d?aYR6ztt$%Vz#)P@@(I4ooyr^p)3cT}@gDSpmx5>iUG;H2wqS{@REGtlwINAg)MVhZdbVZ2tCiV6` z+@nCr64k=T(ikb9!cqCwpMQ0j^wUub7{q@$4RhM3&s~TmvxEI@`nPu+_OFv>;RO$Z z3LaF|!hk9dms~;!;D%vQtc2F*2}<7{E4?2)0CPAtnQY+CGO!;pUwm+}VLVmy{(T4gpU|iMZuIn5J*Ib|Q-r=~AMo!2_l6MC=eiZUdlm z<1duuFC-&`5D3L2`=_<4oV#)VgGyv~-C=$T)o~BiNek3e z;o-LTlhEj6p0Ju~DW$wcQd}wgS<}IXv{0?2HEqp=L7r(aSf!>^n?8vPN`tB91zlYh zMU_FH%%kzqW+H3PhI&=%cYl{^eB^c4L_BJDO90~jS?05QGaQcvi}?fdK__@Tbx}7i z=m9RjPC3<8Bac}UzpQK>6wtnqC~MCnziCt-WUC^vc9TIQ;a)4l;{m!o$gZ`^Bodce zRX1S^#?E`V5+_mW%^oq~hX<2zrfXK&+OpSD;t$*xCj{18o`=wzB8zRCy0k)5{uNH_ zSu1FpOUS}iyJ7DP0#oA-N(vU?1e(j=!3jCpT&VzR+@PfIWI_!$39ycaLT|I125GOg z#4K@swCw&t}Z%HK-qmC=$D!^WK!~T^zrj#mgi1%E3CQgg1 zN_nGm)=a(hc^0I4h%E`cP6jEv1tb*)QZ(lk&?5AaK~y(|8ZrlM0k9oP6n|9OR(kz} zaVzxr^=$*37a?-eUFeWt-36JKAxMQGg;;n_0Hle+Xu<+LAr8>otPpN|N8<0Fd%;@z z1oOiX#FYz~360%D1$!>n)j60l0DI`^-zkdTil!GbU{yX6yL}2>TH3`T5@n@IpN2 z7WaO^eOmQ@VQ%{%e5C+c6xIz>R$P#H#~?fwjs3MsK+OBT#*iKepKFU2fMsHNC~Y&h zP^vX5o%kZVaxrUkxOlBEgafRZH{$Mp3v5N-U=!VVoQsse1DjDO^)rP#p~6!I322*; z7y;p!X@jvD<;QTU)LeFBMpT*-o)Fo{F3%6@h}?cY7Iw^`(!u)RVpZ$=H{=wdDCy3_ zdFwlDcm)4AN%UEiJ*PzV8{<|kk?)NuYT*+!_Kecnr)>Zk)FIMJ8&Yu-7{VYrrbeok z%rvIDfN?Q#LS`|5bCmyFl&bjDs3jM(Hj}ZqeB#^?^MqO=wg=zf8PTM${HcB;_HgOA z2q3gDnGlnOhg|=6J)0$VHXtG!BH1=O#H(qnE5i)>x-c)eZ8oI!On4vO1(tV>B1fiZ zS0(X=%FW+e<;O2lB76`#AkDH32$YkcN9Sb z9^Z5K5^aO5PWB}8(J$r+v`LpFZr&Y`rshSVe-qzlaAi2!f%z4qjxaFt1FCQ$$Z}0Y zaR5P(lm}|+63MkL6zq@-ssx!YUK$%RWh$9uDK4NU-Z&}mK|HWFfSVl$HoG+d$A@I8 zk*Q?{?k4PkB=co|I(RoJqdPLN?Z2bcaY?7+$%dK%|Uv3_5;YsbabW zU|#@!=n@-iCI=kR3t{EnGg5{=F6_QHk`vmAsaQS5Ldf<3JM3O=utjpvg_(#V*vmxH zYo62xJia?Z(myjRJo{M6hnqnTBTCy}v5>~cIr z35#meTGJ5Q{F9({?G1xA+f*H;7)7`V7mY6NWgck{P8EKONHwCbJ63_;TU#qInB9twlbf z$aXG*2Fdr*ELHQHE_@2l3FE4bb0mWSd&kuH$y8_H5T1-%6C=`&lA{ZR30G*iZb11a zNYgWKq(!>gmZ)r0ahQ@2wDl@y)eF=U9=pDH6jzFNhAGN{Ryu1&>{_s<&&aq^BI?wu zee|L!wRHU|4_LH_BaCfQg~vt0Z_m zC)`AsD@(k+Sgd9@W`hm-`bHfmrPBVL)TkBA;jJ{)o|@uxsrGwb|Dh)FQ-^13;+y6m z)-Xl-KAX(#9o#l;n7d4YWZjSFk!DkjJcPVl5iK*? zj-?+z8S%mv>w&Jn=OvXp)Oh6;)l+m@!%EB@j*w?H{?TY%k`4J0H_v!>;GLCQ(hOnB zovT#acFY~OkY^YE(FCxVS425y%)D56n}{mcUgG7|hNrNlp^vWku;T9zx5&*Y<9UuzkWc7kWCbIMiqe=+LP4t~N~KbOWM+ z3gYk_c|c6W!acJR;NMgn{fvsuwgGC69YuQt>eOY6ojFpct#JI<1~3XLnS5m}2!dj4 zbD9aG?uMWLmd#5bW6MjS73rG__kHP0vWrXHQlzgOU(r*7{mSVUXy@!0tOvB;_xS+L z*F?JW;*pWRxV8<}#nUT0Sg8__b$y@5zs8ZGWSE-Y5;3k_sg&7CzF6pBH5jTO2D1<41ek(W zA(0QBjT8D#DycZ^l5+xiO-5XA?H#!sEsDr|3Y5JuQgck5&NP__xt?q7iW%-hs9z@q|a@ zCfZZppaHunCm4;iNxLHe_ zEB5apx<5y5%_yn);F02_*1z&XYx>PxgwgRvr)YAyo%^=eLG4}#C6^Dv)sT-pv z68(;l4k;+pHt3g6nS0*&#<>sqbtQfb7QrODEg@5h1a)$MWe~SE54m8~6p}t%Aoju| z_Ny%tQZgWDIvweacb#rM357K5JkV`QG%VRh8)}(-n2s&OEN3VDYBANL!rKGEO7tWD z{a2{f3Cg}9>j($I8|vzdm`V|0LsY+=#h2%%oSL@FvwyQb7f7QsvMh2L7ZsVt3mUQ| z#_rsB6jw{IgG{VvLC7Cml&eK&y{Y0C6o z(*YRn4%|fMsZS$1ye*_Wm_qG$QQ$BcmIv?`q>QHpl+79SjO8cx57B31{KB5~4L|=4 zJuo!U%3Uj1f0se7tJ(4gBFYwPfSsZju#E+n#)25GJ~QeJ0{F%PKK%jPVpxB_5CPMy zMw?_tvI_q7sR!rL5g_Z3TvlqPMWqVJ8wSW5JF^v^2uI@I#7l@ZO{~uxR6eL=-Y;Bfp+qP}n+O^BJZQHiF z%eHOXw(YLdCp!B65x1jXX2g2WoGWJL7~fDEp|RsO5w13r%gA6=do)gGyhtk}`$bT`=#b?{=0c>xd7MrpCxGfj0X=hPqS@es7L|PTrzD$Q8qrZSSOm<+ zcf3VToLKBwnP6lt$)o|}1Eg_iQR{O=QuubDNbAh3`NJORYbz_OQ^H{U&h4h2{u{>{ zjf9gd0cUw807xISf&+r019GeJZrny5w)wx-G6 zfIQ03bWAw5)_q<+xbig}j>!YTG81j_sCxQP_nCt&FFh;9q<-!@l~>}{T+a~elpfZ~ zi8JZ5BP8K>$a1KK7F?t&Iy;Y7o11-K0l_8#9pU7#%Erz0tIaqemK;Hw$u}G-Tt9F> z)wnBrc7www#&er+iH{I(lz2=nb8txfyxzT^Ep6exEX7WrH!iTlq!`?S<8nt4Yfwh> z9~5v5_W{&gwb~tK z&E4G7?}FM)Z@A%qRk2##_v~!NUFVIw;k?=%dxP{I&Xx zx`EK_JMv6p#XrCnan)0|6?5gA|M>GiGI9Qc3&Cm9yFvVwPDK1lC;m4z_%A=p+{xV7 zkwnzdP~Xn@|5bx$tNu}5P(b-MSfCB6(gPeqU2KM+rJ!phiV&SEfLAq{E!{wnPGh+u zp$zJ@TQE~UzbQt;El(DV&HQSA!y|L~=4X|%nMb@5nq$p2MBgwis zy(#X=c9-qGcd$9aSVMG7i;^OZxp*(SuO(jx9Hr4;BqyA8qmgEk=u1AIcLjWeCt{Lq z6vd+c3#m;Fwq&$S)N~dZGTMUF{6}cYnZhp}$Vqy>UuQy|8a}DpP)9LVDDcuFN*6<6LhE2CxKnbC3Y(#4iw21VkbA^D8nFgek!ar0v!VEMRjYA@GBt` zZz$a`T~!T5I^VfTSnQ&z$_$a0g#V_62VJNtOl5yc!Xt}qiljIw{W5B8%3y$5co1Vw zpCnAP0##0*+$7<}Ve|+Oa1_=e6ZjBt#6QF}ZN*lw)5!_-oVIse16I`PH=frbbV_#l zZnUP*wKswHrxk)4^N(A6iu-~P%Y0rK9GZ{eD=XIqdC9XG1E_*qFUwlOm;zxp$h@o2-3=gHagM*Q-=5S{+ltLROr=#_&3*?}|B$F&$ z%_PAd5ne2!acD zc1F)ea+Tb~-4u--?8?U{fv_qpY%Q=UtY}5r6tlfUPKpKhju~?qEVKV&N#zi|;-oN! zYB}`5Hg%Ue%%A@e``MiRAJ8s=-@eMFWGkZh5326hW%0j3yMCQy|94-}8vL>^>FgYA z?Tj7%(`TYe6AqgM2%Rr0r%g4&a1wr3R3i>r9pLzNLDv5sHHgirGizd5B-ts3Qst;GaL71;?UPSfevy3~R?%BkX{ljhCjhMf8@PVmg4aWtBT3QLHSm127}* zpS=Aqg9_%G>`%bM9u5NBKoC3S5Vj))L28Hx$e zp*vq~+@t0vZc&cN2AGp{LdYpX#b9?-a1_CQf2tO}FRzZ2V?>g-WOx)Uy>E41p84QF zsk=jVcRHMOeHTF!B^tZD2pmcDA}~#LqCmbmrV1X}WvjyOs)&M3E6$x3*jcNBZxlqq zn$e;m@SYzEDMXiwZ*$?mJkznv*J2)tdFM4R&htw~Ro7*s3YPWb#EZu!S;;&Cp)}-X zxxs>=IkB_E-fdBNEx&7OP1PaDa0hiG6%>`CYJ^v;6<)@^hyk~XH=&;@iKHQhzGlC- z^hbe5t$wR%r0Dl_528b}Vdr#B)i&P*Gg;#^zMZMoYR^)8ffvTvlLm-vZX-~xpC=}< zo6IE2_bSQs{Fmr4kgh8J@F}#_gm;)Pn{ndzymsN9XS$bGs*2Mum`rmsb3XSdOMYio z^OCfM=!@@lT8o^4cEsrpp5I6w-`?A+;VDL$-*s*d+9_Bf4FhSK!D~x3Nh(H8Bs%p=p}eH;(eCZ$DNjE~E7r)FyE9K{wqt+{;z);Q#RS{HNL+xDL}qf3I(A z%m1HhGjX=E`XANit^Fr(y|vzFrRFXG)+Y_zP&gr7CLz&U?l>9@SgwQ7IC377LKJDN2WXgO0Ww?2qxl4z2$g!ObpmJd%j3|HmacESf%aRA{X@Z(ShAefYpPbXhz8P36e`6@KJiWtG z3BRM@8P$7UVVkIEnTk~)MTm>(qDPT(F4~kD8!?`?E!F&%Cop#sy z71la|7o5><7ZoJWRqz&hHQZ$#0MR9!!+>FQ2?(WS;J->(i^zqIoQiKxP7_sM+}cMi==GQWwx1DoC<}APlg>Eu@Pj21v9dS9|SB6 z2d(*M4lc?LORG+QVj^rKplI9A_%IWTe+K2RCKy$sc&|MA)8~8GfN$VI$mr4M#GfKq zc9n|5#ETT=rQha7jHD`IH)ozL8~9!}0c=x~^bU#N`xkf_b5__za79y?w~qNOj4`2v zm{q-`n_Nn?^rSf$Cub^>peh(ER`tczOCM%;7Wx8LH99>-XN#O7XKKRNdIci$9HCoo zM_9ygYaAibvK2dmp{&aqf^!zq|AIV07g^$|%qqrH?s5Qn@%+3#)}FV^b6^2aQT&|_ zj`HrNrBXDC;1ZK|Ii_mi$k9Y=j6+Xva8#2+U2cG>OF_uLTJOm3;<@EIFjS426^K;+ z=*2Iw$=^3UM=R-_`;zwLYN!uWI5>iEpEl6zGtfIvX{SMMS!|-%M2e_f+nlafdcP_8 z#U;pAfpH{dOAJAb8k^YzfT@9)U#74Cvj+)ab=?oE zL}`}f9FPDOoO6oNqE5bQxN?SXQfL1;)49^HMY&FfMYo2Xg31_;Z0G49>ax}VCVko* zm0o4ee_kAY4`nNqi89lb_ip;_TRXUqtB)7%m~5}&P5G;^_Uw+8Bk?6J$upgrWmw#B zkma!pRB3ITtCePi)u|N&yWS-CEf?2SKY4Ml0g^^zk2WxlMt;Ht#<>(#?+{tfDo+vB zud_hs4=go|Y?b$lW( zC=RH%biWyjIT6kfjjrtXzU%RjUs9jaI$D-B_qNB+sy6@H1`Z_oc6hXINRT@c90rms zh(g}3`lG9o2$XfJMnb1-{iW|Fl-LgO&j-ZCAejFM)n1tdcO#!l&&+P4?kETYqO+g~ zx!RRn`G<#xldJ+eNE6ExUBo%Jb7138+yDfPg?L;$Cl1BfMVP@ylh%FdQJpG8_hs>g zg8YKr6$6Z3nO`IW3;oY&-$(U*{`(|fd+y&{>ce3~5isyWbosZ&OtE5}t=QGDRJTrq zL^(0Ap}T0kaU3h2YQ<&^TNsQJYdtz7zS$o&V(Iupnpk#;y&}4FFWPFyl4uLr;DUg5 z8bdMLBB!ZAf&xY=UfeV+b{N+uYgbmK$M4w;0^ufIn&rta_`lBQK8D4|GB~b+Y&CP$ zXhD_QRIP*oSC<5V_V1?iatFl95O*Q)bD?AOlNrE(cQ_rpPe6yWe-)LAKQg`RfVP)* zJhZ4^*x~Mu`WEFu4(@JB1_YTC37mZ=G==!;A1uG=l3sV%cEdk5F2NM&X;i3QrbQIa zw%x}9$GRSrK+vYS=|y)jyBGAAuC*KfW^a@po46^stKGZrRNwrF_wBhl)kpcza%X2w z(USq0J5gdW;#ci<9RERK7e37jjq}Yfc$0=jRHe<6nb?_nLzCGWs4S9u_Z($HsCPlN&I^U zO6nl|WIYk|6+j0Aoh>eg1?``XC_KZH3NH-&9B(#3gQZ+eB2vFwA6u)SFA;?v6-rFE z=mzwlJDN|q4bNE3uYD~yRhDX`aG5~doz?ZqSEwz!2@AGw?T!Cnb)nDcm53$Rri{yc|v%M)3+&C}Nkt|z)w zHJ<{(^qQ9&1asK9ff`Us1eI7Im|-ajp^j$4>3q9FR-;h;Hkk(HO(F`G$Z(!(*Py6K zVMXMvwjgY#o74U>S&Kq}1}I4+{3DOL$qC=dUg<@!FtP1}K;TdU$5H_ah}H%F{TbkG zy*G@T<=oJVp-;ILo@eiEGdgy<@v%((-_iWnH;pW{E`rJEEULX$JeBy0)Zi{%6G_zUd(e+Pq>uI-R;+ z?bcYG3%yYOjBsx7GRDEMKF1N=Os@H^>?>^xOJzErzSCZznVFqY6lrJOIuiYmJ1OBT$8ez|VJC-4_(Og;Mvp-K z`R-Av9k7M-?4wmUD&~qms?_KWT&w2OG1`9ePt38j?vM1m_1R^V^kHWT6!(@2hy!|S z^il92&$Fq(fc3dv%2JmeM!&WV{7j8N1=Z-on}0fW(`VlY3m|AfC2VXk?h~_%uhk0; zrC6|?F8O}#9e21D1Hm7kb(NfckbPfkZt!1TY&m0FCrh{+v!Ddc_`8_Ca9-kJcepSH z2O0w7mdY>gJ1|qt_=i-ELaiZACfDoZ;`nSYjSlP~iocf>s`2$OiBRIOdD`$S-jxAE zfB2_WW(SCw4LZ*neIFQ=zZrt@m|kq+U_q&!?llclBY{R?nfp<x#*S2te3-QY`XA9-xtBk8{f7s(uUR`BJdR1|(kA;C#c!-s|{}C3U(j zbz`=uX_rAg$~W8 zZ`J<%*9uk1b*70qiS&!C@WR}pcSB&k-A?~q#TGk%7EY~7dWY@$pQCPbd^^$ZKlsQm zvM%%?KW{Y=j5NyOGsm=|(soT!wNsNH@cSLPZhZANlI^iVoKx_5Sw>Cg% zTwUjRD#W&6as|$kL9823Cl$KVGY=o>pn8lkV9JQT-jV=0PKOx_8b>f7GkUOZRtyv0 zT2XM}z>CYng><@iKGN8sU&kzzxq+k;MUR{|r?0m7v9(&Nc+c{1Y@EYOrnf6Y0;Gd0 zOrxrD^0UKQ{?Pz%^`J?;uHLFLe%P}>>>uG7LsKF#!G2_l^C70j7LCFcl}zQx3VMeS z8@$TGJKA6x$3#6~DP1ymHFeIDa91*I%<&)JF?PIu*!N6Fct1o%i#v&b?CH=AH=reI z1m!;NzVK!9sLqE;cf!)uVm`vj>W1mRj5r2wX)RG+%|30^G34S_`Rt2=f@?f6Qr~iZ zu@`zI(s%2(ld+>>SUOu#;m_RL0H$}uGE{RBP!ygY(!&MddV$At=WD5lc&+y>-%+Ao zOWKQtk;k~kn8w7zC}GppVa8yTxXY(Gp{nU&&|>J+Ps?=o{ylz(w3lF{)kz2DRflSw zhOWGf8BF3jrrO5UZOBgMypBv9@TOOI2OLA%Bv&s$q!XnBZZr;2P+NN9?~Gt3++G7= zeJ3m4>i1%auhTYS(>hbjA4)ybxoXHv6O6-EPRdI=Vc_}HV?*s5MY=%sT@ZR3f^>{D zcw!@AA~(86YeA0nhc=&);UmSrNhSs>-hYpY7m_JETX~-7q|nzvS+e;k7RdKxKWyVa z+pzMk23@JEj)@bi>&g$aO5SOruEgeU2__myIUXs4jbi|RpoJC?I{E8vI7wYP?bskh z*C34O1rTyY1qd!OmGl&ur2X+x|Ad0flq>5eqkxnT=gL6%w-6(l_8TkJ1!WM4NWXfb z8ZwAcW{nkQ3{FqdN>ynTqDkYHBD2Dt-W5>Pq;u?-BH##M`|4B-8_5jC8pd4`>cG|z(iti=MFw(u|MJLj%A2BCFJ zR4NTDVF2{6AIDVJafR(*qT1Vc=Pb$aiac>7U7$R$D=WyA*w;TmH3p5%zF$&p>8)mr zSbM6~6)^8tXa-+6Ma+k{a!gW?5|BIJ7Xqm1n82m=gnR3|E#}3hdaFECgrupGM{$2l zJH*u&1UYsl-#!hN&fkXEtUn*LCpAJzk50ZNIwRxwB>|hlP_-O2L6bpeg*An{U;!16 zIy@|1-HqTUtjg?43OQ&b3VwNEpjuAFWFu|C(TS0X*Hos!)Q>^qqcSl2;xbYg!936q zOPeXuV>eFsoja9a&@+k*zt7_wX?hFZhP&35*m7YjRzvIn3agYY0ZhOl#1!XWUAf+&2H4D~bw|kpq z$n1Ruzjty3x<_Qh0l*q-RjdPP>#Ea_5cOfh__!_nn_JhCw_>9_UUa@_ts}h(bZ&}# zA5Tn>oo>tAQbz%QK9TM(qJJB%3Oh=SY<5PIB8)khNzkW|r*<+}7b$|-mpR1&>tU#^ zO1k^)wO^q>RzQ!cO>m<=sgr~)Ts!p4eiiW~ z&8p8}L`Qu*<2ap^7bc{tIb%&--&Ur ztp+hXQ1bT$xGA-t0i5Y^u$uC*x8#OvE_=2an|pj!Aa?;)!dQl+ww@h^Kw#T1uj*>X zL||}nbuF_z&ZqYb=xDsJ_j5#$+yHVn$EAJ>7*N{#@*}X$F*&9@C)b=fq?d0{BQWgq^3dp#a!Ilj|4^H2T&>Fpvov?w#ji7-jlOA*Po3Q!mxZtu@ggLb<2XxV$ zZKBi9yZ)`*JAc=-Lphy6n@(FYwJ`-V+!Y_+Om|3@#%m`}6zn0G5cqmVV zL#p=~3n{S4d?7lRs;a&1s^z+z3bU09Rvr1X0U&>d zzd#kAj&)xlRzYHrUVDUJhnXd1kQ{THSvDsirHr`Wz^e+2f#T8~IwFkNk4s=xPFy~ET*Mma(p z?9u!!Tz*HYVFO>yqgZ%?er8;cwN;C@BHhw7w3$Iq71Q~kbswe{Yba^MRx?ie-|4wm z7fS|Ir;uIbBW;ztCBv30H6lMtbgIJAYcGyFK(S=u9@1(*&kok=-q&~iqr^V{P3A}$ zj6(<vC0XPH=IufNExr%X_g^ZMf^QOxiYrlU|EA-e}xZ6pv!)X`%UC3@&=xSH}{Iu?!JX z^w$pxqs3?D@Dp;n6U|}-tmKnxC0&Ofk7$&NOlcTp$XE#*6Um?m#*TkLqOEcJwdSJ+qm~M?Iz$82n3puWXwnf8W2V!1xKkY9B(bGrrWELa0=?U;9 z1?mBrw|?TdR)@D983y{)pfG18CRdT9U4RU9`Qk8?v6;GU)1r$9Y=4PGWoil4_eLw6 zMqqx02<_d=C*efUAgP4abGuB=R5{SnF&VsF3pJ(RVZO+q;Y<9hE9gvWs(HP4e&e_go3cLjU~ZncTB8_!c5 zmZibCNE{X0FzyBzP9-d*XU|MCHwAQG!~2+`8ZG9G<`}S?5v+_fe>vz^FhSQ`QZ1$5 zbR&tH%&_P8??Agh)5kT2E z-qY5;a&jxPwPv#`8Z4QM%!OnHSd%R-S z3kMgWrr^?$9T0d0ra10DO5v`kNe+vB6arRJ6Oal_c5Nym6D=zYY^sJNg(eVaF*#yS z62pKs0#3uJLJkuqzW(H1aYPW>94TjJp;~xChoIu^7(7X5DzV-{?hhw&GQtq);V^5g z-AHS+zR6Ec&D9-ehpv!frue|)Lq_27tGxLThuP3VP?YDh%0M7C%N27N!*JVAV*FN( zEn#0y$&^IGil=VIfQ&K?S7t?MurDbjDwD&cyb;9LQ+`%t)P);a5>=3j_(0IpCl<%- z4Rzu4(k&i@42Qmfb2kIJG&*Xs&BX$;j{{Y=q@budFP8G68d5R^gvd0l(!D7{mn!w!m(9!kV%{kZIDn=0E}) zC}H9yA@ID&CJ`WE=@4!O+@YBs_4A%C!L?Qa5Ji+Y!1MXU%w#T|F|lOEJt231C$psu z=70lbhT{j<28Z&Ie342*LUGavI2r1}145#^@hwAl1nA4{Ql8dp$R6l7o*@a#dJy@K<5n{$8j3A)=~i(Jn^ zw+DjRVzA@~hTj~N$6odzL--5EK*W~_-JbDd$HuhgC1zGJmN8E7fb1VjWfJiKf5SjSi8a!Zh!vMVmJPWjYvoAPuF6>6^n1#Ap81aPzA~ z7ZZ{4>ym$zGH48l+JV~w+F}e$sAWWaog+RF6PvK}SoAdam>W$q@}enobZ=nDWEeeM zi`r8zR5uT(8Q?k0!4we!SN~2F9a+iY^~QyPS39RbJpZ;rV$5HPN5FVb%kbp)>>Xb*8qG(6p;tVX_nt`IEyY40D)Ug6lR71ZjLU=I_02Y+X z6aO)Q;o=VzTJ59nUZjS3SAz$(*)J3$crk^+?3{-eyTqL`|ui4J5%4tbp-|B>Y9G9n^ILr0;>^ zyTTo%)Ty6!r_+PD$!{re(rqb0RPQJw2?5RmXhA%+7x=~T3#g?$Qhp~P%4~%!F5<>4 zEHz^)oPs#l{}kQn-Gx32K}NcY(2nddW0WvyTTY&QoGzRfZRcRT9ObqO?CJ=Rcyt(s zT@95;;BE6iARASzaP)AQGK)>6^!C3Mjf&_o5|qae^t1mCGh4iu zl8P+n*!DWBxb%$;=<}!;=Q;FI;D%EaHDh$59y0nIZ{qKIcjF{_v8;^7F$3eFox%sxb21A2r>ZIZb>0{>u`n@*HFs9xS+sDpEDR} z$_X(CutJNau)Krg9$l3B^FBq!SPVsk>Y$__QGF=%KSPLRF_&vE0}x>+QliW>nhiwy z>ygc;eve@m8tSR=e^r1Vpasj*w9G&tBm>|NCJM*IO|PTk7IPY5i^~BLMJb5Ui)t`` zi(K+C#4VsFS$HHy3W7^pm4#T8q(QODMF+4NMoQ7jMYm*6fa}Yy4w{>K1I1Zn0^-@J z$IQzy8lj6g4ZndTp^Q;)-3REu8xjQM1Os~*g6s%KH|D5s1!F;8rwjsX!NDS7g}C4e z*}`%T&3V+%j)Z`BoBm`+LGU?cwc9hS6riYjiKfhgJV_2_p5Bot)Clz_&)O1?27kyV zrLBgSvFjUH1UR};z;VwS&vU4P2)`i8!xS`VV0538d&%H1LMW$7WxQ~IMJ7QGV^g*6xnb_EDBUP47Zb2#MGGV%?FdJsy5N@msy zpL}zE=n=MN2pla7?e@hXNDg=ek%}g!^r>;&{1KFg;mfohuH`BP&9bRYkB!T}4d(RN zlM{TO+B%yCQ{uXtDzBI3D5nR`}OQ`j7z)T*PNF}oHu0& zSSN5jdQ<^0X|AJ`m6KXX#qhtY7UFV+3p9&J5k3$&IPDG)V>m*@8byUkqy*glRVL>< z;8;^8pMf6@0+V=o`}~FJ1T~Ivm_F|Ap~KM#1?3z*4zzPeBim6}NIFhCP8#1ZpFsH+^NK6fRuTFq>IhZ?*Ne~ami0{1$h(?+Eq)%{97D0Q`^(C>j7%1S0 ziB1-zEWnPA4JB(qipV(oz6oR@9?Xd_22RoLMfjcUI^y7K&(rbqp5)*QarhcpG~rgK zyxuWWxQ7!6>W&x&d_0$%NJN-6Ql@+0(;3Rt_~%ri{Cu>=ZiWu+!a?d-5Qe9CIe5=6 zVu_reyQ6$S{0E>wW>V;aFd{VKZmD5H#se(dSdiKfqWxM;Nj`}J^WO=RA1n#>Kknp( z82StRQjYKT`{F4bnA$)>I`;P}Kuca6E{prr9&ArQeuoq{r*bFyLw6x_iNgY_BgH1; zdqsV2e@lZ1Oh~wfQ@M?&+^@VYNlg`?!^Y@cQ5Z~U$Yc&BSxq&%DIwX!vK(D=5TV)E zBL}sIoE>8MLKp#8OtI#xcVqLFpZ*f;LdLhiH+lV|$1_{Bq*tQ9s0(B?>|gCGODBQ@ z3s)jA1TRF1;KXRlBR+$4B4PNF*Uh4<#C7C)Nj01trd;Ba`bSJV#AqBBLXAipV&q+p z?^(~H%V6MGOlh`iIi7d598}xocDnEnL56=n<6A5&<>>S!d8FV&laZ#%k#=JxBIOvv zr|*DjlkMr$5aY{?`~=V!W@}aMKFBH$S%ZWpj`%MuFr?cTx4gzh8e-RWz#Tsnz$4KH_W|&mYcIQ-`Wm=Xz#|rA_wUUa$ z#q^yr9@5e;oS<+Am2=}J!VeYm82pZ-zAjH5&wS-Y{iY%vNd;O#dY;a2^P^?cZ={K% z5Wi%rKU=&9qXrCa*_f)<>`?--jXbXnfr?XJ<+x#V+I!$yAmDqjiuA^IxX50KE^|^mT2oM^`uvuqcyH;sTi3A8pLWgD=rWWGxP%!&#EwFw^$n zj@1Fe%c~)qS^_Y7zCvJT$QdUp(Z`%zMbC}^Ft7LFN$pSiHN8!w6dJ*&9=1SEjMC$4 zB%jCN<^6YYT_~A5w};sfG0VnVE0_Vk5Z&fB4GD5XyPwcclu6PuN4mN0A<#jHfr%_l z#B7>Ewm&R4z9L|Tj5EP%iySZ?zY8@z#DUm}yckX{%)tFknav@LfMNYQt;)O8;^{?R zOkLw^^J;!EW($CYuiBocNoql+^&es=%CeOKPrk4Wt^+)9qdQVSKHfQ?#PP087t_lWVE7aS3 z5_Scfg~%!Fg6z8K&4edAAHH2QBX zeUH6T8l7Q&qdQ`V-3)~IGU=BA+ZB{rg_G;hZtbZ}cSoq{?)7QKF;Du7xv&FAQeEff#Gs8ZkxSd}MH0hgPi}rc-=QJ>sovV=- z>tBtEQ(N-bsPDet(_}E?x$r=0*FXOQW&IyP{e7vvJuxf*fGy7dj!~cY{bWzNFt-UnkRXI5jI#X+5m1>0K?oBhk`xFO7*wGjkY(aX$27G;dB3!@zA7=O z&_1)MqSdyFR1*GG+}|u!yEJ@!>%4fodrN<8Q-67%Y-5@vNalSJpX5Ab-*}yT`0O}z zkD30w*OdZRD$-MoU}Yuj88T(T6@tr;UzS7hM4K9MAoq_9v;ybG?dkS!eykwyVGi@~ zV26au2Q&J~Puw%>W4u<*@}c*i9DQUdbR!I--;o0AqK&H?x#iWg8EPn>IUCgev$?4gxqq^?Mnjpz#lk#_VwZqXhi-Xl<=$W^55yAzS@4q z!uZ1N(W9;g{Ul%f$eaAo$9<}X@P|LE80Q4!qrfT{hujC>&c)qhQ}rTKnU%%m!w4s1 zQ^{PY1n0|O)tHPiY$!Mi{bMRRrV8q)XQZ}%DS=ysGI3=xH|0WBW3x0x{S-C~59-g# z30%HXyGTQpZ+zA~gA2a@ezlu-7-r&e6urCla80jG7^wP^Bg?_LrpR-O(ZHlWUQLgQ zMnUFd*^eS?#@V(eHA!;3UR2p5D)s6~ZsKJ;DrjKOWlm{qa^|KDElq@K0!=l#)c;VZ zXc47c`Xd#^$u7fpV9~~zSO-Tvxzkc35Q_T&zs?wH8hCLq*4m)hhi<+pyUWFz<>;|5 z*Jw3Ha}CwxndjLtIl)KzR>#8Xk8ih0w6Arniwh0L9AV?EhvUv6{y zKH$%tRW7Rq{w&G4B5pdA=s|Q+GD+$tvvKO6{>GSa+mT)Fu@wzz8Od|wyeL@_hI@l+ z6z3UZ>5dByUj93`6hwx>+*0}KYFV~Z3TiH0J7bM4Lxm{_E9R z=vfIutw9h8{t2k24Q3>{R$sRwb(MlLPI%}-n;DCPWMS<-PhEVAuQB8>wzR`LsVZb> zHn`wThh#l#GG-xo~5Dw zRP*|7158VqJ`?u9Q3s`h21XS5RU41cg>;qm>DKYz>*i*1;m9wWz4qi2?3*{(vpI1b zxp||pl#dCrs{Ay0#DN?_m}iYz5W~peVr*E+?UNVFnCPdBmATih+m(FMs-a=cxI(=~ zEe$)}!ZAia`q!UpUC*#Qg0!88*w~f9S97?q-@=3m7M$CCLcW=cH8FTuWiPq(rwu}f z^h9Qm;deIy(=v)MB8bec&ro!n`A>5x&B9-E)KUt=Y`^&5^5B+5eE7y4Q84JjBD^xK znlnlj9n;d#Ygll-J_mpkYv1V8jb4G2bRzaJqIvL%lybOz34qiY#h+8-MH)Ro7&b72exMcjViX%<9 zvz3KHO9_GY&ML)4+t#$n6*>*8oheNbV#0sp${;yu3hs+4xzrJ5J0We^>|t)GjS`yeR6QaL zpO&^HL*>CQ=I5c^jD;=Y+_dzl`*CQ6{^k>8x|$|80`Iv9)PY ziPp%d;AM6}p(a-TTQ+Yk5;tVBiaB>hgN^Q6^tXMTAlYG}S*vc%o}KGW>LD7t!4(Ga zM&hJ#sxVn@V+fJ75C)zsiXcc%Ivwz@x8AH#bH#k6S{$LDS?i-vPtWZ zozj;MI~TuFeFLLIjH(7sdXQj`_Xb8L|KAUnJ{Eq#u!FLV4hk)@T6M-#L`rH11Fv{w^j92hl--s!v^oJY&i|aze+*nCm}+J zRhH+)IxTFDCrp@uwbY)esHPiBY*n*L-OnBaoS>r<^!jQtU_RAsw$tsjJ5fDZB~GC} zl)DLdq%Wjx$H}XC6$ar+j}!Wmyln8x4Cv}|9|tBT5n6_)VvV~0r0oZUl*ARA=6S?6 zFZkb}(Cg42i&d4&VU7@~H%##pu;LeU_bbQL=>kLC0gZ}CB|AtlETqKrTK)z&?L8JB zk?*0BLLGvpOhS<_CAJStJDu+k3znoNkB-B~EShPut1~a=C;af0h07!oE4K%4{`Ci| z6omIfK$FY`3r4q74asclF4nWQ@A94JVl$tT3x5!u=R2d{k?=9KPqilCv_1NltUF|z zcSiCVdsYY;BPeUtPa(cwEPzDmGZ8;*y}dUvO6$2d;{J^iVC?3U5Y!9=sc#gwgoG32 zj0e?$2^Ez!c-fs|KWYB*u?+SPSjY>bg-sQFSdr7e z?W#}H*IEJC4h42@9QB~gfpR_1{UA+uO>jNOP-9CA8%O+!!y=}D>3quAP_WSg@Qr6& zDD~&skIUt`@07!02q%}#6qSm@#el7P*vm?kw_1?78FAcZ)6eqk?Ux}>uQT4Iu3yUL zi4AKpjQ7c`Bg26<>rdaO?L-+pDQq7^u2YiF70_|#0K}TPS<_4He&c2EMJ1MsU-Qhf zr$nO!!*=oFk)v_d#=Lr%*;_D1x2n23lIL2J)jb3J4GI5xlJO5Mw(y!3Cj1~WR?13C zWRRYZ%)VK~q^nf1hSw1Zc0W}*X5YWQ=^M?UrIA+w+YK!qt=;6Tfq{xTmQVpx2fGqh z1)RLr3TI;FJyu`U^A-c^@23O7=<0-L&HCTgcnRIgC-sjBJfYTg-KKlQMYRw7EP*24 zRiPc(JbN73$F0p2v&h&2hoFWrwD(GP_|TLCmg>ZE>;WkFs4tDtJCox+TBjqQ5-%7< zvNL0uS!bEKXqg!~p!&$omf2*+ev=38{z>UMXICcBYg4eUWX2DhO@H_U1iV7-9DN$! zfR0=f66jgs$yV*44+w4CMbcfL&X5L_lv5I@dnZs~A(M;K$(4kenT?x%;37%8Ux{zb z$?b2COJk+&)r?@9Nut0ra%_+8LP$HxMzq?+oeT!sazvl@N>-k8MyvU#zf2EcaaC5; zq^D_(_e{j-yYHA+;>MA1hdZmPGrUUnB;bd5s&Zc_jxbLiJ(bo9=F@vXeQJB5U~}xz zH!!a*Ue`S3_M4>7R2ZSzxGOL*5)A%wHxez*o|RkI!QT&~TtrD_H9{u3v@A@c0>yrZ z?g{hg`?RfLy==R0Ez} zTaPKuC1!-f)`BYBi4v4o!tY!;)lvVs)K@Bz;VW=?$b~Yy=hR&u?8o;^W=EiEa{kp8ZEa} zG|T3FfTXBsSM;D`%Ikml(chwYfO)BQeF1-uOOws6%!G??5H_OH(eBQu{{=0|A~747e6Ms4hR6C@atLr zePIFEI+)U1INCbf(f>E5PwqFaiiGjMk1OZIKAQyrgpeQmO^nizqS+K6P$daMXDXRk z2oNMx3B-Kfq~mT$wK1{b>Q1>GApgNc0z^XlT|hUA!Ak=qkmKF8JGbKWmU@%8=r6`D%pTMOil<;B=%&`V9 zp}gufsj@T$Zb{QMg>DrS&m9#Wh7-Mg^f3lG*4%@pN%FJNfiD2-OVBcX)?Uz8K%gK% zuG=>2Be$U8dg_tDa-8F!E92@E#!xElP3Qup<`9T1K0i;yJNdlAXXj!7n!@h2o_&bG@{{-aV?`RTCD@CoHL=+}$|G#SD zH@4B(LBh$}O3mES+{xDAza^5a^50Ce>#e-XlmJ5l8I>eLPeHQo-HAB(ydYBOw5T{g zGNtCsD(MviPs>l*Z)oJcLHKU~-=w=urWV3Dy^Ne3Pmk#bo|mkR@At0*azE0_^VxVw z0)I~9>x(pTwe(GddoCyqg-CELG*%Q=9wUrei>BHs237Mvxs?TmZP5n*7hT`LoC(ma z859X0V@yH3c z?4}!QEuh+7e1o5y%XE#I{wS8PcV+eGaA%jHXr)dieFV6Q?V2A!f$=k zg9%b=jq5{+R!)yGbG%`wJ$MxM@%z+(q#uMd2HfBij5wqZY&4k!Sz@_s{Cyt+=x+pa|9BkK zd``MS(s_YHDr|JmsEA~Llm)!3wveLxDWPwiSC-6G=zU)F+`L@^#!1BY*(v(YNbPpK z!t8cod$jj0`gcJ{UR{oU0jFEAb&Qy2X85NJnba|BMh`q?oyUgKE}JuY?asl!jtoK> z!%r0yIc^;xiWU4@JY@y{cLu|p3{NRFDiF{wIv^nI|IhpV@8_xs?V-Gq$|s)mW%Fhj z6AL{+3jv2I85am-iEHc@)Xx^eY9iW7&?@jR7|m=#3}y|Zv6W04M?$Eai0%vwM~E$p z<*H>23O*!EEvQ+*I&P)bS-%rnAwu))cmd>japiZCC|3{6Udf&*? z%ak&)Kc#jss$Jz7EoMsO1h;h0)eu5)XA*1;HiJsUsH#>rreHnW2L9?2{B=)8D6A01g*BaCs27F> zF_Usa%b@?K$&^hztV6Fef+1y(S?HKp(>mc_va>bn=0o@9(1WQ4Auh)FiYP#vwuuZt zo4R7!_=cQGm)>HQSxdQ>x=Act-EXU>HBoJ%L09xG>85LSi|twWG;i8HE#2hN1IMSi zp)iEg^N6H>d?f4BQKLhY)gxr4K9J*++OLk|6Pw<8Yxahm#Bc@Sj=4Fe(ptq}Ih;=6 zO^h=T=>R}C2V@s3()4m5OOe)qCAYDowiS)FYLpMNRFR?IRPAI%JUuOoHzZZ0KSeiG z7jxsDrC{JvS+8NQ<5%5Chmkzd?TH}oM6ZN(6&0H3Z-u`$Zh z)6Pnbe zF-~N(f_*ah8C^f?2)T{m%P2c0PFA)M_ou1Vl{pZ6s^PMkERDdex%UvP9JCXroQGtJ zxmGYL49ZcypCEc;13r$`=54r2>siWI?(~UEuTP3r3S)q4p;#UvDTspJU3Do^YxMid zLbov!sFGu74I|bzy2GzV(NDj=toKUZ(@<%q%>%P?q$#REil5z$wq?2IXC(X^ZzS7XCgNm!NoyJ2G6Q|8; zb_1!lhq@|7k_M%VSl2XP+-=`jx)|Dy^Qgd33lSh{_{GSWGn{fXTx4E>&`-RGe~wqld{|g& zU(KF$(a;u-qHCD_eicm1B~;J{bHKz^jF|n&kxs}r@8PYo)7W=bUNmek%dncB#?BaJ zWv*G;;HEiiWbxS1h)8SJkl-$4|6dRJ!~J^~g^{SW2n(EQ%al10CtMJ92W!@zqqL|c zwqh0I={_r95N^t0^;Z-z2l|=rsv>fzycHru|8{!|FYbjj2aUurCH-LGT}!Z39Au?% zqYnUo-ENp{aB@n;dmu^!Pcl?zW&e{%6z&Nk>xw$}ouI%L)*EbKMoJXk&JO>Xo)3 zh$;*xTf6IAegKea^M)OJLMn-q^N3L6kV&iGILy2X{&~lt-ifIZC*1M^TfvP z_Q2l0>$Aj48{0C*O?_G@Ss;3Ja1MjG%(^T)PWSL3eb_77nS!FI)O?za=T3=U=kjj- zn6~9_$qmbJNy&5J*AsjG!(CShduC3d_>4_q_e|VUZJD?^JxS=RNQh)_(|?%)Glv`p zL%uGsD498je93;2eSkBZ@^$&Y`4PZl9e;EWVd}d-^}m7CG4l%2Tcm>V~Avg?YJP4Qd5?$sS$!C9sB_}Y5c%}TpkK*$dLvGa$>7 zH_ZD1r$L6U+X4i>OxOC;U~*V6x)d|r;>N(%IC`2kq8_#GyjV8(rWKR`LM&-E7ttd3 zq*?I6K&60eNO_aDaTfuGIME?gH!jLnFRx_k?!=&k*hSlc*zP!`l$e$wx7~pXFEe{$ ztSAZ;H+7{5>MgS5eGz?cA?K#B$TI4jlFBL#0GuR|+ic zPLw)ic+oVXio$yOvslz!1^eG>?O))btn8&qG{; z+A0**_PHs(*1a4fc!SAMH|U5Ge;;!T(YKZrFh(2LC?yLFrT&zvRYSrQdFi7gx~^e_ ztDZP%%RNoJ)p1)+%hc zbu`zl7uk_6IJ>oq?pY}5+ipQizgasYfS=6&#<6A8`KQ8Pl_5e=>!58W`cO-`E2OOH zQd(o?b#K$+b6M(*X{-EK^tCu~q*ta#{#{Cr%9DOEyM1X?cb?%A?a1yNI4^qwXYHyup(d8ff>V(HKE$p z@j!ThlvBDZ1HMO4kjyGn_su~N^Aq(+=~*Mq$~ltgjIdR8Xi{;@ z24AYzgAxLI!*V9n(#|KG2O=wW*se=p7m90CjttJTDqs9*iQKyUyU=Jo$8a7QQZ`bI zq=}mm#kY8AvE=1I_jv{d&9uTet2kE85L+dR=H-C1vAZ#U+B*b>M_h@TH>tFWQKbdm zEo!$1(kg18*c;Em`1ZvP9LxqU;{msHOZdggfu>&EE!Aw*-X-j`6y}}&NP&Ex%r-_T z+^$j>gVzkf7<29KYW4!=M=qg8sH&kNQvKj;+ZX|60n?XC%iSBTi97Hb@9j=FbAl?z zD76bm{U*N6KJFjrxm~tj1T>Dkb*g{w;uVN^?nz}=NfRI|IKtrp%ZTUA?E41XRW;%7)caCr7l?5ae(D*F4RUa!%4 zPLfLVS{Y?;y^2Tzo>e_IRtRSmFL}al81_*$W*tM0p>w<35x@vsL&&v9wq;j#|rU{``CL=8FV){BlxB)zu8wRL$qHE zA9<6Xs{#FW&6?$^0_7VGqapi#7Q`~ASmpKw;(E9vS=WO0NQ3#!&zuqCoPq12^iE9Y zlsVf3>P}9YpNof=A8<{utK~mgpng2deP|Lm%oUyfDrkyeWNYX4JUC`_47Xus)>rP5 zfLBz`DtO1ardqoo1u@ErpJA5FUEi8dKe<~Z^((z#JVRxmlqE0R+@ad7U3Gyu1G;Sx z;jDqv zChpK0*Pp8LNS@#zp5(M9-9z(@7(9jy@4*NJgKr;IK+N|J2oIki9q>BO~jxl?6GEqf)&_z+w9iSPCw47`N{chngLV&Ox_ zL6X8x-e7ETUJZlEx%cx6I<7m!N)7w)O4d8228nbIX$Ca)k%ETQ3FF^zUa)*#C%wo0 zT8q*O7l`bD+QqO3A1cDKEg)vfJ-Y*rABeU7MpwqB&zg3p(CW!4+Lc{+f^YTHoC(?$ zir;;c{#KIeLEhg1j(^2Y(EuTT^)F0tP zRL}(<=7SAwCA#{dGYgHozxsl{H23Ns(|p zYNpIs%DOWHwCBoP6Y{&y>$Fw`b^)>q#pqI@4_iN8iN2U>B-z^AjQMoH-_nkweI>P_OgJ1O?bhHzZ*omO%?9(Bv_!UpaFSZide^^G27L|iD1#oh&^ zB_=_UlV6nB!@}&bpvadY)SGF!LCV3+1T{ZeF%Pb)LaDP9aE*JEL{AWP;_dV7DAX!F zb$mcO<$$rOn+Z?!C`Zt!2ZGk8Roo~YH6(Ts=p@iuI=CLfgAaZM_du=1DY*g@8iX5Eq5akiRL zcj2?gUfhrB_T%1amLhLF5<}KHUi(C?bA6ud5OQdBu@~-ipLOV!CVA~TJh&mc@+Xj; z@P^8HVJ_;Flm-Nyqxy;Q^NSv(yS%sck*WB<_(Hx2#(H0CI>Js%nzT0zV@FUCa&fPd<>_Py8vF z3(t0;MSZW1n+vMZPkwz#_)d;#r*_$|Quxv!kMS4aUu>>hj>4gGeITvf3NiE6t$uX6 z2yY(n7o&bdtvrNF4R+9iC&5D?IU{hyGj866_+tKwu9_>)B|xH=U9#w~jxXQ^Z7vi& ze-Qhuo_dcRP!%DoGop6(bS{ZNj(7+Dl0chTcq}CE(|UO=7L7@ESt|p&C%S%27bZ@m zkXEcy;+jeIq{6z2VA4`{ANvuuJfBci7h}OMq4!XJ>cx2c2h~}=N@lfGb~^>s4OuTg z&8sFFj^Dkzvlr#h_f?(D97fvn&)jiK^XsO{+6c+@y|Vw3+?v0tJon3DLzn%- zx;0|Se2mi+nlFP^mlZ>tZz-(pk@+QrrjnUE5UPfHE3IQ;YX{qcf&KwHGb*orO`=I_ zHRfRYU88wfPg(Z!Cs%{?s|5wgP_}ORx-M)@{UU({A=VV7s`F@s)*EZfuZz~+u12b< zIkya7X{sc^S`A`@i-I?~p zbk5q;E;C6{QFCiN8ldXQIU75@ib*R}W!Luglo zU2xPKK^k4kPu9{B%R<}WTHm0`;q8@tN~lV3ZxHm zX_AZpmr^chA(b@&7upD!orTP`3EP71YPy)Dg=n#=ey*&IR|EH%tr~_@tuhTBUS5V= zdS52^9PS&A-(hB6qs^AhiswJv?0VUL;63r4?l_U#{sMzvIN>XV`}=dJ+MXAAe1WC>P}+ z*y${uk+pn(TvR`dAdT5Yvm?Cbgf8uB8u;zliwg4_ui28_2d*qeq^x{oLrl>q_BYG|1({c?OUcunMlJzzaQA9KmDdh^#xm zxBq%LNmhh?jwQ$S2TLUl0mAsmK3$llM@%vAK$?j4UEn_gvM2C&Bcc3i42}|uv?)7_ z!!G9a!-S=^(hL#JTt}E*ojr&u1B)_q0hc{v;0Rl}@@URsM|M3(ixaXvdHZES&!Tr8 zX5*CYSoh`mFzCp7aQc+VDrEf)0p8nfg$Ad($-y zhbI!Ot+ktumO8b`QOTBAo$uy70x8;yzf-zMzVn<`O_YmsK67$dgJmp%XwDs*^b**1 z;e~tjm&>#9TQ#F}{}L-UV}aoj4?@D={EGIFpDjGlD2QJx!-(o@+TzAdW8YR7(`dNt zY~~|0J^dNyJFWBBh+i|qCOqhZ@~vON_0%0cgjxhBu2QSJX?cv}>e7X95_DXVmYc;q4=i(zFcc9pL+-c4gu1_g1jC|JDaREkTAi zhUo~w-xz2N+TUu!_YK~VwAbzFYGNVZL2hF76R>uN^IN_)t^UBu)t}<~&7P?N!TS&I zuzceE7=Y?^53$Hd^2xmivYWEi5=G}gziO@DF-;UGj&K3r_; z#U#>Ux+xfm>sr!X!MbS<9-}({RbtL%(mwBC_3M6?Zt3(&mGaZ;xyVep*3WSXs$KQ~ zXf_$vM6c-CQ=Td@&_-didt5&`PL9#WQ`Ax!hsDa|XJJp)dnzPu9*jX+(L}w#O4*J& z$*!cN%^7e|^$47cTeXCl@?*A0u%g*q=XZqjkJV)M;STco3YV+;Zhu>^bYeavRF_u^ z3yT&##hw{emM(L74@;h1U;FVbye(2mG7D4m*65eON-t2M!bQu=-ZX9pyp`8Cd?wwY z(P%jQTPjuYkP>;i{9F6_R?@70}Q5f)}pvl=`MjK`Gl6Tndoz3#%KET<=Ov zst@MAGVV^j^Ev~JrOR4_zA&LCADvQq6r5k3usZv-qdXn5`tbfRX_zkI1zNEv3Vo@v zs9dQ>fSW{Q@kt z22>F<$QqdbA)!_K=Q&nED2$%<;U=qC(%VtobJbq6b~{+F$);C5LtHZXLc)c5{26q9 z!Y${XCPD6vaH!DiibBKNSBIw2&(qc!7G**@r!;&r%yeutBb`$gdT6lL;GUakNz<&^ zKshdX;#xP|-Fd$y*<+t$L$X4QPn+G=OI)#~MZNXUNNtDIb|6URANPoLyrJZaa@>qI zmT0;U>8IbBaIz^+JjBuyyvBbVw_u;aErN684IAX4(8MOHn|a*%q{HNN?0unTyM%s8 zn{`Cb9$s*t^+dlgeP-j5UiCn*^3G6YakmIp;0(9>Yntrt)->7O#Gd+#JDQcJK2t05 zTF%O+po>>$AkFyU#TbP+1yS$_LmZc4He7OAiSZTNjOs-4c%0N3ZZspkGY2Dy%bsnz zJ|rU9Xzcn~LM}y;{EdQUyxXu3VLmS~q)(HrAO)V%6jaJUp#RQk=^0#AO+HDxTH2+v zXy_dDRE#G867O0UN zumHtDGRH(`Iunno(98L1=q`-v&g2L+BX{N`lYM@^81S)20M-MysQ5m%7rEJ-cc@H{ z-Jbs3-*fa&Fe<*WH|2Kd8I{$DRQsPXfa94MGHI%=uXm8$(QCH>Bx}v7C zpZkHhPJp|1LbMp7Jvu>En0KH9!}hO9&+;EiB-8$S`a|xmDa{KYQ{cPR`~@}O85lIFQ@_|Q3?_j^ggoI%KeL6)~fMN*9YWh zJZ3+J-v@|b{LW5GxNw&-*XH`V$NReDWc%?YKfe!XbBG4-#IXsKYIEK}dxYh>VoDeO zW%0N-43Z1qNNlhsOq{FZn1x5}hC-4@)+kR9=kN6#3q^c7CHV~6n@T(IcM#`M*@ox%jC%g#(s?K*6c zjdMH=*AS21UJ^5@^d^QqPqdV3V0E_?B?j}s2--#m-QjYabu>9?(jWMe+ceW9J@oFR zA&|k27J+4u4x*Bh?4c1#j=0-}%dkbb`l#d8=27hrQhVFIoYAlFMiC;QV0jMhcxh5Cs zj?kb27P<(@)E6+CwuPld#x7oH!LiLNXSGE}W*;k*lHMMRI3PH#?w}c!ST;JjWfn&Z z7bY)>JW-6RAqC=#b%xD>w~0k)SC1i;SH-~&$oYkt?--<$%;yFK5IvJk%{F)z1>s9E zv-aJU&n_v1u}mfdC6-a6VRM!;21(-zXg|{W1>##K-r^qeEd7m8X`=PCL8kB;VU-f; zI>Pf{bNucqNtNW1$nd9ZokhzyLrSV6j4?c_D|Z^?sy+HQjm}sU1a~ALfst(& z8~4u;CW?&B0T^S0rPA-C7TN@LqZUAVbEL2YL&tPUuBD_oHq>N0q938M1GB9#zL%#2 zm#-zar`5;&naewaOWET~F0U3{qYhxo!{<4t_~x^B$DQ}2>F0Bw59zAN{S&ni!}^rz z9Rg9=Psy?~D`|=4fFjn+lXmeW=6YO=zs z>Y>E4^KTX2w%q z$(#kObiL|Ue^>et{J{Cps3C8ZON$v+mTkoQ$9h#W1G3dyaH_qctRUUZXmYlEnzq6V z`68fDSLTAbypd#}^+~Co-+t|&sBHCF>bf#9sJ!tqJTMBdq+|vmCo^Ebwzk#{k)}=a zR*M|K{aY7ER9m$VAv@dHoTgg&J_`Z`b%}9ZSH%@QPvMtHUOAJ;FRwwD0r+!+vdM}v z7@WiFdb5E&iWXRK*;n%g;g9lTmM;meR5oc4K`IBCE|qngq-@j1Ue6_&Y0DfbhqXg& ztdK7VDvp%-rZBdGW5-p7mk<=5OE9SAaoK46m}Kk1_I6xKh`zNB7o#SmvaDqSrPC=B zd2&^}O zJAMyRfg(2SY!grS7>NCyL+ZAh;ze=QnCKJPN`3}kIoaNChqJ|;w6Q%@Em3T&f&PW2 zV+*2Ws_1`s`=YO}C+!}&@Pz4N$@0v}vueb1dJUL8^5uuDTn-WaU6zj_7lSLyNLUy( z{(WJ6i!_B9r!FC2sHpmA2+>aw_l?=|+9S1;V*`#Zb^4rSm=ijl)}! zxkhK;+=X6UC)6kvN|~n91I#@vT#Hf)2YM-76%7Rc5VfCY6q@)sFJZ^+E_UA^)^<43 zHba-3-H&J;|A(_<|Jqw+IGFp6i7K*y;#8Mt$AQN&zUm+nt*S%(J0460&KO74xP~Is zsxs{Sb_J+ZaMlVW@Rv!JB{yjn9XM(^SYif~ASp$}RRRmw-4UMrC%r1&26eX)Gpzwg zS^#|TWP13EaF)OI?%?ao4rzX)NK_ZDMKNeaKg4+nJ+zAkNXwAA#g=Oo0z_H`LckJS zw@}NGM?CkfmQA-V+jfeJ-3PV@9v1~s8ihsPZ4xQ2lQ~5**6SyIu$Oe0mSp@Dtc!d(izvo@N0z5HJo1Yl**bvt6wX7W!c`O6 zzQ)%dZM%c;Os{y$T4{OQB`BTL@w$~ZT(E@!C@cz9X%ovcY5*wXY>2pGj~yWbees`| z1F3UG;&MbaCq{H_t3EtPxMUJ6j?1py;C+`cKZA*$XJOf*_ zMMCfr8}Sl@NaFt3Aj&Q*0Hj7PV?H}CVQbyQ_M;5~-W+tM4Rwl40+&$CDv*9Z$+BFW zLf0ypljQJ`ew;gMFTm#+We5M z(fhEr?(1CcIqM3ww#ht4kEdOQ@rq#d-y@r)8}_4VJR|>u@zQ#NM(K-C^+a)h6QloD z#{OMnWSCNeCma&x#<1$9Uw?()dZm}<7SDa@XNBg!y5*fsEJ}du`fAWRSttLaKC}%M z-Da?|vHOmI*&8k6huHDpD?a45J~?80JhMJ2Zgb3^F!_Ny*%x&}7sembN)Wi`%IHbp zJ|5qHLQye_hzlp(d)0*XybdRR7GlByft41eb(n>BNX)+ML9ywysJ>WtR&m)K!)LZb zr$)BI_0JE)xhaq5(88uudzm_9?*#qAB3G|MGDroN?m!`8Y(WjF>lVPaUJ#4yg2a1S z0`>(4xLo>LrWrcWbAzB{T6gRdcYN^>%1gK>vOR_J?CkgwvriD$>^&Cj`zjw4TrR#r z0OA)4{=~}jbT53z4{8d!_QxQ4)%e8?5ea6}5$fd_-x`U2F2=k?s#OtWFVd_K-|id@ zahV>=9D-AVxvTsv&A<@cQJzEvNn8XVF5(tx2UwA8;{B7;pt2wS7Pv*pa3HASbA4^} z$P5*z$Gll!3rr!cw}l7M6|%h1lJbWy?F*dW0r_@EDBZ9r71Ta!&qYH4j17E3AnJ~& zj)bNpw6!&MOHG+Oe4YS(@Z$bJhG*4Z9oOgjm{V6pM35OxgAPX=ZhooWif6V5|;0 zRMUB@-FNj~wlmDzbnlFa?Gb+p-(?>rML@B8v6ScGLv8M8os=v(Jq;&YtA5APrCcvqm$gG!kH zc86o<9UhbcPB2(Qy$3= zKJ>5;u6K8$b64~#EznJ>w>(6X^;%7?w=`s(<&jJAT^7j>qC&WR7~f~TPxtMsjdJrk z+?DkzF3?S}ZT{!ykc7{`g8*>;W2EvuH6_mT!8GGH*_00!a9_Up5tIF6vCpV?%b9-% z{`);C{0HZke&o;F@XF_%tbh+q@4^spUr7L}5`;zFqe18+#E?qnU@T&U1pjn$mi{rs zDOBbNffxPp`!3L0I{$|w3YcH_1KCWhA1A8-Mf6vbPpDfyNAH^VMAtPQZ`mr z4!CVXlcycR%dr!4eD+(G&YwP5){Y2&4Cf}1Wa&5eO!9Q=#;7iFho&Ilnne><0`=lC z)hwObF%s5}Fbb;<&FV4NEFP)k3s%o&@(auz<91H9#mQnrjGbJ--Dyv@R?np}SQ zkWDtPXiS;aGmBh)g;G7%a;hiR`yV zGKSSXv8>)?yUxIVCkr71iamhee_-lJ<_3+&n~eNWl}BxCYH3qv5QW=gL+)C4qUYlC z{vD`?=Gpol;&U9QW36;7o5hpa-G1-5-0!U^pj)-|0e~hcr;}$FSt~0>47*X(@K>TS4PzR4ks>jMP5fX_qqd{T-|^0Qmj3xl7+C=db#7sQK#sI|(O6 z+8im-dPA$_u(hq>dbS2CWCG)01t_qrq`fo=>Lk4uXb?-O5=KR|5i2uUei=6Ax@uq` z4H#_3uz2aR;z$oGN0wm?c9{FAxWC0`9YH)~rQ*={^dPyb@Z=&^>ssd|lA`#peSo=N zad>EQVs9JRk7ev)j>{FnRq*vwip)U+WaKRk4&9zcwp`GgW*spu!J{rBX{5=KCHt0^ z>}vui>$JM#k$<%}G%Pzo(GS~H(3-_DaiK6hXAk}uur@62R~4wSb9v3D--sHdp$qP& zDb~OX=lG(==DxnxN=lGrO6;C|{>(tRwWVPp6D;ipDTwaW{~x%(i;5hPJPcU*tkZPkp3xGQGBeeO=3lVFBK= zu(E7j8C7Hh(T=1R`n<;oVo}IoX>o9q-^WbWJTR)vmD3k90!nY}@4z%dXAwlB_uAUP z`e$^4?|%8?nWAlDbh+DJb13vBU$H{H9JrP$4WOB_lWlIy zd?81&x7M~IIgK+3>Vnt`3&CM)VC8bhC#_)MJRI%`ck(B;!`sR3`C}A3m-3{E#@|Rc z4`D{jcSu^IKDyn0zr~p?TLFlQ4@5xJ2ImY}fKC$?Tv6z*Wkrdj=&ZTPy+uJld)xl= zD3!i#V-|w$@0*xm8TOTwm^PFaVO&MPBJR3Ejy{ZhnRdTLi^rv=;8gPwvJ(==!TiS* zd}zgUXaEXXDu=0^2pyWDR**S$VrZ_&P8k*@cH{D_aI0*Ev~|4+G^gp=f}VL)ND*-* z7`Pgc#omH3bupXW5MB{9C?Te0ycZ@X!wWrK z3m9xAs-Zkf`h}}=Eu%!vog1HW-|1m%Kwj31g#}t*tYTh$ zlJQhzQANA)^n!XU57#7B$ZRLJXg^B#*3q^RgF|U+fo^Mp$dazwF9Y8N6u6zs>hd|S z6>3&O)*;X^>t}k>O3LYTP z@y?xDzeB8VRSv@@joq?2p93*sa6X38`)t0R!S;3Qrn}FYbNNPzfrt3T1>2A~A+)a49dggCVO!4V%GmP({H|nD4YZY6Ti5~nyoFDXi3!qQh2hqJw@4KJS zzql8COVIxo!zezoe)rq{*M%TfW86=a<^MG)R5x zVU!0x+#_y&D(ciuoD50m!lnDqpYcyt1R4d<=chBA-p|~N>VAu|oD%D!z8*<;`e>#Q zQk<7s6ilce>VMs<>V69KMo82nU@_zfam{Pg)O`()#L3!HZg(Q}SafTsk}fG=l>M&a zht~eQXVvYcAI}WyJG+NJp2#i|!6-MD`PR+(9ld76McWO@VCF^TuzXoR5SX-Q6hgm~ zWY4_l?!qK0Gdm2^ppM^&_-O(01#Z4qq^QfLXG& zqB9v$?revzK}%<1eYxKw823LjV!z0faqg+3?xjQyRtF?ZpqJxBA4IY|R09Y5z1i#f zk@6oKsTQ@Mt!qvq6I4#fV~#2s_0aRD=D!{JyS#A{IIx5rVG#e3nuI8gmMu!;KTkZp zw@8-!Y%MXAm28lDjihmq$a89%;u0_ZB5 z!>jV*;i-!-$S-UYK4isCzHUVR`yWJ7uc#B0sTVKL0#xi^ZB3hG2J)u0jx4v24ng#W zEVbd{NFRfhFg+9nropf=`+BYl5oC-~YZ;1f&v;kHuqS>>^Uko$1~|aR@`ypPs!5Vu z0YJD3F6;e%}_xDFm(OG2ja(BML7&nR#d}^b?zz)sd`#s7k1qbz^#ZU!)CL! zlXgJTTXrsl*j5CWlLtMhih?!NJGYN18Jxq6yJZneuG$)?80Ki!^sAu4j$|#%KnpCh zXd;97GZbEx^Pxe6*`7Xew`$P==x{`4PwvPZm{PGTa|C66Y^d=_w=W*1>_|k4Xk~E* zWJdkPA)#;%g7c3H#O!XpuhG*`&3=~*Cdu>gh}INCXmda{^ZBG26)!xMZwZt`%Wqp2 zmbFK3`;)=z*C)Qf+cE1bFW^_~eZM*;iuU&}=VyaUajTJVG)G+E@pZ(k zkZogbFG^LS5trd=C9Dm|qEXid}H)}WhQ8P;pP+z(Dg z{mKK`VvgXoQFKpV?wEvEQrjlC7!s7Sks$J7(1gbJmLOArDurRvH}(B0GGoY!s$ts! zPW$2ag5-!IC~hT&zN#tl!hU@f2mZ`6vVe&ZxwV8;3z z&ZT+_G|4T+)QNTpNkS^ou_Rll{it`(h`li)9W?n@*HvuLkM%B=CZ= zk{0-(Qk;4Cwbt^IO@Bw2gO8x`0rG__k)560=W+#W(`Js&GWMqQR%Rj-T3ytw zv7GA75>z-Yk0HXLuLD->P*yu%C9w)&mKIX@iy}70A69BCQLR7wROjRm;Lo%Y8g8Rj z%Q(1VHc*R@ltaX^Q%I0@(#A2!>Xe;?JgZk<vk zYE?q&E9}(KsZhCFJZp+ju(4*{5oKuu->QaZm@gVh(+Ra@P2nyij0 zQ?b{@91g}Ds>Yr84^arihuXGi=A@V0I#Z2N433p8V{D-osa*sAu9pMwhg!YWLRD=F zhQ5%xm%ITjLuL4?iWwCn(x05*BG`|3c^O&XtW4R@y0jBNV*<3jTl!VL{nK)hRP%B4Q$hNfz{TB;jUi+-N9VM-9DqV^H^JUvWjDwe^zDi?7+nHak@_9Tvpgnu{0z<(rj9c<6Vvr@p~H&>+ChK(s&S zv0t5Mg+Bie1mwdP{0>b$hdP)<1dmTkBBXaDdDwFjl;?8-+!clVmfrn!A+C$>>HA9p z`hYwA3}pLQmp1V7TpTgHEt4lzzEfoa#QZO8K~a{lFhwf(a%dIWcS@lprlQ}c;rjdI z(V+tks~5xeeY3nJH-&tKjrUPKi|%c{B0wz2K<}0{AZUPebAdwh2w)f={yOD~rLT4_ z;B^T&1cL<1M#L)0N4MS0dtOu%kOvV{xi{lSKTpUf|CpFrUlWJ6UhCbVoPA|MT=7g5t<%R|Q3G>w2dPlVpP)W4G&_xW*#3 zxige?wGTKXU6+`pZZ%YoSjF5HF}yr#@G}Fq94EIPtUM5dWLx1ME`FEg^IRm&@#B9+ z?#FSp9dz#k##g0>mL z#2Ap0(u9QL1IX)s`C67@O0Y_GPB`w{cuAoOCGn-D)dEC~BB;zC!v@ex&Krh!t45Gt zl#uGyMKh)vXpJyEJ9>?&HUKrM??}`e!ped!`G8x)^()PBwdgBrG%oNoE>~*p2F%I? zOnNa*0c)VeL9V)hv@RhY>|YnCLq^WE@QO16q^c<@Cb8(ow5!f{Qy;!?T{Byn3hY4V zcL+SbY%2Ld!wURp#&jAecT*%aNWNTTSIWVL;hD#)JOt+6QCJ)SCr-lM-iWd@f5|}P zXAgmrtPk!9)Zxm^jeofg6N<;&{EaGf5L+8*3qW&pSEG!lM6E%_5OK1f5i)6_Auc68 zY+M^HE1Gh3&ZKl-<00{WU|X3&GYV>X!%g_Rt{gSx1nGH0&x~+Wkhyd#o61d@&$|Zq zY%+hZ+ti5nbXH$SP~c8(^G@qGAR>KPrAwqRWZ9aE%tS_T26378NSm;+$4O%&f>vkn-+l8nKCRy5 zu&~+MDzR7YqPU)KJ*_!Gnw9h$vk|L0YU8uiE%7Dq$}+AH(duz-Q_h@0I9>5dTMmP3AZsAU^b?Y1uqS_|l1$<5z3x%rAxWR_|D zAYWZU$-4)^X3-u)xzUBPDD9mq%4itudF2e> z1bsEfnfj-U-8}8GD@+~_>(x6Etorr^Z-GB&f*TcTl&oH%L=PiJZ%NlCyJ?=c(Q^vznZN~k9DjDx2W(_Ik=0&LX1%cv=bh)E~y2c`Wtr1SV3_;=*3qq^ZTGWvE z9F3B!mL)y+7cRwHG+?Q^v|t>mM2NzaechDo&@1*Nu)XXAl&Bd|Tgm5!RBmb3cq6Qx zX zMm&GvUisw8l~-1`;BHcFI-zMzLAGJUU{>peUV0A;n)j736R2r}ITvXdo7L!cT?JqL zz0ZhBV^z-80??#hS>InYCM>Sokj`xQ`j^Q|V4_ubWKIH|zgZuO$qY9%Y(;fbiYCZi zhJd@J*xx5*pWliT+wblD5zxx(6Fa((x!b?vZpirKKiwU+Iq<=f$OjO`uW~9sY(r>y zV39S!#yvA)l2xXxj(=0Fc0&cu<{kY@FN$kqJXPH_OyO(=RLR`pz}w>Je=cM|ElWP)Y-RBWpy zTpmXh4BI(CRwR^F_E{5(4O>W$`rg6YAlM#SN86zd2fE&%O$nb1x%Jrpj~z#c-S=qK zq8G4X7pVMpFB8sRS%g$#XfpUT4t;6a?5-^_ zKU}I%+n`lj?=yNk3kv2r2aZy#rU&@YD*-OuJ2wP|5i-LfnL)|)z2r8;UlT+JG6;*$ zUQ&U#V_^aV0*EniKu`h$1PBZmASl5;1q22d5Ey<6K*V_$1Rx2PO{CX$(Y|CGppiDo zzEgp(0=Y6_~#Xd&Lis$PHx3MJ=7zA3A^Pcm+h7 zU_JwOB=e=3DVQBzcM-@U_A%-lL*+*S9%?rV=a7d%K8?CR0D67IkB~eVHOgp1P#=yO z;&FtmkxtuyVwZ^yBsWOOAbOqc2BU!1F`>*4{uQB$2ausDno4$YHi||L;I+SKtvemT z&y7BwnzU)ze-cxWqo}-tvhGEDfFKKj7y4D`ToZHP! zCpmS3Z1)Zt(&Y*4>7G(Db!V?p;}my=^hHk^zxskkjz7+z!~@y0{fpYbb}Yb6I+Mi0 ztqC_T5ejR=Zo2@}wy`Z+)zXHIN>iiua>H)ntmhp$*Ko`l4NLluf0Tqx1giOdO2v+yI*PzK`gWyU z9>z4gW;m+a>du+##H`rsC{ybU9ni4JwBBds^dq0<*k^N3c09idn-&DQnE-ce1CH4P zKbRj){sAxplrj!G^+p7+?_vJf+J=+AAt5MKUo?b^oM(uDp`4^egM&%Y91gI?47jw? zJ-cw2W1S1-pDVc(!oDDaw(3<#Pg&Nv1-lEto8ZRuM_&i1r15`a%ODhMOaWbwfrsbn zJJ~dNw5>zmG|&(5a@r~%pP?~xlTBY6snW)t8ne!K04{Veu9hlIM&!+~6+sTikH8ZH z`v<{PCY~Vp1!wrh2o*7Eh$lZMbWf6XsO%6cFWjG7kGrT)G-$SMK|*vCQDPhzVQNuc zZ!vFS+1h1Qo*k=Wgbv@K^674B4)|vX_op3?>RVqcBuM%iD5BA!j=Iu;K+l18R_{b> z6V_z%nsjxV-;sU=yrJ9!)m!f}U*onh@?Tn~lA|&ks(l-gQwTS={e;+&?UiRKxSOA8 z{i783Pi>Q)wCm`k1>4X|d~o+|-?-lyqIyK7?ggh7n;`zP#%w!SBlq5pEJ0;w_$)mIVtz*0~>G5#T9ZFZb))cEC<_tEj3!%lC{6{85FYPyR1NOsfI`vk{e=K6r0 zq4}$_TPS-2b5}t2{%kUHmn!F=TVs5BSa)l+J->sv8lb8Vv@@rGxTI5F&yJXHciWB= z^y)fOy45m(FJaqDuDPTz(Z3WuaA8}vaBQY!RL&KeuE8+rBM zwtvht<`}r<;253@a4oKcGwrwHTKqXNaZGsp-RkP`ZS(J`n}nnF3vsP@!du2XAg&n> z*)77b)ADc)KhbRK?-@1{Mu;uMG2;qwZ9n0zF%B79xpzE(Y$NVTHk6(h z`VsiGz$c?i=Tfod3#%D&1i1r3u(B+8qMjKJUo6PW@7UH~fj1~e@aN@NbA-8L^t*x< z-^RH)1~I~(9fJEh_GIAH`lX6t8>9L2vu%^}vW$#d*537#;ur==$MjpI-TU&j>4a=j>0Dk0ir;nmIDpcfxb~-nN4xf09f~%w zo(y`gR8~!ec!6*c*LzH=#kE#Ro5XtPl4jZ{vv0^9>t4#K9;O2$&p4t9mby^}^3A;b zOUQ2!y48D!rn}uNrg*p}>K>rNc-w&I8ArGxs<#|WVE|85_`?|vNWq4zLyD~uJw3WA{pwX7q_<kNqd%FBu z`n>#VzH+!&joCPIJXn}G2ba`KJ~i^stZn_zF{6oqn)>p6>w~n#slY!4`$L^smsgX9ErJuawit1cgWE^%Y!jJ$rKb9{19BV|W_G=S1HpDuG}bE`4TZm#CRl z&ua!e4%vTQ+$P1Oh zPiBH%&Sh#d#mGlI6=~4rnsf!UPK?<^Y4hh!oY~}Yd6TBBy7bm(Y@Q_Bq^onh4XMu( zkGCi%f!e2v=@#miJg;W=nuXj6G^PEglrFKa?FWxAT*9A1DFjsKEcenw+O-qGz-}ef z$%nLzp6?@aRBYa#HVq3`J?%YLj!_%x+~umKj_r6{-YmzHTv%jr`3Y@{aS5XV@31Y( zv$sCzL!d0i&hBf^$21`S*Es={O!-E)C= zyK!GX<6S)IK#x6HBy!|MAAlhCMpek@1Hs#p%_b);dOjgl$Eht-x+WBL8*@?Tr7ObZ zA$NUqRfTlOo+%X1@0M^U{6K$xNYG+NwX-rha(6^i-{B9t;kO+xm(B zpo;o1zk7mvR|o&%rp8X;#{HZs3n6=6SAs0ZBr*o@T@u8-m}uzsw~Bh3S8BgE%`D`xf#8{(nQh|CMcljo|KH_zlzd z{04o0iDCaA1mgb*`TnnLi?WNM%m2$n%vL*B!WKvP1_Pn4UiA;~50{MQ4CJsOJkUWK zVPaZa%lk_p>~o}11c+9oTQ6SmNx>8Fb4UJ7*qK_J4Cm!d;X&a1T@#hPK7Eo@0n?~<0~ zMM?00m9wxfOn%x+$=H*C_B~Pw;p+7b^XZAZW`^3up(v=h;rEO}*VBVz(eO0#L~8!C>!8hFD+ZrinSq35-;8gXSVJ+b9sWG8p%KAtL%z_REeN2u>= zQdaqgVi8AaF~qZI;lQchd_Av`wCRwcf;yOh(M|!Vr#D%W12?J_qVrhVU=rM%!Pt`F zBgf&|Y9#-T*(l+`zYqHl?TmO&vrf6rsnRpY;#!MJq>&OMKlZyFQ9^;vr+Nqj^eqv`V_43)h zo&JpyCG#$;?d$_>7`t)DxC>je@|U&iW@S)T#-Y0n!o}(c>M#O7Ypzm;IK=hQ{&~9L zJ>J0!)B%?zIidi1sANne&WJfi`DqNN2xsJNoB*V_(#Wq4n4I!3`z4CS;j>0|M>crW0j&E2G)BA2g zDHY{uZdS0IRX<%pW)nD84Pl_3l<(3D^OT?r(>4mS-+(`RneTM6lZ+`kyMl=jo+8ya zw~PrAG{zu0jbx+`5@TxE_%aeaL&&)lBhZvVYH91C8foNacv2XZXiq<@l&3&?(ZHuT zV;p3veG-p!PKi4;>l6O}6o+?JKp%x)4(0?D007=Kbyb*wFzK~^5eTrl34YvwbcMoO5r8jwHmR9d! z(d{uG#Yg|%&9OHu&DRyJ9z0OltP!(1B!g-s5T1-+N!W$}Zfas;g2|)JqT9HMi5AuY zwoGirQhJeYvB1X4i!2@Njj59@mcdMOw&1d(i-EOg7RMD|i2ElkLCLwhqCDc?Qpx0u z6&f%`a&V1vBXg-1T50wP-Qbi6IBj8k+==SqftgDhZ}KH0=PiXrI}3YKi`C;^M+-(! z7JCdz>zIwTMU8HpWB6q~_=CX=18@uHWC$qk4zpaB-n8A{TU}z{3J*f7i zC~{NnMySV#lFKHHH1$5Qvv=NA)XnJlo-`IH^RT;B;D7+9o)#!X9fVytUY4CBXGYwE_wSJ`K*elLd@@FeJk}|o; zFcFoULKW`GJflSU%OR>!k5v41hQ*AU6O1B-zYI&|D^*HRp##e(Nm={PZp$Skb_7A3 zILe4+l!}l2F}5g-@ccRo&PxDj)3KvD0fIRV*#kMyAAG5loo$v_ENp4#gvUY$BJ=_b zFHYGrnZKBkCq#&-AO1Z#=CcKwOoQ28_b?8n#pDG#X>PeC&*t(}LN1#&AL{$rZPJd0 zisk7r+c0OYT4{H*7bo`NQ5g425ce2V?-Br+B%rdmZkrSTK z!fiUJsvJqPY^vJ}Hq!(~t19c62ewb@2*GVuXLbX|PYHU$jsF}33+6Ys8nV6sp0f)P ziOvaOB}x%W+KEo?3NWp4@vFmAuhvD|B$5Aig=D1O5~A)~eWk9*1I5N<-h|MKwso$e z?YG0WL)=pMB%Bag6-P)Q_B6XIs0V!=wo$6N=P~GvN?7v688%Yybp*Xd^MR)Rr8RiP z{Fk{6_+m?gBV2nJ(ex$CbMyrX>znvTv0?UDKkLf*BaP&P=eu~EZyjG^XQV-qCaNvc zA{u0?wXQ2X>_r^H;gADu_9{B5)xR@beB_5y)($@=UPEzc0s_})IoR4LBUg`x$WP9= z-35%g7_mvpyk3wiOm@L@s2KVzna(KA1=91JDYTfHgEGcQ2Wv3c1y%GM>qctzKGOxb zS6Y7`{@nVLa(hUBfgNu5ddL!wYJ-B$?rE{r8<zm8Y3 zi(mAW#Kz5Gq%K4(4fzdXd-}+>nIXdaoSsGQr(DlM`O+<4^|ttDiR0nE_y_2JYBLvW z!Jlgo0DxP_|45tv56wDTP1_w=73Et_ejz=UcnJ`yc}psw1YI(!K*%B>m8f4S8H8nc zlZ2CGQO;~&3S{`>%g>kQk3aY&XetjiM0i5c;e z`Diu+z=>W8c>FYJ$)POIR2&5dWszYjJXJ)6MoPnssBvW;Duc!zI>X3NGUs&Eai=Or zst}epZAZ{hkx(?DC)ugSpo1{Koi^oT5oW7wDS|v`&M^wocupZqf+9OTMry2f?BBBF zh5_kJ%}kP!woK;h!V+`I9=d1{(MfZFpd1&R{g{oCTj<26*iJn{zT?gmr!E_>p1XsO zpv3IwV2pANH$9K?KA54A{(g`krRS}zyYn@Ls}pBScP)+x&}&jJGD^PvqeZiT@OrL# zJG>xlYp5gMgTKWLJ()-Yb4uc54=~YJjsAwqJ_wi@fi#1mA^A)el&uHHLwwR^_1%hA zEfO3m;f&SdNz>}ou;QE4jg@H+2IYY`^ za{HFJvwNDLv*#XoY!ec+ z_@IJmK#t=|>n$QZXmR*t0WE1qR5|2X22*IHUW_g}X;5KyMErWYMHcEajiT$xpCTM1 z(vx=S07oT@Hx$#;K8Oh~HCePW_PLiSQv?Kt+ZnlTqq6)Q7G8@?yiyD0yRO?&Fj=A8 zcQT``M=VIjZzL~)bt@U3u!VZ0ohP>eoTLq%c7Ui-d#W)7b(2Mk(615(^Ji?D!Q?9h zxT9FBUB)|`99`B)GZGv*#6Ld^HLXyZmwHE8*LZnx8)jp@WL_PJg>mxmirR>PrL7yA ztr^+i+}Ku63mY<6d-#QM7P<=x^Kl*)#{NAU`<~D21yAs7OX{MFFicMesv^-AsME|G z1nUvp5_QB2yLjb7oUqlstxzem&+79+acaY~`OlVR`*OWUE*tI5KRBr>^-nP0M9kI9 z7=^30suT_rYOdyRE1cTO6ei4bUw0XpLgH4J=CcIa+^%hY6{?l(w_Q75VCOxu z9r~CZ`lucD@Y~V|w5$ls>d15MaJs5CSus<3lJ6uN6z92lF@GZAzA?GpPS1|b5l>GU z#(MkMzDb{v!b$k!gg=3lKB;9MqFzJ)#7Tc5(LLhdf&U=>dC1kk|4E0v(_d5jrk1|j z9Tb97*5s~>n(#wU=|r}kiP?lZM$1Am!NGZ1z*CA0d46fYpB^vMm3F`^jZl40jL zHx#$bH3po?K1|uA_{m=PXD`txyYk6?Vk`WB{!ifyDUzv7{r#yGgZYnylj*;4Hr6&M zzrtDKzOjxQ3WN$pYnxwp=H_lVIS5V@x1p4$6}ce=dSUG5Zqs@-L&Mx?;G16%E{`Hw zJs@vu^e$cyC0KZ0OC}OAZ{dT8gpVN$fn_YS&2@*Pb$Kmz3v}UKWjUfW!jX3SFj= zJ%{|P1-Se@Bk0CyXe;!GwbexDI%cK28&UKXdUZ!lq2tmoNSy-8T~?&uK#F*>f(*!2k8^?^0g8Wmh{;_ zT?9gQ5nWkM>~0Ds6b{)q?ntVQ?eQ)pEN7r#L>oY7scUh8bX?Rrg9Beh8&%(Mpz4=a za#|;c<3A7!YXGd#^hAA^Cqhw+U=RfE*qF>Ewi0(Pt0#`u)IbrF()rMR}MX zl=Q$Rssn>iaC?ZLaR-c`fIM{m!!Qe9xj@-2TsX)=;2Qf5K^QS6HXUPfjwjOSEA@Xs`0 z9j-so+S;Kpw3_VSEtJvqkQqc;HN%#bU^TKT4@j#Jdn)U(^VPcWDvS2WEG`MtXVBpo- z%`(75ITb`YG}g8*>c88C&{N$?_K>nAl|*l5&y77lrM6QPlU&Ycsj41m=LKoaxIs1N z^INg*2?5h&7fb=Ge|rD4b&_=|JB6&}NNt+J*~5V{0|yrn#MxtCS4bV<)aOn%8P?c};xFi=5Q=zQKF=!`RYQDg*M+|W zlEagOCqM7xdBmJ?yR|0Vv&lYg|75-Z+51QH%yFYt)1xMgAV6%%ssf zMZH-YO=9^4t{@Wv4DKjtVQ^-9MOO(=lqOum2I6B_!{6*-9QA}N4a35r>T_6JVOun3 z@wual*H)W$j*EwFL7eF@EA|UY6?`Ehd_yem5X>v%D)ZdDB;QEdE9R%V+4;#H0sIdn z#b?ySD{GTA_O(g5Ljdlepet=0v)z4&54_qs{5vr2DC8^gu4%sk?<;ekzL|ZL575%} zT8=@#LCt6GnR@=LrK)0wXGL>3LYeY#C1Cp!d-DVi-{dV+iqr9N@*5tyQB*mknL{ZZ z(eJeTo?EmaIgW&pjXCYIO*K_toPU}uRn_>RX{9Ck4zsE{iC-a7SQR4gQpl4ye}Fh) zJ)@d`KNCiwGbyUlWewpSriYwM?JzVf(NY0V!zQKj>BBuVc;Cs^UsAU#3tg#W-|?jg zwut>v**V@j^IRym*2$h8*2};n`~OtS+MzqW-c#!vPMNvCFu zf)WKR3I%DCWhT;1TB@`yt%8N11RyYxO;WL@g!`b`q$uL;f$kj~5tZKG--!+r77q9O zUg1wrZ4U?E>p|h6JKN@*rSjR|^sLA1q}z||-8Z|=x9ikRoxE;5z*gK+u$5De4ns#DOY64GVcH3oeQ#W{RcqfEgWHIYf$uZbUZ9AIC^e z_+9hJpX8u4mLS}W#(;4gpahLsqgRoSBBYN0?cbSbFgfd8#R6$hx`4omg|Qn(IekORZ*ej4maYM98Kk-M z`KXIc;VoMTS9ZwUnZ$AcVXSH`FWlVKCQn^AEYBOS@?XolSQDd8S-5LA?{#M!-1=7j z)gTgQu3QHfa(bEk+kgY3G}or&l{eEaRK5nQTKqlFBhVFlLJZ@yGjC%qfK9}xh?FJ^ zjD@LA2)DHqx~(ne2<<7$zDTE#H;SAp7p9>?fPKQSBNi1ryhx|DB?Y(vNDlm*n4AA)fCCbL%1)A zihiFQEKQX&(ukC!e9xn&a8IPCG#@Y-SW$go+C{XlidwtxifX?ni)z0g8CmI(nQzrF z87wOXV+na%=7D}&?xAl%=PQ5%QVOqP(-)SELP>jyk^1YdXlh21#|X2fGreXDuA=&# z7MXGy&75KBo*CMx%oB%Nx78dFZzV}Mj3um6#Xc*oQrwcygXU zy?c%p%LqbZkYO%5O}boP<84N~FqWg99`b7XYOv!HUEQ+6Yn)QHBU8=$^#)uG^Mu1RxljXlVsmW#e@W)Aam%J1u!7L7DW@FXczOOGDX)wg_ zBBEb*-vkw3{_wANI5osego>{<6s+<$h-OO}tmpo#>hG1Q8rJ?}u0rOUzaBvXKP~wgUSVAp)r`Wx z&K9b`M6xaF3it>IJ?L3zsE0~%F^m4r8d1dGM34XzY=taIi6TdGJU2iTy0C!N86{eH zqrxr~jMn-w_zjc_A%!PV4FCy^Fk8Whb^=P%4L4l8hwaLVu0T&gkDTlnNVl}7XYmO^ zM2dc&3f=VvR4W#*MIa#xQqCD+qAIXeF9juuCwL1Im&blTucSd|Na?986bwE;g*x;` zV{<~)@*w5%xPEr(9DkrXcGu1^Dcm+z>6ke?*xCigf5$NLD)c#lzVEIV#{X*cIi9vh zuNT+*#`HOeUFIzQcR;YDtG-PEx2Oz?p9pG^sqb&)6bG3$q%ZpM@i%J|6)NP@|27Xe|5<5a`EN&jT^(5+ z<44zN9L6ycV;-uFpa3jfDUB`?I*@`Q3JWCYGCq2|&xC9S2C8|PuI2Y@cI&6B?K*nO zdPnf;$X?`YDW&(l-kn?Rd1@-%%?>^m(AdaI^6vG1^zF6(vGe;r&BO<|8aM@9+BX5n zK0}gr2bj$hAE3l6HO+z~2;2dfDUhf6B8Aw`$}2F!jCa%<0mVc&)k$>J96`mj^7jB6 zMnd**thWGEDJ1td(m{2CtS1>RfbJv297KZYPeMv7fsq!|`gD=ax(2}ci=+cd4D$`# zQQjGbu@cG^wsdJ#n4b|b5^jBIR#0A86Bp2`j6gAlnvxE)7g`Q0Q(;CcJZz9oC-UbM|YOA<@u7HGGJx3YO#bBfN*XAA; zGILOt^^Bc~f&rCdKE#XfrIokQf?ymKV45=3XpQHG5xpT0o?h_#?H!z>fo#csVGT!Q zPTqqEV57 z&RQ3E9Er~af|i{(gN-g4HR=%(D&0PF=wQgZUwiik3E0rafK?fti+Rbe522zqpzF$V zs=_u?MqO4L)RX>lw22b)0!jMEc~zxyPn637KGojF+nf48%|$T|SqP)Ba>!x*zTRY} zn|T#>b?UiQVa+LC$Uto-iS#Hi^uOI`^G08G%b{OrcxdW^j5I@8-YDX}f`3RRf|QP|#}3P&%oUNyLIwW3GOC&6&kZ9ZTa@paD#doI)YV3dLO`;^D)Q{qq|!!1LFk<1eQ*~#ZwX%Y_L8D?JO^z_%7kfbK@ zLxHjvj>i`dZzZ1MuFK5rK7n+dE=cm=LF2GS(Qi_rkh#hFHX+E@f3q9AQhOD|Wz{N+l z_00=Ci$}0aJqTZRVIMD_nFe;p?+-=<0qO zxGSLNJI_6M&E77eu(chXy5ijBJnj7Mx#cE@|9Ss52e5V^kETE}v=Q&b;2J0!g@fca zb(!#Bj82O3vEx-zXJZGOlHmQ&Y&H8@4Lz0?5??#0So{EXFveBQuRQ7_-@T2E)|#9qq{ zW1zq#tK5hjdclEpJ=8(Ny}EU(5fpL~_+Pc5YY~@0simYD_CidpF{$Rlih8BW-Y_1j zC1oB5X%h#RKCR)Mwb4|{mp0~ul5cvdWC;iHn$&2V1WNrC#w0uQ!XJH?LjdFNB!fLu z8V1#tUZ|;Zr`TQDiQ>S-R5LalX5|z{Y9T4hpg~zK!HHwNqS=6?_1ugFZU;b*8xf0o z1O$cqb%}!f$gl42Oi)>f2o4y6vf+TzfuZt*5r$Tf6Qd!T4#%fCi1cf7Q0@QM$W#~f zcK>hSi=$3Iq+s?CC1|Om*nlSJDMQg751)bqAUdkf0A*nF>I7c#mf2EZrM8N9K(y%m z1j~sHR0_uf3b63pKWoafcO=1=c++;H8S9GEm}&!PE!3)EQ_#$Mxqei{F;$&32jPC% z{Uxm}BaNrrb~FwhlW@6KCQ8;tqZEhCeAj}|3_E0xx9apY&p;XXiDjJdNUF5u`k3PR z5lZN;3=guhYtXpkhYUpm&L4}~N@X=Fmj;|{f->O4)?j&8}yv&{EgeeWsiI*(M zWN2Bz=@wd}IN5DB@S7FX8pD8+F6(pp4D zuhxukE$K^uX{RbnLT>=KxHG6BOBh7(SH>z5EkqWRsseKSoFy*(o`Rg58v@=yURhBU zm_zsspl#}?J#0@}`0ro?E(A!@MIIv3-XNanSPxqIf(JUYVPnzY!g6lj0^MIE6`?=7k#&PWT1IfN!V49{3-X^k{hi)&l7%Cd=< zRv?<2g=mMVp{Z2~Be`@*A&pg?*0qFrkRGb0W5tUoryl0(U9v=Qiw`+S{D7eNBOv%= zDENa|KWW(RoY$si3sdw30`-UR{2+NhncyC%72Z^z9McNv4Br>i#l6hn7iAXJ$Th?C7E9}a* zs4=aYnu4UNCM9nySuRJX5W71I*epzzB}T+vq}XR7u_mYpcf{(V`{0%T$03a;TR6?QS>fWrb1MG_&}Q zb=3R6AW&(TWxkZ)001X&|B<+}{kN$E?VzKI`8_k;?rm>ppKl|*iI8X6*_?T`02{An zqoswUgf%tTAuXj9xn&cZxoLN!d$W@arlNqy=ke;-bkK=3T)}bxCK_)DfSVk~lcH{D zAeNBEMHM~8@O^q?Cb_uHB5d}Y^^^7E`2F)S`;(jbvHSKf8$fP2H~^!=YCa}M>*+uk ztYvQyOi>cpoPf0b6the`H=vqBAMPu$Q&wq{)HNW$buIsJK#OIc*y)1^H#HOx_%R{% zQjik@7eB>$qz}Ocmz(u!Az_)ZYLIb{h`b&B@~$YmOE8FjI!=O_o5a2qhc00dk2Bh; zO=Zv&Cf~Ki_`{ruCfe<~$xS<=>evKHa_qi#M-sw8aDzv-(8mxA=}dR5j7_ zgqo_-h)pmZ1!TO?&aU1$g7H$0C#M){Xvw-N1+%mp!-AjT0Fn#GIDkPu!kIHLv=(Kw z7XVN)akK`O=^IJ4wiAJ#xw1~n@4 z3Bk|i$m~ZHQ^d=Cn#n=%wH3`u<`o{`=!ozk8!c_`7od$<(Aa$mLv2PnG3U@E zExme9k)}LIkIAM|@wmQXR%rv#2y@ef7OFww8F5`2)={k!+(Ze5StnKy(|C@dCH88r z&9^)Sq*SKx8Jd#HOS`bJCDSUY!((Jx-jB=Air)>)eVzjrb1BhUnlGcS?vW$ytQ>-; zNl7aBEYu?G({V>#;krVpC>f#!G>D6b2Io2m6r!nx@Xoxal$@WJrp{kXLOjb*_1P)w z=IPq0ykS$eXz2_tj;S`o zmaM=-#(Ur?lInJ3$s9pnF;H+qw#wTNtol*}$rBi~s+@`Z>~dK@Y0 zwH1{C^j(>&H~j_3MS5kQHidd0EvaQ6p?(*RK+AYbQ4l&{wE*AsFz!o$tgPXNEEh49 zg&Y(AD*JPe?9e${7~g2=jY4ynRcn=o#=$|FO zirvLZm7#ipsFIa;V zWqcra0l)aL9v~BZvX^u$iR5^bWOb;W)bEG{dhLc5m!x?AXL!;FhDPKT+d zM9b}Sf)q8eEu&NEiv)-)NaKcsr$VH!*>%S!N;*O$Okw1r+2L%`kz~+8eB}(~HMK~? z^&2VLTS+vq$BYm4$o^dmmaEbcm+WzbnODL|8Zn`IA=F_qN^biUcw_5{RqKf96=Lqi zt$eee8@cV{{FHNf=$sq89YOPhe}C(4^Z$WE`{7;q^jrIcAG_y2Q2GX_Ie{&YR$6Et<7o+<1nH4&@O;=)-lS9v}5I0Rzsa+FSBv&>rlYfZKkf#WNk)I|ssy^h^ zCd1anP05687NK0=6;@!Q>?p$%qP4Oaq!kt|q0y4p&i8^Do^u9rrTW;-k!0l!ilsjy z-uHl7wS}I%(~p*^pYqAC+e+3sah?O(wTqvyXa75$o+etP;_#bJPyH>t|3Lti z{r?nQRc+@*F_dq%mMYP*7{wb7O2EVs$F0A?3r(zPEQBnUu(V?!AWuY_(liPraYZgA z{G{@}=f-*yL53TarSjQu_HV*4=En$JF+BHqnAy(HXF9#SonCJjFTDUXee>~yU7<*H z5)FBVi0G<5Q;rdb$g!lc@|=8!uUUmtj+%q0I?E2QlT8q4v+uI+kBH|!*DN*8oe z+wYKBBcaJXO83o?P{Jn-hn(}Oq5D3^5noBVPPX=yCSn1Hwkhmp?3fFce!>h->NxUS z8|yU@c~+}B3mB-qcAc4Sdt?6sm~dIM;{jpbLr)_NmYW9^;!70ngQ^#j=5{SpwA>IP z^P2E%JC_B*Q*cLZ!~3GIBxGo5QZSd)_EtqD?9?B`zbd|!_fz6x)N_3M`iMg3?Fcd6 z3$O;;4}}>mN_vr(hGE6sI8#;LyN2TYwH_~O@R9u3MVFqixkenNhuOJ$hHY`(6;vJ7 zN6Bs&O@81xoAp|?6Ary$q?Q6yBCPxl&ByUS{75{N*I+fh2_$J)Rkv0R_X8rsxbXJ5 zLnDq#N>{qXmPX5mIPrTcvA7l*mf_;m}Fi|DhkFO zXf2<;veO!H7WR+KEH|^_uL3D5*+b%y_C`n0eg>>mF{@#{dbE zJxfL{b$SHUZQvfx3*B*$T~Pf57Kj!k5Klwe9iMToTGtL>=3z7i&PPecJCgey(Ucvd zEq&JR&7~ELe!kk--rXE?H7a6UQhy+ar7$rUrLZe3DZ$vQqa6pDRD_vn?Xp;^ZARuI zDn`6dpu2jb?v3(ez7O!nv_{HL88$H5)QdcTy+y#>=SkR>M(#R?=-iC(MJ$X>(1|6} zmAr@B%`t3^ZEg|5J+nv%uO*Olre{hO(A+I247V%`n-YQ9 zGyqQ!8hrc}DKYP0z|NVA#m4B^rKe+Mn&}%eA!2DN0LWv%dh?Yn(P{Fb_h?b(J z=G@F93^evmNB2cc?#0WUry6xkKLGZvr6}7{^lyMaDy6sU0xm2GGVW%xAC5UMAJZqx zy+2(b@#4b6APWy;Fb6brl^OzSqHvzSJ4pwst+zzO*KN@!wh*8w-*1bG?v&cH*Dq@YcvmtIfW5+ zS*e2#mKPXCOq+GnWF_jcNir)nQFpW#a)&0zB^%nWI$-wY*=nRZsL5`-f?6txZkC(A z=ya}+vAO&X)Sr9Q*`;F0`r^-8Php&W1yMC%q_@?Ickj`TPv2Skz4d)!xuf23NntbwugW)blu|V0b z-A}nvC7#Df{DSiXi9$uKgl3-d=z(D zLS?!8Da&c!_hw$+xDDIxz7^?D&l>pv-(cprBjU~qxka~7gAs|aWQ%9(d;7j&5Khmc z5rBNaP9s)H;wb10$nFpaZ8meJZQHhOr;CVPz(e46Zr!maz$gy>SveM4{@OV za47Zr*>Xds9)KzOBlhpOerId zh@;H1_ORKZ!gEgOd4vnLuX7BFMOYDj@-7h;2n&Bf{{8gF=P^aTf6a$ujmb6hjmx3JT((FPpp3G&vcGyNMzq{(S{Yp{ z5Pd-PJmv*N$Dn*2edgs4l8|PB`%n(=dD7oxvOtW@oIa;seoaaFy*-cRfv_K4F;~u4 z#-bTE7krOKbfLX2=Iun^sHrCmd7`bBCny^XFVr9VfnDZU3Z8dl`(PMdpuZCn=J*BNxk(YErEFt_^i&_~EjXwSafHspEV1^uh32>hu^pC% zQ-*M0#CXP9%C;ab$FYES-2|Jg^IB**aVKTQ&z7f4qL%s0$3`8eJIIKgr3=|_y5RI1 zz-y$sbS4eDQaIv5+M3hy9&Kj0uruJR^Dg)?rQo|3Ur(k_1iKjJ-kHcgG^jx`+^joZ zBx-5zBzYD6akXrzwXSRVL&Fim zzgl77H%^fqafyHeH<3bks8!4IP$nE+*cqVsS)(e0h{o`W&MG?>2g@}LZH_TpR zY7D#QW(U#jq}b3pY(T11XlCcDQBw!Kn6tA(PrhZ7Y!r~ayk%ri$5in|&8Tz7jh2F8 z5oWmp9Uz)pbU4KXxWLM!wlY(-pmQg2N z6ai*c_~oHu?dHP`xvIfP+xOX`$Voh$gklNHu}dD@jPwM@IeSlw{>w(RCipN;Y(bH= zzy5np6vTa0k!oH%b1NzJ=R)p9JyIFgDi@2zkEM&d)p??Q=rCF?OE)d3eiW_^(V@R3 zP46!mBjQhdtznsMBFyo_0>?)R)vRiMxMOtuCMpTW|3h^BV^fYTIm0fGtaSNC^Y`_$ z{lkP*=2P$XmiW*i&!Cg+T(cz|k15}xM9tQ|vw{T_(H3p!hc(j=bF>d?bs=BQg*IG{ z$1Y824Y)vPy{tvSR8-1IP9@F@Z}zrFUWZ=3PUnT&l}|y1RH>284BCKH{3oTYoD4s z1QiSm;}5k5diqR_-LVmkF};8DJ4w6a`#%e;k-9@7zZe!qjc|G99O>2!`NkTbP5VKm2<6GT%HF=FYKW(coW)miLhq!o{Nm za(F+IY(&svCPZU%DL2VqA%(*Bu?M>5|APH{9i9I*mVoh9N0k%m@_@JZ=ZnlP#0^cib{m$29#$Dfo z65sIJ+7iKh!OE&P8OD0Rw~Ut4Yu`UFe|+;Y{69b5zW>CzrokR;7T&H7jB}87XdC(6 zu)d2@mKT1b+u&149KnP00Ly3~1cK?!5>7GTO9?V%q^VdR$pb@!W6dg%)=+BzrcWK* zO%qnAdQ`=DO0Sg%@s8fGr<7)H&S&U-ki%dNw7TG zX?xa!Cy2a2maYy*JF_OrSLEE?-6aeZ{xa0k$$MlTKP<__X{j+PLU*h(8O?OYtvyG> zU0Qtf)Tg5m55lfYcjm;nNeZ&UM%(i?e%0r=U2D{9VCJ~uG%;xle%iEsec~&ME6W)B z`wqHkQHFQ&*AKx#EU752iOe~(?i_zn>Jlv#<*2ZsVYAmjq}e^Yja5}(iLN8(S4t2U zA2i0$_9#5GHc>ITkBPLu&x!nXFB3v(!h=rkfH9|7e0UpAhQT)vVW&OVCO{q9V#zF!PtE8Unv@`gx-~{E;pR#e0AKx?N{y zc6dfH_E5kdS(EdX996nsw<#59z2O$Gh&kbxKS9PQ#eVm)`boenE2X;{WBtM*CqJuP89jls?dZZWp?qV8b3Q3@`NTHYu-)l5F{A7f zk_q(T{Q_S!-D6B2V4ME^tG^iyUB{~zjQBMGT_Yq6a?~zfb$QP!ZpoC3mtKC=GzIPP z`qqH!RjF{efa}u>w)s+Z9)*UmjO-JWXJk*JigFi4))DDQ+QUIzz{XLo3O{`HhLk(} zS6CmxT>pqrYF0IY*r_)({t*$Yu~&gR=PB%UY2(Ma3@;u$Lh;_Ah!bAS$lm^;kZuW% zsK~jX#YuDML$llu;b5msQKG_giRjsLw#Y2W=z5u;ha^^>{c@x&_Tz`Oy^QPHA=AugQ$8$D#?E$ zEJt#rc86b6THaTE`JW>!tp88qtBm_aXd*D&Fm3AC7#_7iEm|j@|5>^gAC(~4LeWsM zTzbI(#eGP7eRykRWVSh>t~z2pmPS^%er(51k!k+F*vEhVGUzA z;>4ybTgUyzi$U`!mM;4^P`&1ufaLVjtb{zKAHuHQDbg*ONw zx?uYL6dvFSf9BO;fPDuGeUuP1mYn>LJ$oj2YS<*&D$(FSSRgdOEi)Ei_XVR`64@}@MG;+nn#FCUW1DF$t2q(IHU^|gmPpA47k77 z6{VI!SQrkM)MWnvobx0!wbxzP+*)Yq8(~CEVGMi66dPoxc2VMp5>2JUoU+de+G>_! zz1A4Lwm3G`#JFQ+E5<|r_XBb|B**ksS(jl&jkMA5X3|h`^5uM&8bD21&pMR@LcQ_-fGH$z7 zt)3#>=jREXI^2h}Xp_f6L@>^Kyv?Q`-8wB?^P(}|N$!1&hOAhNFnxK^x16{z1z#+3diWmfPJtEj-vv)>Xz)nT#ruS^J z@~nxQw_vb*7HAlvmQ=JbDvb;k8d$2Akrg>n)bWeavDhNRAWCAPCNU=`(QIht?~58^ z@{-MgVlRY!w1WFwCmnm44QekrtJnJsI`>;GLDZ$xMj$MLPWUi^LjLeaepe#w=w4P%jXi{eWV^a1_=hC1_+z}|K5$kCS5ZNpQIa!g*s1B#M$3(96OF06gYr2LBdS~& zCfbiBlN_j!RNjtzswbr2RJ3KV{%gC?P`4qxY{sa6&i z#TZ6@Hu^DOnSDPH*F@@~aGis;>Pf-~?Wia&Jc=A-F3II$9$dhauCQG=Bq?&BUi>ty39S`Q+M~O1HkaDM_INH4)Wj zTiN^t$(A+ZYqm{aQhtu9Q?R4Zh7*I8DPxlaZ7$!yJfRv!RXn@x?%E6swS)pBvdL?-8xeI}pv zX=J2$^fMi&sucQEfRr*Lq5f8mb2Q(>KloQWbbrRW(0-WEn3I^_RW z4)yLRG_~lx(ww!qapIcm2srUh9BuDt9GF+fn{$~4{_QL5BOdNZw3DBeOE^t6Mb#{T z%}+^+pdD#oA6g~YDW15jaumSf{rerlnB_H)^T+$;xuqzY?@Hzb?oT|%#a)H=9N%wm z3X$A^MG=(v+`Pzx1dGoXh2=50>{JuKh)miAfWTCUkC5vzdLyU5-qA~DlaLZe=^!; zA|I)N8Y+llUIe!0A1Wo31giYw-06sThZto2NaQ{G<)Bj;8!O*?SBSK7KR~o!^>^%k zJ1`_bjn|FxIs0wLx!1e3SkT}9FK9jzk}7^b3&wZoaEfx$;QD#5kpaho9As%xF?3(p zSJmv{3NN7JR}y=86C3O->~Z#w*{Se}_Wu1YgY;1EgaVp)oyEM%glaoRQ8S$_W-OzK zefl6SJ5%nV-D1Pv6+U*kTQt4`(&2)ZB~RDYc6dg8h+aElLxvzhsm!u=V@;h$4#L7x zQ+na_JZgSCjn!L-ZDeS6j9HC5L^F6B@NMQgJ14pyA1sib8?B4mBy9uzZhgs5!C4Ii zFGd4Akf*Hd>O}_S&SyBhZly*F=GEOfr$?dVjU`IwRH7f{3dUx<);ubGQn!p+I0y*s z)W(uS%;N0PMlz%Ap;PJe@yO#}>z#P?P4ZGxoh1hXuJXhvm#}D@FOS=0<|u7k#G}?w zK5Rn;nX87Hv-ToGx$Nzc@1Z|xD9p}lCsam#Lkj>&ipE@TMn=Jzv}<_iwkfOG5Xzx`*h3vQ~j5j-tPR3u_g_y(xxpvIv=W1P7kr zLB9rzORe&6-lmwBn({GC6Z3z5!C=QOKK}+8u8YluWM6cziD>roI3TGA9t>Moub-oc z3Tl2G|F-DNo>U7%0ITItKJ18Xd-d5jt_`dUjw+`!kY|#TN7k)#x zIsT?4MJ=}cwnFNLk^dCh_k+&pnfAaN_Rd$>A?jckVaRu|o=aueZ?4k41>XXi7UK0= z<0Goy1qNjC8g?SAq@?}5f>)T>#doJgD2>H2CDt2j(6l7Y0hB=vDZWTVqo@3);oT6s z#9gHWt&0od9VVSn4EWk9?hg{(!bMRjIiZ*T7{X&-^O0OYK|mnCPT+rW?fp|w7NgW5 z2gZWz+eE(s65y~0Axw-KuMz9SLy8Pt{39tma8|0izFt#8FN{f8gued1ZBn$$^ag`zD!RmoTgS0M8C$=-QND+5h@EO(@FIV4FQVKQZ33OqDLy*TcOQ625dd%a zT89iiw)|yy5=Q!{t|GB^;%HHo{lf6%x+Q40tf&*GGWfxlbPC{ba$u3BcF`6v?4YX* z;~D(4SH5ao$_gO(1xPzygv9^kcH`;Ref%BG7Tt=i?d|4jo16P1j%MhHZ3zBxmt>0D zoTW%W+zr9#Tv`8c>yqI4AnUh)Dx;4_UozrvY`G#H>;a3+6^&QiG11J76QsLVfh;hB4#7av7 z7t=ap*Y-B&`_5?*U+KspO_-A8F;_~B3tHR=_qgbh{(tDx%*6_$Q->clH?&g z(3)fG#ghR-_1@E?k#t}`voha)SU3J%z2te9cs@li11{Ua6kg zO~0aW_w`EvmCECW9)H<*moGbd5oRIxs?oRln`tL^6ulsW`Pc5^ska ze(IdzcuXtsCwLtSv;GAxlOTC@Uiw%%_PMR+l%lp5Xf2{12Ux{v*>TLd5Vb6}jFsi-flxZGVdgxxVJP$WL3%^G_zxT!6Xt*_ z$-=%bj7ZD1-jlk%T=wf$16*Ps- z;_%v^AEuMXC}O=PgO|q2Pb_Vwm+E8+9P+ilb>IDeq1ylO+dCg}@c8ldH-z}IZTX-3 z?ft8gs7WjTZ@<0oZkqP+ity=Q6^Ze&5yI_|KN#V_v>t*xwI4+;ySg|+qkA*OU7dXP z&Hg+yiU}W;XtlOD8&Kjj$x?@sVcbL7yoK2qNXQI>NVsdVI=zz$xUx4F+**PQGDbu<%INGRkd{MGNC+68(&OjE)4{XtN}v}+c~I@ zlA~_S*c%h^iX{)U8+=m@50gX8+FfX6VfI+2o2}-QTbEUo9vccz#N=b-W#w4mn{VhA zIC^SK;d46K>{CRKn1O@BEy~soeP9Gu83{?K`CyecK7R4k{FW=zxQ^Ehw^B zyH1x1`r4|Dgu81LugGeSSd)Svx>gcAXL!_2LsgXmvB(&$!d!2ny>pSwZ{BLWTD$p) z6`DwgDCOV#)Y2e3OTwzf?vqP=BMt{Zx9pr^b4%>QtNcA^#>FCLz_Kdvphz=z6mrA4 zKF=E0m>p6IEPaSO#wsS&5#}HhsdMA+Z;`uj7quZMj`#CVrpu|h~9&mm(4J)OQ} z&1$%xJ(Q%{=OvDysF({86fQ@Y-w&pOhhQnsBjWZE&EiRCX$cp}^oMzSAl)wx{~`+> z68Z__60&+kAiM|2zDs?f0ON;5UZ&uaio%VO8e_Ic9*tljs~8L1JAuDXjD7USdbBry(DDBy8?8x@SuB6#d zTDKN$$>Qzkv@Weax%!H87Hz?(J3pOAVuI4xYg_J+)WTiLnO`-#U71a_A8K9Jp_VT; zeM?}~O3HT3)N1mQ0=h z@^65_%ciAQ$e>?>N*79L4mQ*wS~D)IPUDrbomO)Y#8!K zXQ+M!M#yvJ9+-2|*h=@`^i=LqLvcPTBa1v_#X!eYbV83@V7?ctj$C<&jAVI8kMLGw zrMq3GAR7XXJjT0SG$}*00j^2bKJrSNlPB!E5RIW7+JjrPy8~UcyF+i)gR5QS?@A+3 zK!uSyhrq^Bj_vB&!!=;INv2wwY{$OG#`4@1}ohn16QM}NWuL$+TMXS+Rx(xCAHE#R5DYzX#%z` z?)5z>Aluk!eg7ehT2X%?jG~fToH8BiC#i#C2_xIU?ELoNjpV}OHg4gJbQ)~-N!D6+ zM-%N4IHl$6E8)!QVy&GtuKxIWMW_A{IrqsrsUX`Fd8556 z8r(2zXNm^9W6eRSH>c$bwWHYgT8jiat>l{?zPAS#E>*tGb`I}11)OeSCZ3SC!m_f7 zGUkG9;QdG(&~XZ@=DmA9`U`UGSiCnIcoWfDdiro&8Nn?(ixpIjI%?SO^FMi#CnOj z(}?4;XshfrDFJeL- zY6PoD>1p$4a_;<&>#}RxX6O&woVv>2xcZ|Uy@ozRRLpS6Qo6HBWz89Q6;*D@UTlWC z+H3?v9Q2V+Y75Jo!d<4&9>^UeYILze-NJ9Q)urwT zia$p$;m&Kkym7FMYN&$5V@ds2%Aipxh94S^%5nRTJ*t0Re*5-CpaM6RP*ncee9RLk zb$x{O9n|Y|3GLxgBzsK83A8|{UJ;=cM`+D&x&~X}XI4$8vefh188do3n-Vh`9aFwXPoFeFy zM*DYxXbaFNNR}VzzdX|+zJLLGLUCQtr(-d&g%SSF@1j`G=gEoB2n|3Mb|>W2crX*| zB&<+U6eej+>87zBe$;Xrt+wcztmy#_J$6N!i5LBffPw`&#{7icE@Gh}mE!X#Mv*&4 zooG9qZ(uj)Vo;5e#9`zzujI|=$zy)k147)`h4S5oj|aNfBQURnZ@3>o#I>WmcF@10 zF@?BX5y*C!Yz|TBQCq{p+l;u5VHTq~g%EF|bhoH!hjt6G-eTX7BFPC+yYA}GNBj&A zP!{4OMnLhA!|vKWQ02qPxXE)z44Ej}6nF`T@f%xGlA#8L)e{XIBs{ovda!C zvhsOY{tz1OLrsppaW*Y#HVKz`JGhBz(s#GGH$WtH+Bd8d+`3{DT1A2J$B-U0TNgbc zYp5DQ+JU^{2W6Mt5uGKI&bS;8yBV8qnJc>Zzi|IP$cpgo2)lnx5AXjK8{q#6Ol_6% zRMGuXgRdKPLEC=DG@rC^{s81_)gai?FkvLOewiDo)npx`Vsg3<-IfA;D?Z^4vWR$r zfw5u;s%5|(!9(sN;LJhRQAgHYVQ>HoTgUTu*PQ3<_c&Bl@-p(9NwEYXz~C_k%=-Y7t^(N^1ueKbMz5?BiM zGXt++?WjFe2drWB)D!xP1G8v(WN6Y;??Z_@DSYgwJd{VZQH8-Mxq75gGfB~$M|H`} zaQN}0Jz^)C?(b`9Ur3Is(64@*&1Z4w!o=st zIL~GqPC4j`^Xa0sCdJHjxRDS|;l)m6ucxOqQ4)-X!j`5O$_=vduX-{v3eEDlWp*69 zvNW#QF6Nr^`w{DQU!{UiBstg!!osNP0fgTMRFY!`uxL1P30iaj%ChSaR5P`iq-PE# zqFy1H%^@nCBP zWOWDdnC#r&&H@{i+HXn;*nU}3@7IR9L!5peXbJbm-Gwq$`@0vJs6FYh5j_(jBxg!v zo?D=iI&o$>$uf7BVUADsMJ~GWio#5~4+urNN4ZFGV&{+2bL2ORKZ}@Lbc_8xT`Y7U zOd(?8t6%IOKX@VXCm>ikOn4HgF?a&l@g9%8ii_g3ugs1J#$6(4SlTtpr6mXtFSYs} zo)&BzYrM3rTi{PeFen&k)!FR8ug|lx-lE_(9V;1OGkrZ)Cuk@)s1juA?0(9kUbRsmY^%Wf6e4kRHeC5 zZx~7I1NSU(bcEStSHyx{I-pHqw3^KkIK;}Un*B{+Y}qZv_zTuv{av$hpn zC?a`Ge{)#*xDqu4Gf@;YtJf?E6T8&|GD#vZ2W z4h(Mn@8`q-l>dw^d+323>VxpIz@Bg8X3$ijz#ro*q4?ILtGie9N|X9Od7xIbhey+H$+4kwY0JRVv=cIdXjp+4`s}BPKKPJ}TrItEzGh2E15rDxqf;e)y!8 zJFx{$<*M$I&gSfX^;#Ets1!qGY4uH3|3qOsE7RrhVNYHY% zWuzieimWn;lx38i3zU~kPao4f6<(i+d}ub_*p+n}?+qSZToW%p-D(FxF)6zv$g2-U4Of-W#=aDq z@z8==A3=+|GRvf0yn)m!Ef`>v=78e{-L5l;(GKWeL9HEVL9HEnLbdCk@$rfZ*7fnC zbCYh5ac(<8glTC{l1O@&YKQYC-;U`)O*M)y&hc6U;p&kX>}i)D{8*Q5*tSt{kr_$6 z1Np;>j!#5xNBuw+;j>==U4|Z#y z!96=^nl8vF3p&SEJ-uvybe&L%yPIyuGo4jjT-lgtD84FwV5^Yq8Dkh|vnpXCf~Xxy zvJVm3{g_-ZoK|3oW(crW44K7&W2cZ^k(86l%0oB}CwSTTta~u0eq>sL9V$0J#j0Tj0FY8tdS1giPLWZLY$(RI zE0)Hqg-#8Z*j%I)sk|#oV9HvX$Dy`fzjtP~@LCDet~dsomcRYwvi{wD?5??5Uy7v4 zJGpWtw}VpCz2b{^jjR<~ol#kGsgiqZR>M*uOyOr!6pngBrXB%O)3i*zQ%-yF#>&1j ziYA8d<=1A!v5+T%itsCcA)oi{bq0D!MqiBpG57bp$=?GR*5veFRZI3qY%$VpzD|k( z6xAl7E5JM4gym|A(Wx0zUNEdsHb>>4<497Z=v#YTTcu2npUyAVY<{n-6}817a*$8Q z`MN|sW%#HFZTK<SqH(>ATUX5Eo@h&w zFmo^F<42F60-ZUjf%qvlGHg_Swjh!PHacut2TwUlS`RZcjPK5YP%Q4qaJJ!}+^4E% zuRR@(=+68U!DU0av%jzh;hlQ=-ELZ22!>2gKEX63rKc_=dxgL)sAG!gxj>8Jhtks> zm|1GHf%?X04!odU3Nnuz(A?lidSO`h`QY}8%D)-31i#{t@#yXmaqk$l?g>|qd|eK~ zMDIw9fY4iaEN+KrUZy?(q)4*jdmJf&F~wKED5g8O-r-+BSpOsI(K`g^eZJGtXB${Q z1JL*J)I+M)Z-Px)?~`grTwTa&3XO4v&>R}#5&BZlsc5dqp(2keJV}ue01{Qo;gy2i zG(^WLNvg2kn2N=LQj-hM52gbCV;X)CSP!wVy$Lj#f+}vzNFVKcB;3ElChw*q8A;4? zYsFkA(e1%(P-;)O(Wx9XO*ExO_+U3jFNDkN(h(xrr^Xy4I&{_JC#c(aG%O9~J z|EI05m>W5a4*>{B0PlZuvHVLZ$Oq%0th)M~P5x_#oJkrCVz3t7ght7YW?YF{$;}dC zs<7C06LPVboF=P12<|pg`iq-MO>6fPLKGRramaStHG3gL(Hws*L&zLI;BWR@qEbH1 z$Jku1`(Dpkw)gGEuc%(P$BiE0@K0cPJ>Y%bKt6Qp75;8@&-l+#8I z1&AYv3xsBTqLkBy%xNg67Y+%*Q54f@$ngrK1uDWB!TG^XFc_$`1jvC9ZuboI#|(q0 z^PsB8hRCLzE2x>mk&<9fIVqobj>Riq`wJfo-|N6P3HX@@EBN1r5RbncYPN`%44k zg7qoh)d%fm+=2%bVaCW|BZyHbw8*t6Hp#6}T_cnVbqhsEFZ{`IZH_F55p_i)?H~yu z^&oNJeNeS|pbI$qOjs?3@@v4h3i2dwE`aL+5(GFdhikL^mxD#Ma{OSVytz{H*QG&& z6GrnnfzaOP9;vb4g)GQ~6BLZd!#TfGF+tNKKEgqfVtl}&H7FDtc?i{I5LX))nF=B2 zP83KwSjnnNF{`0d!z@MHJ2kT-=i-Ei0KfBC%BqAO5`#k*5u?J4j)sEWd&HZ0$;OV- zzA}VMSXaKOLK)o~RPP0gsE?niYbN8F6P6Dg(JhC@shO2 zW#CN%ENp3ODN$gb>4*^nM3Xx-=%fgdKb5hM!-7-?Bm)%zg{D(K)(<0oTnMuYk%<@I zgbXxJ6mTC|-Wyo5D~W@jENEcZ;*pOgw}PW97)3)=eKQ%B9Kp_nMn;X27cCyHyaeC6 zKp#qU6;8O!4EDrabRK&gi$>z2FtN>EVI@NiAw!i#R}n32AdWyVfibs~r57I~w9h4$ zTjVnyxOB)@u?&jGj6ap8imD{Kz%8uQ}>jm zSHy{?`aP()+T6virHT;~9TA_F(DK7%G-zPlBP5R)fYtY{(7;a5Jx302D``L z@?*#ZmNszrSy)B-Xoeg7Aw|quXmkj%NcR&TvOIq_rW2hE{fgP~W0HzwU?^#eOhbW$ z-dF2m{WjNCM;nmcAw0Z)xEw$b*&UD(IUS%8IqvWwhnUsJ4j^Ouoe}UOis12MjNzw; z8X#kbe89$xLHp#`9Z=;s^Rdum2P$~w*c^CsFsP*Qdk0 zTNZ8gZ%q3k&T=>qqUi@?1>NOlvv8|J9OA>gQ^z^koKs*7CoCm?*%vFo(8{b9NT@Ok zc_k)Tct&^g&C*n}V;*BPIl&;xlEpxCG_eJUB)be{mEenhIOEWoRb;j>DG*0 zWr@C^^2eO{=fDrhAZ&Ou=HY(sD(V0eIBAZkiZB|3@p#D9yGbJ*;qZg_0O^EhCOYFE zVa>|ohzV+`iM44*6uh;}cS_wdV^f-4>hl-l)~Jd~z!Z;^0ChHv2x)kokSX}?(2Z}V z1kzIBzNIQ9GVp2@izSUd*}D0($cRny@%9YxnL*TiIF#5W;wL+a=>cnvfaV{ruIUK* zNsX8*;2FePjX#?$_FcI{bt4`E1J;hL2<(~f#Je@ewBfrk?Ah+z?kW;d$o+>=S>qTa zzrz!(p;jK+bTPMAlS+=((;p!G`hmNtVtcF?idp~)S!-AnIyo8Wa?Phnj<-RnFcMf5 zJwQ6F0y75KB~igBhj!ZE6%h`jmQRLA!_}B-bmNf~%BKcr%F}!a53uH8rgK1ua%5^i zgaWh!k?0FKtLQ%P&T)|-jY?9g18S{`nz~M#)Tz$87GF|DjZN>jYl%B^YhOSY1bm75 zXSqVrhJE=v^NfOaf;p`BdAq0kp*&uMR@+Q1Ra*j(4!n4!Bq5k+NKs|2ayK)WwUJow z48gb@_U%mSBZLToX5a}8EoLsZh2bB8 z?8MAF?Wna1YY%sC+$k4qGE`%4OSdUvArUzbV;SDy$2^2k{xZJ#FBj zeXbwNp$~NDUW@=q!FWT(-*{izW4=Ev;}}EWxyl2S!;HbER>;L3r8KrK0DEOQB|pU0 z=kI}!`}qdzF=%qdk81}4K8(Y4n$)6E=U6vMpa z_Kv_kv;-wiCcq1!NR3L&)%bWVmc<8lFK(E$(iHJFQ^5t-Lz<3_wZFKY37u&%MG&CW z3p7EPoH$5zP?Y^yrDSF-BBNX(zhftd@kajS)EYI?K5(-$ELJ(OnlqD{SSTpHKyrf- zEq0E6R0VVLD|2y}=EGTs#+s5U06IZY##Mf-Iw3O|ydqLCrYj=1$#Yf?w@e@icnRda zV0{s$v=e`!M6_(xe$7eZKAkuk>w31kc;URwOmP*dec2kBlr*_E@O(iRi-jBiioB-iobF{4)ORaA;#H>Om zkKKPij)Ym&!nLnT86}I;JuG-k5u_ow7&E@ZWj1FW56N-V`8UToog&Jv-vY6~Vi?~R zMuZ+#<%1?bcWp$q_U!JKPSbf|Nsp2a#F>^SL=Q=t>^g-L_*ZT*QpI^-I4zemeDXM% zo)MnSq+OFMtFykRB?=EU^Z6}`jw#FLB}#|*Q0r+B&?7i*+9IZ08_3@Uh&al55iFGl zsfU%_40yE6zt0-x33$p3EEm4RMJ?_f;LV5S&2!{L54IdM;{&U2c)}~LwDD~Y`9$|h z(f6XI5LSKYwNhkc%rkcf*(_W04Ic3&0Ew>EwGry2RP!Pqo9K;6 z()!KKl%~(hCGs_iz?;RI1b@a#m=Vm;vhu8uqjzy8bB-alD{j)YK+Jr_>I^=`1uGKm z9^%Azn?v@e1bx+<{NnW@sUr@>j~=rDuD-08lBm|$FzQqlmPwu%sOool7mbIk5n%o6v}lG_$#rRG_ussxoE28Y)UuelVi10C}va8m1Q{8`21ZxSTP~LKXcLCl95?{EOB&< zxRhiYCP+Y+IOr}Mm2RWILp2+}iMGJfpkaXXY~-BTUdPTNQ7vnkc&3a9xaeTVhaZ+} z@lZ_OB4`>%w#?XIL>s*SYz}$e|1@bjwFk4b<~>_o;5pR3&)N5I z&W!K+@(v<`$}eG$$b)kXA|KWRp@c+YN`PfR&3mBO`&RMu9*s?53o=e_&9^rjtW+<< z?{A7;5+vv`;{Ftm>SF#dB-wUeZf8#dPlKE-_C51%uswR?S_k8|x=h!p48HEL5;b=w zYjEbAHF&POHwZ^IhrcQx1hQq9Z@msG9(I$83t^a=rp8-dYXtHRxpiw$9ILz*yz<7U zTTq0vycRgsAFR7X`H))*vEtr||3v)h=-fM{(&Sx^inR`zvaiC*0roiWG`L@Nf4r;J z8ONO~+1$G_(6_$MQq#A-%+k}Z`s~0)x$3JKHaxmA(f_gPB?Ncf(*!T6qY~<>Q$LVf z=t>>_)ubAqzo9KT+zS}4!WErvxeqnhCPLO->2DPResEg%oJ%CGMd6FptF6;(C!^w~ zI<+hpxb*EhQ1=`tEY`i8+>2QHP-pNbn{&b}>ccc%`=b==!7Bub58gq7A;C_}2~idy z4DJu?Yr2m48POqTr1Yr5*I=D3cL0SIOR1OtHYX(_x)G_J`?mlh?< zEPx76aDv_j@SI*pa0Gn;p)`Abp|)TAfrNYDfqMF&1MqvIfdYHvfi*r!Fs*-NNzyN} ztZ>V;TkVy4($(l#8{MNWMtY_AhF_kHM(LFgdAYXq*~A!36r(hj;WU;r=r8FppM;ZE zx?QR-#aL+SOKfy#lBVTg4!oA_TQY{7(>-b<)|)gJX3`zSCYB$PE9OX2u?TF!+fKus zZ8vN>qpoy#L?YpQJP^cTdn9?UInLo z%^#fj1Sv{VX)vB_cZ0XY9IB}6urOR_c?&C4*gH1(i8C2Xi>L=D#e>Nkoh@nIOYsLZ z<=}-Q2a;_F>A9($a9N;BuaEnk#){oVFS7+;5r>w=lUo#LHZ_Lz z2#o7uzj_oE{Izm!z3M}ch{&T7+Ss%TZoS;a4;pRmT=E7cMI$8Ds`ti1wPt(rSBkNt zYm-YcKkG%Uv__w_2A{O7tQS1Xay=bGs|l9O)-EMmu0XT8$}hhq3_7kww=x6{fL#@L z$3`6f9QN8>>DuqowFaeH^O9<_l5K;@_>9DZ(c+ZrH;cbn%bz=VtLxakSyv+rz1mkJ zw$>pshqad6+c({F1l@C)Diew_SJx}UzvM>hI&2o|Kd^=Sqzk;?UTf>H+_MlzLaqpN zLvJgNMKgk>K|L*c$|+*4qkJFS!o4$$_ihda_CB7B2(luWQ{JD;%!tadkY-t7Ze>u@ z?U6Vr_3OsJSKNcS4xW0aUw3NTQ2*I-3y+EJ(6*u7CAlm|N8OAlaX5!5iH9>(Qq9s- zeQv5;%~HH#hNHk3O?%I@XUls)QO?hL-$cB(Q0l(`^gqN!ZAk(zF0aiR=i?G<36*KD z7q}za6ws4c3FTiV(XQ!U`GVhzg>RF3J-9Js_pWmeD|kOZ3q3ZQ6^<^hEWD~R-6+j5 zm!>&6l)aP~^Ve={&`#>-sxdx1H0nY%`L_!~{RnSWM7WUL7wXj_KIx<6TurLhYY+{A zxlNgqJH}r6dnO|fH;Qjm^37+*|BJG746-fSvh>Z&o3?G+wr$(CZQHhO+qP}nH=Vch zRlV-6S6yAv9kEaBI6rr+8EZ|f@eR6c<&;Psnrpl!R0Xd;N2Jvh;1qGH(SN#ny$289M5v!)DKX8_NU`N~P@|l2^!}>y$bteBQx8&&n`Mt4e=7 zw2f;8DLlLL8&)%aUd9mHatg7zQ-2_ty=*gq&#;#>Gop1%t#GQ;JhMzHU$HIcUF!0E zxd)e2brp5BI^7^om<%Pet+B*lo!_ z?l(J~S70$P61|d%nym6-25Yg4j+PBnFi=Z4E-en2#qTNPnif|Q3KD@0tm}6(Ae0qD zA&$FGj3%wmJVMIKUogiVI1DD^NjekoOR+ARqFP$Jp0zTz0jE6f`6F9w;VWS;sj9^# zanoJfcD%zDf4sT7Y!X?9n&eBWO*Vw3Fby}_9HxUDrX`asSIAbis1gg4#Ylq5^~r=pmrFR&Y_nAH)D@L=pVSLjY#-H{ zBbqtC4nxk*2jdI>M24B zj9(y-e9$LR+XRhirH(EUvtk##S8%>l&u|61inKhkJiT|GX0WNcHV7ywQrc5mUi6Hw z_oeDSgsaU9(hNHLt%#K78&?u7>y}oHf>w>J>n7t+r@a2r95=b=cccuhnN!I@D9@I5h^|9g_+FH_7lsS*t3*9 zmWl4e4aDamE7TW)ld=M4)_TeBdI^NzgdmZ+!a1+fS)?&xfc8pKsG~ygVDzYRQ=pMx z0m@Xrq?P=&m;3~&fK%I$whkP|;3)e^RRk&PK{O&Y%m`ToH0^(FguC|9e*eP8gX9p< zJ^L+~&lu%L69=lDk1!Ji+=LK(Q-|fjN1X+nnMZ0B$WsT~uuojzhCLIKiwF7*{o=+zPGEXUHQi(b5KYrnzl8MU`*B@CvPd`Lv%g3+#|px?OlRV^y8;pm7U; zUAr}smVV|h)eVz@<(Brk1(X2g7B($IO@P()nxIST);^0(Zo9&3p0@E89Gezyh}Mmk zLF*Q7yH0D~EqzbSn+EUSuA4l8o>smg8k?+kx!0a;yDn%e>)(*DW{P|G*IsJ1+F-Dj zsQn<;_*P2=b>f;3&Kt#b>YV^M^EUpYO`Up?F1TDZ+>o+np8Mk0zqpFK0c9<12UV^S z*Gj&?Hk%9OYBlvkZ9=m>DA$T(gG`^f)~aRwPM^-}M024%pCk9&xxqf3es|^327bM7 z-xYl0Va?$Nr#@NR$@v24%Go&7$KJL0pzX~22JW7V?sJ<5 zz8F)!^11lK8oR!V!T8G_vArtI_{|#q?unz^qjf;`B4_PWb>Q`)hV9=y`nZe!MEIup z3%+V4a1X*3(+yEZ;~6}YeaN8WBt=NIp$X&>#Mw>w-<^iK=ee&6fJ zJ=PCmyCEN7tY%%%(W_R%wN-)**1X{!^y)vPb0)hX9p0!CAF*bwe<%Lh0%F8SIk7j) zU8DKojKV!}i0FqHa=c>=uT9XyYH|*wovQ^|V@cL!A3|8&szqaCu^N(&Vq9IRg=%AE z)>j%r=A5rZY-7#pr!0nWXTfkyn(NV)qIz;}>a{G!er0LXjjjWIa5~!gIikTh#SHP! z%(E@A!-uj*(ygjT3}z>@4WJJ@LUW#=^I(}a4SNm7o}y>l1;j@Ki}+@l9UT}MV#&~x z94HxL$S|JlI~D$(Wu2&-D;pv(1lQ54>Qf(CRFO4I$=Wqhy)V%UsoG9? z@8Jri+L?L?eKUD4^1|$v_<_>3-VI`FUpuV1{jzU;Gws&-Vc#|QMO0&dJH)x|d1v#& z(>3}N>*^bMxz&3BgyRqEvGp1^8hXi{?p9k0Wb@=SES;mH zc6uI~$^CV2Kda|8zzCK5aJ0XeBi^>G7`DmfXx~|ilzX3K*j)Q-T&YKqKr*$(dN zc{6m%)!erFhKAD%<bcF1#&wfCQxC^=ue>+BHTYceB>&#&gs6SliSoVU3GjWg8J_L#yobEy z`Al@>{T}v&>wW5p)ph6XukFq~)a}+iWZlWVyWPpX--M#{NzLDfMW^{>^;H?2F#uZ* zax7!`OsyCOm`|}7gwKy8Ez`g%-pC7&Hb4XWr60I)R1s8}SjjJ zPt(v}a-xHpe3Er6=&_gW3Ugw>DfOAfwLnu8UnAmLEaD;8#3I_zIY#%Aj49+wr{>|_h-NN9OL%~9C-2-i zuB^1qwTGPHjXIvnpFDFyg$&YEpizpBc%GE*MeveJAZqW@zKf8U4?S+`rV`FSmvX|D z1Aky)ChBN&YqgMoyAi3fV^0uiVJN!)j1oqpgndmg$f@pxBx-&{$m^oeT=dxSB2RrK zKzQy0|3~rox4tWG(CI!@Lf<~m@6IHctek$jYzvCHobczD>YFUsG5ysfR((#J#hSEcJ=9glEs6t^`jnjcW$M zN**N8pb6`D&s^MaxJlzgas0mA7R`8fgl}CnkN^HB%=#enB}dVZR4?bpN8|tfe$YRm zkzP3592W5X9XS7X_5mbrY9M-+k2A0N9?T zAy8jG-U&AoVz7=N8wS&v9QIV(9(G1vU+*7Y z*|w^5QWkZQN}27ZSZVX}zm$e!oXuJwbZDUhcIpk%tH(y-s@UsOx$QEY$+qbW#|(cr zI9n|f9=}?QS=+cCq+&@~BcMDaIh=14mQ*hO)@3}#M;>&aN1Vg{Fx=AGD1ilIT?goV zeJMi5;ulk#ee}wuM*`z$Dc{3N8_G6gB%iwXcgh#(=)#b(fq^R381Il~ovqN^{8K?9 zCK|%2fi~CZ>ms>QZ!MIQ zPj`$G2c{^5!ZH8daKfT{+O-oe0pu8zP3ihW@NIf4C|Yd=-mvITyR*#1%1|Nwq+#=h z-G6?pz>$Q92!}f4k6YE8g(7%>;=#{4Je_LoGZQSD2a09ROs&7+gSPFT*A1eQ7xGv{ zt~J?+l|QkyJQ%S{cP+?dVbXZ%LjD{69agxLlJ3k}UKci+A8NiBhr}8g-;61gah?1R z^i}08y+A`OSKbf7!%6TQ>=3}fC&pe%YuFdx7Fl=PWzNyRA8T3zG=El&wE6`wT&pCo zM+@}*18dw4^lO6DL7c$De_xX_fAaESGECtubgr2`%7T8V*?l z_dh9%rBh=!qkk^*R>A%Mg!})bB&xc*A&w&djTUKGF#r$+9|06(gL;KS1H$)Xw}xU{ z3n2)kObn_j9!41JoUj14AB-nCk1rDUC|+tRR#j+)E)JICPfWlmYAP1b)Dh1tRP`ts z^}Wvhdf%xbdX4(+mCQ5UmG1tS`Lc66y}3O6m*<1a59O26?>P)K&(QT(pqO1^l@RBj zx~9}jq*@qz!@D^n^67h^iePLbTZP4ou6p+#zmxCCpppd?2xow!Jk4%#yuQ(fY=W=EClY za}6(`zV^xU_MDBT{-#hNi7x8a$m{5_jBSy%lrbOCa~x_(7}2vd9fSd}?L=P%CUGH# z?k44A%G2!L;EON`&^D2hyq>EfihWGf;f$XGIa9KiDGAmu33(0EJ zf)|Tuv>C$Ai-_s6b7U7%_5TB=nOBna7ZAsjPj999kWa~Mgznw|%B6_GNDt4zq&lq! ze>^ByqQ1_L8*hd)N^N3lZp3C=Ce0oh+ifHEkfJ95(Z@@9c-2l}nA|3(-yB(2R7Mzf zri?8Tk)p@nwt_A6RM{&sboXTWw(dzgOLaaJHS8iSr6sW>|G={?@8ARK8OUa4Ka_&z zfTMqT`h>QveC6z9y5*qF>&v=acsj%dw3JAWzU;c7uGZ46kPKDQ z(_&c6nWLgMxg7GVI{n}})EG;e3+eHt+-7MQDi;@yiUJZK-JvT|_vD1$m`-24PHMH! z4k_HO+!Z9JzkTP*t3Gpl(>=BX*ML_Gne;Mg>y}zakq(PyLtbJHkb>Ksshm|9S zk{&9Zib0$U3~kE&k`B5ij=LP^=%~B34@c$4^%_2ChOq_BGq-a2PU$Ay175$iqPxM< z=`Wg}jhoZiiPOoK?1U*J7$-|z@5!r4v{MM^6K|)6n$l-XtvI{N4aMxMUoG-Ln1ODCAr)#Kh|L<5LmTq8aG0w)?59>p2a z>%K81?_ zshrh=Kwmh`KzvZi8eA%aS`jJO*=rJ8Le0WqkIWnmnAW4~(oo7Bzi3qRcH!UVuf-0; zsdAjjIcyW9a4hKWU`&&Yl=M#YA}n)47PZGadTyV4MC;fQRvwftor@DB6l7+XbAFU1 z#3JfM`JL&g=7!`yLc-8|%(mH4W{VUl=3HOMvB=FFN{qUfSTg{j%O+&C#h|gp8et0N zeh!9HdZQBa&OmxmbIdq^ZwoUSi-HdEH~_J+&Jpn)*J_bv`7F`W5doxt7_RcWl2G2) zPMN5}POp-JO-I=$#x{9Czp|o;PP7s*_zbx(l4KfBQzaHpXpot`3cAK2N*;^ZmcKeK z*8t*}?nvwPm@6vrIgJccjv7;*GLud-WS;2Mut&>}xB-3gSfFVFrD{5P2L89xj}I%u{FGIiwb3&re578QfHm;CP_1hyfwnxA(M^Db6C)!(@p9I zPIipPUPxq^ew{WaiF3HDOE4?0b4cAim5r47$3}BVl7U7izQaxX2E}$F$W8JF-gdG( zXV_t^Q3@jmnc-TUlzP@A{r*XEvr*E7J7ep1YkgCmoQz>D!nKbyW}bRG1Z;aH+5oP5 zjlZ==)9mT{TgGje{XG(MSVqhqCv=OwXEFFgX5qRm(DIkKs{tzgFQ5MD)i|vD1fG_5 zsp^@>(l&90QE`PyafawbBMwq=9v}gIiALwF8|$Q-%E1JYLYr?+#Kj0JLlkA)F;DVh zShz#bc>-Ada;_-m2OfhbAFE_o4jtBx6eP;!mVjfT_LPtM_3-eb+LJw4Jdy6z@>p${ zkk|@h?-hzCS)EZA4o{fpuO(l0-oI4Qxq%P=$tTr+YqNUq2XUZ@`s)|vuV24x9ZYF0 z9BrNLXzlbJ9E}}l#hk3IlpKtW1)R;TjEo%=o%EfI|DFAutP1Xhw2c0@_iAS5$|yk! z1dJ$X@mHphqdgc29=@LqCf~YW%u5}5Lh!WJ+V&na1sKkrd~@|vNlQy|OI@M*g^G~o ztcGQCDfdnhy>GvEU%YRRNUBh2XqORRm%zKCr*}XQ+b5idCdk7CZUvn0{bVp$C1kq^^ zMENazu!dys6wzt-O=s3-v4eZBe!Br>I zuE%?k?>WIaji#QDb|YT{gR&VgxZYw3zKQlR(4})eP2qjx?W&_#WWOuJdd1shMhLiw z4{i}$#?=m_yGah5!8?tqo{xOPUQ2^6jh#FmdWAkU2XN3Ux!!mQzA5!S3B<>=i6K#?n)vWJN^d@+eY*P=p84nl(MEEn_HkT+IFOKkMl8%d= zj6r>Xnn6mI205KzUj?0BmIhJ1J!sPi%FQ8oObNVoa_wY1840T-+$vyGTnprEtNE1Bh>|yZXj+{^%MK zE4qaHEGBG7HH{%1wxk-A@sjLNC`c7!!qFbtq9RdH^>etS_O!N1R#(D+%1~6M{DQ*0 zitA9sX=JMcjAmuxh+jHGxWbafuPJ3hQM=h?Sm_^hxsh>HvOa) zCsWvoaGA8S)s49Y{Q6d?@xm&_HP9THc zvm40e&H+*bJO=_fbAjUuxH3ah5i9)^Prb#2=S`2j`G)tM%q(j4075~{vUBrM14^m6 za|TNYU}HGS;-aBYcH3%mOH&ktwPEm^VK8tE9lcWSEqx+KfqjmjIZxm$#VZ?6{+`S!wMXhe#hOtFJFvQn zou9OQYGjp$KC@NDfW^8~{IuMf6c*|_Rq(V4DS1`QH2k_&!^lZERZ*SCN?x6aOT(Cq zyc(h^ zIaG=@_21s{0hSSYxfOdG1ZRR z7tq6ZI6go{sdN!w`Rv2TNaXXajad?z@Z8c!pnA3|^6THzW2Hy=+T6 zgKEl!m4LdxT&T4s0YlJ9-IC?qQKLzz95CDb03n2&yY5 zY||np)ha4?#W3V69=AM#AOaN(uhi_MdTEkQpHHmygM?R1a>4|eWpmSY&Q~xmIKN6Y zC~P#m&wW;LDWm}9HCDxGEq%$fWPvA_%kryx<&9a_c zvf8ZBJ8dH;rj#Bl+SvJ+>8x4PT-xfB3L9Opq-U%?+Q9dwI@ASZs$)Hz*W8?E!BUr& zqh#}4-8szF%F|xtozrH=jLw=i5{gHx*QFV91`Bp*@WY^XUE)TJSukIR)* zW{PM>ub-rKy1O>qX90s$?YlA#VPZJhbq4P>Vpi?9hV5BLFWGPQ+kP6iShhz$1#Bk! z&0$;aAvKomu^Z;-CU(#j60e`e;SvW?>U<1O80I zBx0keQo*y20UB5;1cpery}c%mv7dl)N4RM+E7)$90W$Kzo0y>l+c8NIlb{Q`Hd~uo z?9q@x>5J=K&@LLxMdjUp1>DZvAh_IS(tg0c!9=U&N`3^{p%vKuMh=L!*!P)&!|Bt~ zN5fCZ3@S8($8qAe#Is}!^QGkx)hr)k^1{)@<3>&k{xBPi*|la1qV4ce7$5)Y3SyhV zvBVq~yv`c=pvMmkA50U{QDHWrb4ODYnJt6>ba#RR*w-%`u$Mx@=~vp9Uk#*?g=MJQ zeqBEB;o?&cKR9mQ1Pch7%$lAqUkk1-jOXjS1>RC zp3LhF`_Tn0Z+Qm>=qRR95PbCElAK&+c93o6dFjD)CKtf-Wszhb9_uNM7`1a0_>Tk( za|g?Es&I9*IlI(qGx|4`$wpf8pp`bK_*lWrNEX(~#YPt&3O?pDJJXp)VcPIGi_Cbw z_-aNmm4;Q*$y|x$qw)cDT|Hf>aED8U#LzGc$!VR?s*Gazx=kTrXmLl26tszbNAxkf z)5Qja@=U?x$OM+k`G!R^QzWBRR=S|t>P{(zde^RqD~zR+b+tyyFbAgTv0Y0|wd!PJ z*xK2+eqfEY5@iG1=tw*JS%*tbg^7JnG^9hD+$xO%Wntn}7zW z83MA|ywE&?Ku7Q;C>p%7sXYWh>OdVpkEeHV5)(Mmw4{DKeq|7Ijs(A3vJk5}Lc;Mn z^yQ@#MUE+cXcavingPI$JC1v@l>=t2y~-gJU2K}i>tUQ-@3zGkD(z@Mm=8|-sa~L2 zIhQUnr9EYR6dlwQhTghl^8p$b#kN3Z@RvH6r25pLkF)sanq|>@X4Kx8N$_0K%v6q5 z{Nn4>dn_15g%!5&AElmCOe6cu@mgPfxFP1KFjlIa)l+=^B&Re}vHR|M0Nz1Q_T4fo9A6QMKd$SBawBKk`Lk2M1e+e@_pi z6ESSAA6W`b3OTMGWzi~nri#YX0HYWJ5&A0PCbJXqs5LHT2K%nb76^* zuDF*(nlRb~V-xDW{*CR5+uKny9&wcoE6J21Hju@Vw&9I_#r7w^q{AC)MN zrFzGtw$nG8Jr6>RRQc3Gh^}>>z?NHSq1fBD2eG%^Y+Ffk?8!Q(tp0=;E(mk2)pOZx zBQ?$6+&OK7Jg6Kt`z(T2KFT7lPD=9nWPc3>pn``N%V{`$tv)YUo;k9nxexz_V7S9y z-KWLAL!m~koCnLz$$+ z!ZBishWRQ1QyN8z#VwDW#1W4ZCvZwqE~}%f)Pt0X8KgxJk%hPhv7;JbO{8!T2b(kT z67nMrYYH8YqliiUQBxvwRN)X!J}E<3L6#6Xl_vq4hEImGh)-1)e?&aMJxNQ_&{AD^ zr@~J%S?e1PJ6QV&KF~0-cb4%HS$D8Kq)Y%E6%&spUl^W3QeTH^CvO@~X^|` znXiNS=PG0P$szpNNT`F|2~Ew*OkKp;#fRP;yY5FIft~aAVDCi3Z`mV@BhCQXPruxF zX515Rd3(Ho>|k4h0W%=!>W$|cw1;9gTqE0t77*dhG2I(gV!oB5MFKE7^6XY)5EwYNHlcc zX5Xc5i6he6cPKnC2CsjlikapTRrw@I@VXH#>8bd*wpAigmhHI!8z)ix>@*?Mf3qav zQN%B4NAu@5QFkdzhB2Bl_ zMY}R427pvq+)n_zk@z56rpF0(UK}Tn7%Gp~~<83&K)|jN7f4!b$v4Ya|-{01NaG7^n!L zXgV|3)qxFfNJ!kLG%1wb$0BLn>6GD_RjRm7Q;$@M-a1^@J_3}*<(*!#ZX9P^XIy1$ z|GvH0&H`lhwemOV@%V{op$>BL!`y=0fwua1%I3i?XJzrhTY9PpSnb*; z?PQTsB#V1Z5n!{WAE{OF+*(C7Du_FWdbU9GLD?ox2@*)3ZuDspruEZ%S+a+#{>l1AG@fXg^EOD3zB;4`|T=od2 zahL|MW^q_Zm{}V{fr~nx7sGsbB^9fMxlIAG!y@VIQzO#Mky#EteL&5-DcBUZh+P-u zmbj0&Ie8oVD?M#>izzcva9kyhcu6j)@PXEooyrIW7u8%ZUT^PS>G=ZV>)P=?pJw1Q^BAFXv;Ji(Ut*)tt3$L-YLDN`x@ybnV`=0n#<>= zyIXP>@>lH6T!UCh-@KmH&4cUb=GUBDh3(SM=x>o{Ql{otj4x78plxb@X(p}iQ&pVL zN7YontCYA{IKVi4r`EFzmd3gnE?c-0>?r%3A#L;t=o^@XK6x#?A(}t8!Z#mkKGtrL zMHN-6DqYjQ`nL?}lObN~;}xk0^Jp|1?R~o&{O`4 zluh{o46pkVp4YtIA7B2Z@t*yY>p0t)P0RClj!>v6 zBa+;M)w?*H;Ym5Tmi#ky01-l0_0=}mwx}$*r@Ya&_-ylux8pNrrl-<#*RteqF=^t{ z7ZM7u+-v^Lr0=!fusz zsdwrFn?bq+p9=ep!L#r;C_JjKT>Fo~v~WFgZk>0+1Drvw5Vr_B)Lo*l2K%Ytx&&K< zU8=9g10O*zkh&CGlwRo^p6vt35Vdk{#0mqdBySWXoOYlVQi?7(cA;A{9($-9uI8yG3eS6^ajm{{uw5F%(}!k?GR;@a__Cc{@x0`_X3()r=+paK z=q}bDX+YG7(LudpPH)7#Ya>jB)&n<9q2s_Kpt#X*9RWXE&` z;4YQR#ApyY;DS@2KgjE6iyhA0)g*``m5NsLjzFH=64<$UrtLDj!~sxbo+7)(0qS2a z=QT{-x!>&L?M$cbw9_~)ZMomf(zp39B<^_AfmelK80_UPc6V&si6=l$<4j+n1Ept+ z<~(D-CFO;#p8{|po-vy&Jl0@lbYv(_cUwGA?GCsNv|q|fxDDVAmTwih{X!DQ`{rFJ zSS-d`PfZq9bitKau%CRchaziWK^AhV%tbS_seC5m*aPsbCzjs`fk4Va#fWLELd5Tr z#)05SK_Rlvcl%_Fb!>>L@mrt9WoL6ibM;v_nz2L8Zl8YhjTKTHAJU z>7eO?#doTMXx2BtnM`&mpVHH)dzBQub1=>r;mG{*)cRM&1}(F&=HX!lz7PE0YYl~r zdbGj>njS7JsLCjyD9+yS@8nbjF1&e0esAo@RYpDgYv|sBiqS03cTxa9jrDbsh%NTc zRkLZZpb9vitZ*TUh<8Ih5%h`A?1KiV%;3%?A#Vq4yhLOy=4q@PX?lN2@a<@kbe^Cr z&jzxynLxKIe?`|aZP?J1R`#uSk!*7n!J;lRw?+(YE_=Y#k5pLnKpB&>Z)s9rD%%Uqc_m3kR$UpvjcVly25^fF;tXY1}fB{SbpGBBd||uO3B4W@Ar0u zHaw}YG^nicX-#PMfS<=*As{m>VL|ZTiOYRic+&4+mTo2Y654kOo}Dw5ZTIyoxMBu9 z&OR2@Y2GBNcYZ2?^$3QrdFOG1-c5c{U|-W-C1SBk-zc;zjXXI39<=^Ug5^HLnS0mS z5bKezw4MpgJ=uV{tq`I~pdg7@vc#?hP$7FJBl4jgN zi)Ix*1TDN5oOg3%+A$bW_aL5n-8bu^#^6PWZq2mEAev*=e=AhO?xzT`SN`L3Q00v~ zj6?;jiJ#MaQy3(mz~68^T7J$W;XR=CjYcO)u1C4fl$hzjP8ZcRGaz8ifRi z$X5T5%TY72=%r<9=(o80fm_=?(0RcpJEATghTNr?xTzSOv83`tOnj$BG~{u3j61AI zC9c3klo89u>sH0Bp2n}mBz6h=%NdvM5Df?H*LHPRrVq8DP*iX#gh5D1NJ}^PK?0U8 zI4UEz_#p!JE;0Rh4w=R1Zu;VGkZ*(z69jMiKpS=-YzKG`L(&dI)UKTv#Sb`DoO>m> z`O7&CrU_tuB0?c;$=JEVFh>yN!p`Y;kZ6LxI!pSoM$xE@9So1AuoaJk$j-}mG0gg- zIdzD+b%|+piCJ}og`AC|hq@6Mgjxpu*jd4yi~f{#(8{j#7c`+ypk#im_@RZ`A7V(2 zevzYRLFMItz3jC=6rnAsifIGH+Hi{~sv$)>nVRy}G^sXreGXs}w694yaD?xN^Xh?d zvA)ix=nc(A=WAx&0_>tn8igO%a10&&#iKjE+26g)80P`P+2X%eLeHaiKFm+Jnb;eT z6PGqD*&97PtW8~HZMV`F7c=cn{ydRnyK{FROx>PO6J-JS6*a<<%@F7%Hln3Dr2NXv zX7o2VCAUjxgkoDa2a>T*0b-OvVOur_<&Xij%V-2-+g1n9+IIrf%u?=S%v$bCpTW?p zY(!z3R0qwvt^%F0XZdx?f$9I4CEfQnW3mTm`U8z~S0ho2t0o1za zLTa00$6Pzf0@Av10&1LT?{hij(hGIuu?uztuq$@NuUlM)(K^=pt#y6%tH$01aFwIe z?=owTE!=;w#O2>H38M>2w&}OG@({$|_s`D#k8({DzPk)v53Wx3rhv^Chs#AE- zX1q?5-xu^9`buu+yNE4OeXg ze^+>K^!EEU&=vG;))geZX4U&b@z`-%NL+%*1YZ=lc8jBjg`V`_q|T>3!$n zeAlnr5q>&{Bi|u{vu{! z4B^hEn#CHl?#rwFyE9bxS&&dJh<^<}1bRioq z?t*I%>U;sZ_tYh^6S@;iY}6WyVJH@B6+8z0#KCv*jlw@tN1KjFGKP|7!&Oy4bB3zb>;w#aK4T&qSd8XPJrqn`O4Owl=o;=Rm+)arb8+@Pj4I zWHy)5u5P?^%G}P%5NTX{J1iC)n$VN`)4*_dZ_~Xr#RM)+@wyuZ(es*jz#(@Bdl!o^ zJ3T<Kd14`Q(Hz8X-&qEy zXKqqbqqw0b1In=rDc+&?9mJ0P5H#ft*YbV!y7B;mcSe)gthptTMUODK^rPtL=cAi? z(bBz&77Zo1cod9{)1YNL*a3*uddv2gc$kPrU$UXY0O!@GbMe*{(vBUi&*N)E6%ssU zgBi(3d>})KUxd|x^d+Rs`&!Ny(<#!XNvlrhA?KG2V9e#;g&moXU$Z~n@t9~Lr-V|f zAFoSeEQZphw?luVzyfk^g0>QO1X?u@xn|yExr2*^q7u(D+awyj_{(Wprkj;_Ko5kb zGS#)diB0arNSd+ny%|SBCHr>iZgt9z?T%$`6CRp>2MX1~PI*=}^7lr7u-1_2+&~%c zv_b-VoN__30YQP#Hnzvyiiyev!sJV6|Cl_2RIbW4jg$YJau}jbq6H-Q%5!QTd)c>- zfXBce?yw4H^(+|{TX>{fxH8Io#gz=POn0VTg8bXOLTB_-~_4?##@@rQ-ZlCVXo!YjZ?YivwFH)Re@)Z6W0Pe_HK9U>+9@{jwY+UKE8j!^I}TX%hu%V zI3v+m7}NSwf+~VCep3fpZ^f@@qc;@tsoKTj>!SnB$XVrJ;6iOPRrTyVVCg^Wz7Jg4 zm-feDzGEvo!Fp~z42AE(v~M}C9tPU4PK94PydVAQ0;S@hN)VPDw*%z z$QQSl)V;>@p4pXJJ4roEv0m5c<6y?beaxlb?LbZxb~SYFyv1Q%u84@`^(6DW!~i-+E^h?!K&a*Uts-s|wKC(|sBZ{1PsknL7euIQDbkbTlkRhzAaAM44+$d25^;@C2)_Ll zK!2DAfXJcq({JXmR(cNUN}Whr{I$s+JeAhXLcK{=f^SP3Chhr0cN;3$oI6jzR-srl zE026mXl!CU|FXvJbPo3+NmUwJTu+6 z9=;RCd4n;;lzh0Iyd_B`s#t`S`f$K6O5!VnV3)j;0EQ!K;OQzoW-I+v759{t9)f%5 zb=ktM@;+b3s9c=;$@ovB-@e>G5(Y~oUgZAWh$B@y%A-qsUU9GEL*AondItug(K?hp zj%cYEYMd?Y_=MGC?30Bdh5J3>{r_}e;fP9eF#km0KiuO9|L;4Qu#M6G>Nt8UUfLl1 z7_`m={1D1~UL@>mTNFc|`KA(Vf;i4QUanXkiD~D;L$k{?FYO2awi|~Rff-+}a zVMt6!!gn1p&`C^b1PTiE9h0zrh4;awm85p4nj1Hw!YY=ej8y7t|hm?lAh*Y56JXKkkMmn`8&FQVAEI!Fq z*FfeKaNtE+S1az$KJTAYF`oSQAy6{qUw4F&kn2DW)jZ+2V?Kx zU3s`HfzHIXZQIVowmGqriEZrIwr$(kv29FjOq|KfxqA1U_1;})t^F75Ro!28{i?f+ zuO^r*Wqn^giY|=OWb*+~?Eu&!71d3?@un6W_VO=#Wwh^K7CS-xzyH$r!f*5#xW8x; z))(@K{twhCi2tX^?V=>7@I{b(WMGS3&_%^Wk&O6=Afe5pGLWQX!w78J7~AHIY{ybE zo*UViDG0jCF#VDoT5t~fuMAy}rZTT%XO1zy>9OIN)Y}vWqvKMj(^qLxvHpJS?#m=( z!m8dv4q8KGBoi?_0l#DG!In9Us8^7*#F7sdL>XUrM6{T$?Fti)oq>RRCxa?Fdn1V; z3urbhW8ht&iZahmSBzK1=j=-9M~L@=oA$t}499cliA{8WEZJAL|6OO8Ki;o&P*ymi zC^s{t_;<(BF!DRxA9ZcQtsH{3^nJM^FV4cN3&B@c?UtYN3bo{W>hmy#QkbK{f@kp% zEhjUHu3@r)bS-PBns49JC9s~rObilr1BLFAg?B7?ukAu(X7OH5JiXf?Z5Fe0OtWJkdqjH3ENLP3Gt=&<{` zXh-DeB>-!xiZ%v<$giM>wy`+f7JS|^rXi<$9k_6Iw;3Zzjjw9Az`xMckG^I*87}MS z?5ZE;*V!Bdirx%8VlPhldkICaU_VI27<*X5U%g>yl~mSfa6b98F0o~9-qX)iwJXq@ zKekGw8^;pgVydYZZNu0qIWt zy;h?mniDg+U^Hg3*2HC)1~kO|YBe^5LhQ=;G=)LmdEoR;e#TwwR<$7{oDh95OA}M; z3F4j06T{&Se#KBY1WY?fTSAd*dnB7F4O9n96PBV}RmFV#uPEbxFeA8KV^f4LmSBJOZHh@<^(32#v`plMwGtx?t|@nG(ek;TRg3r6Xuy~5f^wzI+)dJ`_A$HYk`biXl4q71*pG9b)J4UphRzXk1f1=o~Si2P4 zX@H^k;PE6AA57$q5$tTF9;m=YkHRaHxdsqq(eYb344DL3V8+8gE<^tc!9(~}e3d9S z7YhjR{6xOInmrc}@~@hX>3guiMgEFU=9LI*M)o`1(N;Oy5Bh46XSUrLnEAB7>kLdpt=&HX<~+ets>J-~`Zt-W8726?O#6XC9tyTgbZ z?swT#NuNQm>6F{ab%$BH4``q9IQeO`B615>`J7`HvlOh>DVt?0`BZp|+I*qd-TQS^ zE#U{rWUd1%VqMU4o$Kw~>PMhyUBcpb`i*ChBYOs@Pd)YqX>t(U;>F7m9lH?kv@sn+ zNKENgN;f1=g~H?#{`UB3z}ypa1^nh)1*viChtczOFhYG943&1DhV+r$7np<-fR~Et zg(ABKP<-74%R6rV>n2FRihaFGzGrq~Gt2hc$`f## z)#xN4|1+lXyo+>7q#FXrTZc@2*h>K8Jno+gO_X~WrQk6^6?(vl=jRHzc>mE*8f32! zR^i^7H(JIyo3kGPu`peZ@R8NsMQITBE zpr(2Lsd9KR@o{uF`H3dC3ln99ZOujJl2L+Vj4UY3IPke2{M&+9GjH=qVWJj^_YbG- zT{GPLA9u%t=HHyCg7bInz@C_Djbw+7z&_d-CqdysksSrlkYm|I!Ohr)z|7d;V7;q| z8SJG7!}=&`9KS+MRc`e!)8Y0K?&q6i?zDb3+Ad73xEC67U{OTG=*V4jWF6^y#&*Il z$W81HSxN(hSoy9z$@NX4&@XIX@{Y1Sx0?7Xa(4C@YTZjFkf>~`5*X{-b-mS@uDtpaxSK0+dKQ-z-w$OU~tP)Ndn6>izEu2#hHd?J@gqx>Xe;ql;?6~vrkQptyikti=1!v@3Aqa zW8@gjjtUgq{0`Jq3rq)CpoI9!3QVRdB4VhOHN z4|!zx3C;kr(}W_OM(x2J&C(PQUs%gY>8wF^X6JEri=v?R{N8`&db^73 z@jt%gsnf5!0eJtv6#GBvbx6vD$`{2O=}qdhT_6HSqmAGb1y`lnN4?SdF$ZIH4!fmm zRRXq=Bo$cr8E2;(cCZ@-3#^`Qv)>yCfyHFdZMz|~=i`L`Zfti+veQku!~K-%<;ePR z^ERdPjkK*cA03meX^BA#J~2&N(S|1gLD!^>%o(uPFiQ)^ov(py7BbDOi|U!Z2O*M^ zpZ5*s0egbw!X}+JW{+u8HI7P^5J!D)tfAjtMrd$xlMTEva`Q-Rr4GH$rdgSpPD5xR z=}%st49vbq>IMTmww+1@rg7p@fs7hj8T;vk+NHADSa}Vdc}n{wmg|U4g`lQ=mb83Y z%4tqM320DLM@F?k-eO9YC3YiiGWa0FO$dwFy5kZFL&BDa)~Ovn_{}w61e$9Ad`oi~@8W#m0!QE}Q*&01x~p`yUdFug(v73jJ5F-E(u~ zx?7)?0Wo?5%W7X?$QMm$f<%oyRO?5qocERXR_heOc9hFx z%AQo^F^11B@@+1RmFv7X`~Lm7KKg+L#iH~+ST&19yZ%EL$8sLuxkg)(S1MD=ErCMH zEk5FtPOqp_s&k18%Mx+l?_Z{@`9ev<;xB9OH^Tpmx>|nixBj<-3)WjzBmFaX(yOfx z35pEK!2MV#qeWF3v>2W^X%UG^DlcH(lhr8^PS$LH6PI9Vt7gk(X}w0Rl6_T~u(`~c zR-L7PzC*9l^>X=ay=HlbCoF0OHq8{SGNaA-TwC`&KL!74WQUJk}y+fuR^uu@QB#j<=)t&e~ z1(Rnxs9G^*#2wUu>(EL znM{Ax+ul_Hy^;IZx2)KIi6e3kp$xlZu)@2l>j-`^HiXKpA-dDUAuHcE-ntptb(?%NB zKQ0spgdn3Z6eur}>}lhwxmYGdw0JTcDVL+^O*Q}1aP9|=aHnf|@k~xPq zUL4iLC-syUa-o{M8vQ!^F{AnR@X(fzG>Ppwzq>bnWnDZisU+SyS;upL z#nH<)W2%V5vE61`Rnvj`4?#N5VHdBdHW|VV{sfiGMk>FNiL&k=7-&m#>_Tem*m$WD zmO+?ibh|iv*YOKF3N=Wy0%(ve4goeY$3+*y<=C@oOMcePb?ZaPCR@H_*IkLkj$#E= zaeALsQ&iz;^O$`mFma|HX%NnqtB~B2Jh0=T5!&G z>9O+NrQK_F{Gw?SV~z;+`+g@(BrXo^TjWfWn zF_9id&^$R}Kk*+)+&l&Yq$!+K#QI#xAiCZ4ZkNCjT4NG)73rOeGZ`))6RM0CQtadl z*~Eb*D*CqLHiWnnra&y4lszuX+=IL(R#wYseNVda-<|N$ha^-6sh}M-@%U%XDUm3t zZDC$0IP9`2gdD-TuCD1|+=9$Gr14848?L6V+J&b%`ZzV5yg4#hyvhl%NA7ms$vT?03r8oF%zm@F|2J%CYqv5G(V`2$M z#kq)8Vu_t%G)#0(Q0A>FMSQUqiu$XlH=TJJn<8IkII8|;LZq!#Bb<)#nYf(oaA8IV z^s&g9nOt+F?bl)!!iHLDiR-SC_zR8PR)MRdL+41@PIzz6-UoB|ErF z3P{UGnkbgISWmjJa$OSe$^Nmr&_}R3L&#cLB*wl{b+cr0kX89rsDAs+SHW=YkoPA=56350sz(JdiNKAa5^JZ0`Q3+g@IdqpS_{w4&RdcbRF&-hQ8I1aD&ZyY0 zy-(u<_06lNeaMxx(39&3Pn`IUW%V{Gw}MVe@lg@)a2`UVFd#0E+qDH95YTersHW|{ zaG%ujjH5ze#Ku^o`$j`h<0}1(UO{obL{#od_}rXTXcXI%OufKpkEq306Kr5J%B~#YIrH5Y4J# zH*z{}Tru@UZi~n)S<~f)$GA;bTeM$s@|QRV?GCi)lP>9v_gM!@pG7a#Gmix3WOX9)KIf0%VF-kDyQDO(OU0y#=b3Z z#byJ$Idw_RAs%8&Z;mHZ&tl@XrZ7X>wU}HVWvH`TNHSp8q7H8wH+iG;gn*7M?;*%x zZC=sUHmH3Rc*a3zS0`#Fy&Iy-&dtvnQbX?&GaWTNB2V>9s`Q#t)GbmiK6xv&5Zx4F z3M!j1c`IwAJ=ag%X+pKBC_WvuRqW9fl{==OhFMQK-^2{RfDzSV*n4ehumN2Tbv>6|s5HC7#dFEa(m7slgh zx{Sw37V{ap2o8K55-;CWpW2kqtKnbw!#jItTQ_4nuCre}eCdI$f@075PHtew`FlVB zH5etb)tv&#<|1FX5JD_|zWPBWtzBz37avmzcJo%5M#m0lJU(PN_6CwlYMV0K8bh z-1$wD1k6&=ysSEMow#sny$`$W_4fdsQYjNS4|7GhpYasd6+7v(3WoCFSXNFFUqto< z)wZWs*qDvbabWj+apC1y1)=}F#i86y__Se!cPDDxxgT*0V<~#!1JdEy@39yLa~z5H z_sB`}ne8j}{`KfssZ(ZAyY(*~hJvCf30&@obg0P~Y8koI2SX0Sl<^w%dY&iuk`mK^ zcmV_lv9PGqiQv5hX^IhTv=i#`o|*Kl{NhI2Ssu_9XczO*Hk?uzUZNB3@%)XyDB_Ko zFAQ;QeCJrI36Zd5hgzp4RClcBE6LMSQa=yIbVtQ3*>y^>~*I z(w^-G5#bNUk08XW7;+Be*VkjHU5jb0p)=$LKF0B$c!ffpOp~-U^8e!(yQ`FX=~AS` zAsQZ`9L7V8fB~)L?+z2ojpx0z3IF}95}{5F6{@Up>gy)4wo`Ls>k(=L<+Oi&~&JKtc@{)C4(ZSe_Iq(hhF<#X9iV zL66|q8rH75P925fA1o_tkfjwr6eamgg4R-80^nxJ8HZ!pwl{&!^fH(LS>pYCG(TpY zOP(nVXY-4#7U!|U{fPfBxV})c>=oL^LBNhXT-?0*o-AczTl;5UZSonDN3 z^MplkUB~o%fx@0x*O2V4`}gMYxjas|IZ!})ok+x)i<;)b|%ZPLJNi&7FW$Ed}3kt2>ITenw3p-|(k0nl6p8 z9Y~Blfo8004Qq#9tA0=`P=P+eA;by>Z_Piu#7mK}H*U)kYB-3Ma1@KBHx7Te`3k0Y zlQIU3&$zt@9#=bw{TyeX|44bBOl-cO2}~^^AC&>aS0sjj;Wxf!HKkN@s70_y!}nZM z{Dk$tZ?(9|3s|4=eu0IZH$L-e2;DxNYVMxFdYW;RR))=-#V zQBNV}niu8t>lV-tIZqzEHvwy-7adFLU9Ty|`4ii|EPLJoOIJ3?gi{Cig^s0aKFy#rhr7#8CxbVp8hBF9X^fjk7)+;vvuY8@{={a&ggRPlFt{`AZ_|_W zqs|p1N;{qiOec{HongS7L@_G&LjJy!Xw=~38ePj9`r)zp*ChN_Kkv3jT=6HPQTLGX zhbiAdL(ek3mSL)z<w$bbNelG9!=7JaURbFUt4JTUVQ{!c?ds*LL_>PT4~kpQWc-(c(n{dtcssb z_7f!5Y|g&yH9@~V;`DA~Beu0qyPFf_PnyJMyck-y|3uRG^*7?hK1;<*sJ8P}+=M

sw@tboVyL~OTc_SWsP80s-i@ncv^aZW0u>Mu^;rIWFOTu4@ zs_%GN&)S!wn*F7yQvUynOaB;g|KnuSe;fJ!=klt4%i$#oBh zzjwhPxO+>&*?M3d!!~&-O3~22E2jhAKbyoZTd%ynfx-f8jbK~IXuj)e>o3mlnW5;O zjxl8Q7n(^ci901}wH0C=)RM6Sy4hAX5$+t%W3c2jZo@1PH67VY1XG|k>B8t6NQN*P zQcl0k^XlGpk^Y9&mWOM-0rxXYr0IAVIxPYI+-Ff4PW0PJR{C3w`^P`%iGTH$E4 zm%GWB^;oPWy_MU$Rt3hGOp;u(!y_Lj+kVmh!9cdBONZo5@ll8@+5 z{;%Y5P)6M;F(?p_IbpS;zj^bl#-Jcs?i%JZ>8!^r_fwp27rmWN-=p$P3LvFWbN5+M zb4io?nC2;1u#|c(p`DFGr356j3deC&mgSQlLvXHGZi)kHv5dU*X5bs)GF4t!-yyFS z+caEY@}0ZSo*!S7 z_ra4(?nqE~WcNK)Jmw(1J-_K3u%S-TJ{kv{#%O~1(8lWL9L7@vXP9M6)S(Ie{1{sM z$ZLazbMl-c6k*MYMLw-esYF?9&UK^vp*&mwjAweikf=dgH|Gr`2<kzd--{@s`{R4t9O6*4m485%ladx!00=q&A z$20n$Fk-0Bxf(F!i4;0dq8J8$gc5;*1GX;1W?ydDeg|dek!h=XGnO$%6bPPXt2FT@ zx;TAgzV_v)(yeP+7$pR`d`eI4VL?HAah$3?_Gj@LY;+&LIb?CDYpTV6hkbtA*GLP z{&4mJ)gy%;Vo>=z)8Tv1Et5c$PQk3RZgtw9;wlM4rj@YJH<4Q8H?A8r_v0PKH9mP( zooB&cs<7E}v37kMAQsJ-Pnl?Epl5wG)zgSm%DcG7&@;lXy(aP=D8UsAYzw1&MpwAS z{reR+4ePhdU989vKfafC`u^vVZ_M_2cyVCn~_lOH=fpXj-b~=Ul;J9zsGG_cY6xK!*nj7Rm-n zaOiZ(>O&)0)CxbTp|dUzObQt6+ciY~{0e+Nnf}9z^1YF|S><4wg=P4q6J#MY6a&kp zthT8^t+9E1d&79a0N{p0kGTTlny*r!Lw^W8`A91D!9+p(RD4Xu8>fF_wL+=;T9$M9 z%>%f{aGNmSF%h(iIEG;>pY4|~v37)*)}bdXO96%TOy3c#?b5KCBxg0Bm>xvss9@1c zy71xRtst`k%UO{_SXKZB%`s=~wa>Z6+br@e$xM3U48v5kBit*=Y%?)*3&>WD(uus} z-28z@6=&l$#A4$lK9aGR0Qy;Z1OZy+XmG2P7=bw9n60J^0X^-LL5S1E#weNSae-2yN4h4$HloHL9 z_5mRtj7hNnC>)#*NkzvO*xndBw zFUs{2M|XgVOd*v^@<9lQ_1oTpkjjmyk+F?ie z*SqNGZQ=~yzp9x3->qVn|9)s~zY+kxs+dM2Mg#^5OccCoT{+MHIgn@`L$QfS7+mb5 zP8XAl&h58dWbg3MweND8)%(|Bo>;7%f?G%|^ICX=yoZF1t@P}4pQH4(kN2ko%5Ng> zC56a__?Y%eKypzl%P^XH4GB0Oo>%5PX9xTstO_xx!SO3cBvPxM0yd4MvkbsFjySf} z2>nI^DqMB^1IEtbEnX|vxi-J zXG((1xc_Do-&Za`V+;MJYK&J7ksQ}$8}2Rr%-HpiTiZD_XO2S->_)*-!f)l^Pyldr z2c2&sE)36Dz}20xc}&kvdXFK2SBq*_mc^L>6CkS#$Qhr*dLXB~&M+EL=H~cB^!{-x z4kou@Hhmk`%1z9fT@^qPWr@XUkBW+8hn*%m{{uz^qJ+6{uO)ytZOMM-2aXfOk;Biy zzyL^1wz}Y`fY3a)ije~d^_$T?<6<4Pj+}8_maIyHqCM!r6D8AHU7Fn88tEbC#a~XA zb})?eMXHW2WF6zPN&*5RqeU2Tiwd}~1Jfu3cO(IK;Z@=jM6ihg6$yO1sx=SfI#ZLK zHFd?Y)M8>=rG|{K0G`vLY|O=xO_eEpDKQnCoRle|3()FgUf$rq<$LeW-{Q`vS_~d6 zy*)J`t%L>q>8*~#X4bTgVy(?s=?R5#Kv5|Ak`k2kp@;8CTgeg9cF}yD6SgWQx^`YE zO?a7CmkyluCnvoqMjIpyIx!ZM&3cU%6&GsZ2t!vjtL6_3__nOvk_uX+i(k2ts2QhY z++vl6V2W~1b)3s%+d*VE7hlA@eJ_vE?w}= z!xUr={Mi<;Be@$JTT$tLM^HgFy;~K_7Vgwk8Rzn*gs~#|_~>^Hr@yKqKy#*N*nfD)4XdRo4G5qYV`uF|1GeqO9M}?WlGMFl26Kn< zmtF5c7DhBnekhTe&sv|ZybMf8fUu)_8z!==_(Napp<|f>x5;vYAhHl;s`Hml#Eq_* zTmU^hu*VEEDnmEq6At9gLbj`6{cQfv)VwGxEN@9F0Y#_s)zPT13;$3dS z_#5cY2HR5#*nxYpY*Ce%Oa&Vc`Sv6#=#@k}qouW!7BS^@%$_Ly9v->BHE3FZmsRSc z1qqiixxI)e#w~P5N?7Cx4|6?Xg`cZ>z}h>>ebzh#jGxl%Z6oCe8;AUiFA%A zR3cSIm8ssg_P8%jI8Qp+c{(f@DGN|3WgN{q8288(wn_5Q3K3APU0jGT)L6|q)IkUd z&w2c&%CCBsZK*X%UipcX-O)o)BZfgwj=xM>LKuuYgeo%Y_IQr02pF1>rl?dCaluU> z7(cK*qBzyM40*+|e8|t;?n{;I2=o_#jv0nrWmr{7z-9X3gsSzemsFT_#S!V4smK`a zzVD>zNtB%37MwvTkitj%w)2A`rrP+}Yvw#oHz z>vIEJxmn4Dv|^$;Ge0JH+C5MuuH(f;#cvTHko9pb^spX0)JwGNHy|0aL56 ze>SRn53b+PTLayyU3B;lF5I_MW+@XykByW z^;eVmw;BxFe>E5ty?-S2@TaO{jFZq)Q3WQ=3(LfV4oM=U5qd1^wSkBb5<~Zyd;X6kCcUbhaHqYdkR4|$Z4Y0kr#fd@N%cSgnXD@R z&z>_E`J#%*M2>4&bx@6_*pRy@zxMP+dPXvT1H#uH)#Mf^kulyEtGtY9Ev$zQd5tnf zno@$UfE72to9D)@WfR}+yCHBLoZdx zz&Y6zxDeE^*2n<6dv&(chG87CzO!qd6o@Ut2Q;{MAvCv!l_GP;*P@@9GoJ-QIb`<^ zGW{Zngl>V)V3std*>4NTOufa7;2>~=lcHj1_OPvmMUBWC_>;F8Q3#yaQ^W!^ zu^-4u5a<5_z-^J0dZjM_&i%UpWdCmf&dL8L09%s8XwVhGs8_9I@@0@!ZOG#wP!pu# zkYwF)*QLGyn6M`Fj^5j6?o$?y=<82t+SSO0L+VVqmfh|)&F^%b&ikKPQBl=o328F> z#7Bvv28LjynrxL4&?Zw^tT{l89L-TwKxNJG8U282k1vG)R4Hj!ic?wgc{7byh_P_j z5huA}*OjN^XMq>ZQasmXoq53(r|7XJ)yk@mxo+#0|F^*IV-Mve9ug=ruLhT&%~_3o zyzX4GAqnu1)*LxT zTO$xT$bgEN=wo+7W7P93i6(*I7pS<9)ZnS}8W!!picPXfSi$u*v(YVZXUf*bz`L)T zO_Nja!Ky*EYEH3D+tRgTFPdeJL>s!we~auz9JL_op4||Jic<*x)U22Qdrt1~=|q=| z8RTg9z-wH6V&8r#O6Z9NZ!vT-JWc&(N6E0Q%!Q#uDlZMu;2ckYm&xt$b@EF7X@Oz} zcgqQrnY(~hyNo0ebv&f(kkS*-6CAejP6+UD5{tTD&%+KvneM9y9QqN{$`a$)D93MkBSenq%n?Jjw}9i(>IBJnhWZb zHsG8lituDF$mdL9;^nWEb71IzO6(LRb6B7KBVVw#AkdNT_J6Ix|D!LaNN>O}enC>@ z-z8og{{>5Roi7yv?Jok?strbhBXq#-NKpwp);?J=n}PH`Z69R63a?xl}bdUb|c{Pq5-Fy&2Z3ZFN5V#q3eHmzDKy=kxs*(?4^S zbv8%`VjCDnAYd^L6digwRz*eqybz)e7y_L4?#fnqR)={aXO*?}V zbp?J~uRj@L|8pkSB0Fk19npw53^KR`dZJ~KY)_f4ku)blRA}`@RJ}bVTVWc$3JHYT zTm^-}giePcDH3;H4+9r`+j4@7TS*hSrg)`!%749u4+QEc%T|U{4Jn7xgEGTm)7uE> zG+rz!)t2LoIx$_-L@%yIo)oo>C)dzTJ4|87;|lP+dv0B9u@{qA` zF342C1S6&3?MC7H2$?o@K@v>5i(}pHYz0id7;BU4hRnpql#=3)&u0AT@0q=PNiWrSj6tK01qQtIa@oEyRf7}WZ*_BXPT+Y#$(|hyz67mk{5ZMAU! zcl0YbAU@Q_AHsOPY{9X1g!uQO;zT{oe?MVAfasbsMtwNoiKvW{i$)d(yM7iZIH_G< zz|(bak1V{5-Sk{vItk*; zP9~TzTQzEBk~VYqCzz$1cp!bkn}O5UGL4okNgyLQ1ZZG>sAVGH*X&j=^*_&kU)RrX zhl!Ea6^2Z;#}&c}k~%mkAR}Q55a&zHon`SYmJrY*`AEIvVu?GDW?*9^pykUuMmbws%U>!x_JS1va zPcT?pfBb#UwfM>VLqdl#v5dB27lrgopwVV@s+It6wj%Y6VyY&VKKEsbuP7Ys`v?tq zvkt)ZF~hYF`Xur1ET0);Pa*g_UoBT6tXVa6uvy*wz3*JS>~l-0X4Xno#gNoe^z-P!Z~=0Z)L}=;Pp!&n17@K|X;K4=XCLYDAMK zjrB`9;LHCt=#XuY$P`aY7^y(AgJ#G76FTW&8Mc!ZGFWr0Qfk0{&EYUp{#>!5g3HE{ zLhI3Hjt^;k?$(k8gAFhDTVVTnhjMbpz!SN7gQ>#OJ+NoR@_0k|MftFDTjG@GLJb6Y z^kQE+;5-?PeL(#R#(nRzU0H<65K_7O&{5oC9}P)aquDTwL{`F$l#E7SV(qTD@QLe{ z@z$dn7q;nOU2evFOMfD7Z%G!;u)nV$oScuN&SoDvCquKpBAmSn)susT0%n!z7OrwK zBwH@py{7Qgq8UmBnFv&DU|kFn*9R*<_>{^L3UJx(TRPnEaAYPOJT}3LDsCgN{X^Zf z@}t1XxkyEK7&cnX=CCQq7C8*RRUQ3LcZ)N0I#tfA1qiwB4m4gYY=g#DW@5yR{6xxN ztMNOC<-^1CPaAKYCUOm$ktes!#x06z=1ErN>3xvi4xy8uh#N?Nm++{%)?h48kEj`K z>Q{tU9Fm%QKf(uI&rq_rl1F496~zDnr~Q3X&xD0uuOM(c)gI-%w9;hNZ0*j5x-wG# z9M?!s_bsX_`Qm(Fmy2M;SiK?F_$q#P^V{*X$E2~pN0&r~0Fev`2L4@YA;2_&Y8IKJ zJiylRCL*R@Vk+*}Yw!a0j?dqCaQdacjey&?|)@}hWytgv7p{U7l{NTMmri>-GZj|L$ywlC=6PPd<5l-$h9Q8 z)@N5Ys{HxqNrEnVNODce)%+^kX=c;wXu5Vs&=*8=Fa{pCk>ko}dV~)Hz#Jvk*pVu} zJqdo${%bEFgk*5Gh0&*bFpge*<^}SqrOl>|z1n-N3@=3p=v9i7QkYrg>-IkZLrs7NTM=Cse5Qz_kq~#4$F=P58B` z!m>up*S1mkR{Dl#Q2!Rk{qVEH$ea`1Ujwdp7@pcw z&V2uCpH`d`<>ZHFH4)jCKJ*sc8G?A7N&bZaHX?l$mwo-!E;0um-v}yTsO5Z@ngid) zFz7C9CDf1s!ekG7a9K^`AEU&&cNP|0p>0if2~?%9wJ~L6u?k?*z!%^UTcf5D%cc*j zRq9P=qe8T%s(jnTM%2#tUGjY_qx3z-x1U`w&gBmOmP2n)SXb(3yJ?^0p_x$9s{cIW zjD*C>eeSW`rKCo2&SoQZO%b8KLfc(HG0U6m$K%=5mYQcN$E4&eCrBv45>@qbyl}$N zgR%#}2l5Sv*lykr>gZ4^YHmFbI>^Cx?gGg3KBH^qakP^35xR4d838fK>7Q|b`Rn}YN(#wf+Cnzw^4r5M z5zH*)#fIfXC#7hTN9=Hl|8_;dEGwT1{&QE2qD@5S{sN`?zf0x*E1fe${X;=V4a-;m z#Nw2}=8hQxRKsje*l>%&w~hovuharaA!dJnuZVCc>~+1>T{=jA#x^}<@vhC*fz3`q zA#%4R0RtJ8w{8FSMP!?*>oy@I8bB#@aQP*eqvnzmQ`Fk+El9%{!bh5!eOy zO!J4FEltIa*SbYYm(8Zj%Fp7M8Zt|L;9@hjlMdQi23z&VnsO6!sfaB+PYVibSS#u; z8)sUpe6(v;YqFFtY|4vFn#2Bj$|+dY=oxyHu-GCXZCxvsX_^M5S-*e|4v!!Q#ht@m7Kieyw zVtjH~+88^B4fdWrtJsywA@qdJxob#}nYXio%Qd|^i$yGY6Lw7+5m3QDRp;XGHa>{X zwkbrY5Z!pYgN#+a`vY7KE(lH}^63DbZKq#O9cpKIgAYIJg!XM)|Eci%6rEIxy)B28 zQxONH<~$uL9p*uAe_7rk0RXG{m1=+(;r6Vq*J>gEjmF)=fl8n&yv56+Ch!vFc($Ls zD~PBYHfUP}b4xUMTSCw$3~XzF$_Fe|pdZ2~0`~!1AaBMYvS$eRfZmh0(I)rv!0Q!4 zaG~`%q-U_!d8O1E4U+3co-YEJmZRiLY>f3_$?!`_a|CH$pxPA9IGELHw$`kg{=BOC z59GdI!N(m1&pJ=KsGbtI1lEf&=89*M^%g^!8txq8Ng*L}aqhbbiJF8?0AvdY;vxv= z>mzv78JHi~wl9-bL517WD5Jvi|!+ zn2Ox5ub32{jWix9_#^;^wz9A?YF65!+;f~F5)F)41)3+38p3AFbV3`KYqKlY{V%RT zVoAI@A;rv!ki2+MON0Zx;KO1X?yn4w&TZn-hgm(g-{)i}Q;P zKP8e4Z`(25Z(#xKm0pvAj=H4 z=4SCOC?Iti&-v}MaXL)qYDBn)+)cQwOKVawOF{(c|Do(1_%q#?_0LYn9otFAwr$(C z&5qI06Wg|J+g8U$r=Or>bvVyDbJm`{&u9KK>%4}$u3y!C)wil9l%y?5^QY>)h4Wk? za(?>ki*+8flua_e>Mpgq5dVJoyoyAcco!2cx!*5@m>QcI$YTeNv@qXb-#-v5oMVgQ z88Qa{Q*8iSh8mkd5j%Ir#@>cEGa?c%Z3ug1n<<>a)LFRxQ6#dToE&#Wu4iYNAwe0< zG>)fKn*gJg34A!-b>hM`K;#vtYK8cTmN2RRXS0o3p2+xf^-g`W-Tq!OnBY-ykx%4$B@0sLbhY~ugAGB086T84 zD^?u#sTn5w`PXQG9=#lrTO3(;(=Rb{FSNCC4Nqx4!E1q$-p9ix2Nac52VgbTWCm}^ zw|u!G)fC+6T0`_E3k?yQT4g{S6V+~yaImAYN<9S^HL7R#rs3@Ae;;xC%eb&yvuGd! zg`4a@EW=p-CY)6D9Z^As?vjJHmS#h~I*m?wxzdKnH7PT;YB0NXkO0g{u;Y^XXI100 zX~Z{10bKT&xaaQ?NbjEllCNhMwPGe6Sa>Zh=J+2kI#_oDeBYpqppo#4<2{@RIPt3s z;zdx!gI5qbt(7K5>G9fdP~(aj?@a+IE-&Fkw;S;k+Lw?lQlH?rkNFnQ&G|^+H9K zI|pp{Wkm>PTBz7h{_~&*6rOk8&$lZ&FacM`%VKD)|6K^^l;yW+Z?~Q2 zXDbi!w8dUAk>a>u<$Kk{GR@hIc%>Qs({B{l{s2{)qtT)6HB$c^V0{pt@Pn0R;ulYD zqUl?*Z7-I$FCA4|p*fck)o6+oz1DG$>4i^WRXdRkJ$N4Ca30a8e}S0 ze+8MS?IMI9@*?zZ=@dliAVUVhv*9bG05-c#(EnGE<;OxJs*Llt$KoNbC`l1V6bhd~ zZL^h|9j3-?!beChradzc$#nQlD|*_Gzxme`rQE&D1WytNFMsh4BHm8z^%TG%fL;D6 zDY`My3?7Gs)RAc6*J*tc2#?dhNBGpMR6~p;D$HBrp8wo($dn#!Yg#K(V6(lKaz$Pc zd#sa;drvV!JKzJaIOz(U>FaBO(sNdXyAK+SF#ckhNi3)cpo%o+H;5THfk68iQMUMM z`Y<=x7UO6(-+PG=9PjHxarYJ20XlfA?A#Do86%ISyXLMu6#IV;vN}nBj00bzdPzMW z@Fj(+t;%jQt+zfl=V7T+oRlM10>z1jox{w_FhZiJ9YQ2%3nuhKnPN=3+a1_P{sr|d zq>|J?#W0&4<_yTpAG6vvI>23s$cB~ki~0XUkd6KS23cLge+3x?D9AXSV8~1B=R!2= z#Rf0qO)e8Gh~kTiAs@J9)Y2o~WYmVu51L@~2bQU{8B}ye*fVQ@WvSL?R*cpn?n&*e zSpomHXXdY$tm4myH8KbxSp|{I|D-j=@^|J`)BUS8^^R!2uA)i*6&C}e*`~k<>{11T zPJAKKDiFet>CnBn%DgROJM-xv>J>I15J|uqgpu2_?Hmf;xLDYBLlkFaZ#&LqZ+gs5 z`#!(lL4Fs`jw`_EGFgi;gXag@3*%GAJU7=G7V+z(tyl>a3C95LPIr*64zna)njQKA z!J!aQGg(_rU&dh0lK$8p0Ho4FS@Rg4Cf$$2D7`IU_p5c2Lt4Bx6OxugX;tK-Xw6re zf9lI7TZT5?e=wS_F|HGWLAVY&7f+^!{*qoHyQ$4oG`!uiA=gKlOrqicZIBi_Q@#gh zNC5x<)(xeV$)YJVnTQNJQ|V#J=?p93&Sx=&_r>?&^p8;uAZ}oDXiJRH#MgGX(1DcZdsNXx#hm)Cm#v0$|nXh)IV9|s+;kGa8JRB;GlN4=hH2ZY4<%_9lH?I%+*s8V~LVe zesZ}C9OpT)R28|S)0u1f67c@gE+mOE9i&Ck5x)jF(Q&_N7Sn@{;w;|2COUn2;H#4~ z%Zb09;v666mYe$4eHxVQE}4_{OlL6pK-Xr^o0<{#aOuERm;ly`s8f(fk;>70i=sbf z1!H`*{oTs3GKnzo0veOx)d3(dhSZSTP{CZd_FY~$dInhik`w_W`3dvI#;P;ev$74K zbP6`eE_TsW`^RAKJ9VlO!HosM-*wa2^jADzJ@b4>vC(R z{Dq0F;ce5n9@UN@D^{$f#W6`i2B`cfBh2l^e?+V>*G|IH6uya5V#xE6mf% z&D$QGAbh#QY`Q~C1M)L0K}$V{qxy}~KL>5zvG46ny*e;Y(XZh$X}xZTira~5&S!|b znezTZG?r8n{mlCZ50R&r8R&ubn3tD8g}B@&2=<>(_efb$=rpK4Q2M8;`WI0GQWa>m zAb_FyN2m*&4n&7mi&DN`w=r!{iC8nPXaY20beM}B)|5k2SLl-}=;?k6L1MoiAlgg1 zPPF=A)h%`yaK&PIMh;byHGSf7?90k~JCiH$>iz-t-FX^%Av#!C%!w*|h-)#NLJR{} zt(Q2|WRkNw6{UsX9&ER-(tazBS#T5=1eulM*i;nyVeP2$OBf@9b<&wYuK{oJ^H>rUi=@wN~8KyW%I9)$9bsa?j3cW6b!E zhas^^>5I-^Ub*@i(iOqnS6R?DQxd=$;Tux<^yk$OTLts2rN+8c z35dS^VosS0`5;D~b`junfQ5*V&gxRDJ#+LqhfN(+-Di7+_G*U!c^0IJme3RITH_2o zjainQ8^GP<0j!P7&>oU#RH@pE6Ne=px#mv=UT)T(4O@ z#wvv7i#_KIcJmyZL)$g@`yUOV^Mf9xDGLy6SN!5eR4ek2LYss|F{@DJQTH&vqrZis z2thx1b3YL4#ZVG8u|L=$XexQY(Ki%)@ongurzzjHFdTB5BfxOZmZ4T#oL}OF%XRanKKOiqxsI1JY%o3RY3uXz9 z?1bFn1Y>r=Rxh7ZtGKOQVNhiK_54T^9g{%#?#v&_s8F2eJ;!Fz?I z)7$@yh6SpbqplOI*l_z@%OaVna*npRe|7!J{&V4R+{^Fz2G!4%I%GB4!DPZkGzzI_ zCx&*gh?l4(;w>G4Fl8ZSPTEN1n}WP_`h;Y`K;2+IEt|`S`3}XVwXUe1|cGs?cfIUTZ{_ z+LB{pv((a7Sy8#)J}6zYjuc|vn05=VI80G$(Y}Ks5m=8cNbGm~WYB5GC9#7xOOxgb zAX6YL_*Kl2eSi#g=5aX8r<;j9yRNHXK$mq>Z682nq;@(cVqWdL?ylXZJ4m|@Nq{0} z1JVI(8oS$Y6UDL)0ofv5+J{>P;p4D5^lsI#5BDP_#YuRT_!H2An>g7U0s(Hs0B z7x}qn`qGp(dXFj+UmZSSbv3zn<{zFq?!RY#5;UDo?BH-Zs)-ZR>aEh^Xw1^-xa>&2 zgg_~qZh2RSdo3?J8%JO2vmO=Ii_Z_H6{qyTmhVMHD3Pp-)(Lg_OC+y;J(Bkh<9PUX zLK@Do+&<^w}!D6%a %t`X9uhCfG{g^!!^l1gnoeI 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 literal 0 HcmV?d00001 diff --git a/mobibot.iml b/mobibot.iml index c27d878..ca3aa60 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -161,6 +161,15 @@ + + + + + + + + + diff --git a/mobibot.ipr b/mobibot.ipr index 1eac796..9c565c9 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -138,7 +138,9 @@ - + + @@ -146,7 +148,7 @@ - @@ -172,7 +174,13 @@ - + + + @@ -181,6 +189,7 @@ @@ -199,9 +208,9 @@ - @@ -224,7 +234,9 @@ - + + @@ -252,7 +264,9 @@ - + + @@ -294,6 +308,9 @@ @@ -308,7 +325,9 @@ - + + - @@ -572,7 +583,7 @@ " + entry.getChannel() + ""); + buff = new StringBuffer( + "Posted by " + entry.getNick() + " on " + entry.getChannel() + ""); if (entry.getCommentsCount() > 0) { @@ -3308,8 +3319,8 @@ public class Mobibot extends PircBot if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase().indexOf(lcArgs) != -1) - || (entry.getTitle().toLowerCase().indexOf(lcArgs) != -1) || ( + if ((entry.getLink().toLowerCase().indexOf(lcArgs) != -1) || ( + entry.getTitle().toLowerCase().indexOf(lcArgs) != -1) || ( entry.getNick().toLowerCase().indexOf(lcArgs) != -1)) { if (sent > MAX_ENTRIES) diff --git a/src/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/net/thauvin/erik/mobibot/ReleaseInfo.java index 85adc8b..ea5f250 100644 --- a/src/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Mon Oct 04 00:59:22 PDT 2010 */ +/* Creation date Fri Jun 29 06:58:32 PDT 2012 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,21 +20,21 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1286179162570L). */ - private static final Date buildDate = new Date(1286179162570L); + /** buildDate (set during build process to 1340978312640L). */ + private static final Date buildDate = new Date(1340978312640L); /** - * Get buildDate (set during build process to Mon Oct 04 00:59:22 PDT 2010). + * Get buildDate (set during build process to Fri Jun 29 06:58:32 PDT 2012). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } /** - * Get buildNumber (set during build process to 2). + * Get buildNumber (set during build process to 3). * @return int buildNumber */ - public static int getBuildNumber() { return 2; } + public static int getBuildNumber() { return 3; } /** project (set during build process to "mobibot"). */ From d5669d4cdb0e7c633b2a3deeb21414ec9b34b435 Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 6 Jul 2012 01:47:25 -0700 Subject: [PATCH 003/842] Automatically fetches the title of a URL submission if not already specified. Fixed delicious tags. (comma delimited) Added submitter's nick to tags. --- .cvsignore | 17 +- buildnum.properties | 6 +- lib/jsoup-1.6.3.jar | Bin 0 -> 276135 bytes licenses/jsoup License.txt | 21 + mobibot.iml | 9 + mobibot.ipr | 43 +- mobibot.iws | 487 +++++++++++------- src/net/thauvin/erik/mobibot/EntryLink.java | 12 +- src/net/thauvin/erik/mobibot/Mobibot.java | 135 ++--- src/net/thauvin/erik/mobibot/ReleaseInfo.java | 12 +- 10 files changed, 464 insertions(+), 278 deletions(-) create mode 100644 lib/jsoup-1.6.3.jar create mode 100644 licenses/jsoup License.txt diff --git a/.cvsignore b/.cvsignore index 3369402..53371c3 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,8 +1,9 @@ -DevSuite -build -dist -log4j.properties -mobibot.properties -fetcher.properties -*.ser -logs \ No newline at end of file +DevSuite +build +dist +log4j.properties +mobibot.properties +fetcher.properties +*.ser +logs +.git diff --git a/buildnum.properties b/buildnum.properties index 8640ea3..e07de5f 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ -#ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Mon Oct 04 00:59:22 PDT 2010 -build.num.last=2 +#ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler +#Fri Jun 29 06:58:32 PDT 2012 +build.num.last=3 diff --git a/lib/jsoup-1.6.3.jar b/lib/jsoup-1.6.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..b3421d59b99b2fe6ba2ff28f028b7ad476973ad1 GIT binary patch literal 276135 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 %t`X9uhCfG{g^!!^l1gnoeI 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 literal 0 HcmV?d00001 diff --git a/licenses/jsoup License.txt b/licenses/jsoup License.txt new file mode 100644 index 0000000..fda732e --- /dev/null +++ b/licenses/jsoup License.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009, 2010, 2011, 2012 Jonathan Hedley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/mobibot.iml b/mobibot.iml index c27d878..ca3aa60 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -161,6 +161,15 @@ + + + + + + + + + diff --git a/mobibot.ipr b/mobibot.ipr index 1eac796..9c565c9 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -138,7 +138,9 @@ - + + @@ -146,7 +148,7 @@ - @@ -172,7 +174,13 @@ - + + + @@ -181,6 +189,7 @@ @@ -199,9 +208,9 @@ - @@ -224,7 +234,9 @@ - + + @@ -252,7 +264,9 @@ - + + @@ -294,6 +308,9 @@ @@ -308,7 +325,9 @@ - + + - @@ -572,7 +583,7 @@ " + entry.getChannel() + ""); + buff = new StringBuffer( + "Posted by " + entry.getNick() + " on " + entry.getChannel() + ""); if (entry.getCommentsCount() > 0) { @@ -3308,8 +3319,8 @@ public class Mobibot extends PircBot if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase().indexOf(lcArgs) != -1) - || (entry.getTitle().toLowerCase().indexOf(lcArgs) != -1) || ( + if ((entry.getLink().toLowerCase().indexOf(lcArgs) != -1) || ( + entry.getTitle().toLowerCase().indexOf(lcArgs) != -1) || ( entry.getNick().toLowerCase().indexOf(lcArgs) != -1)) { if (sent > MAX_ENTRIES) diff --git a/src/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/net/thauvin/erik/mobibot/ReleaseInfo.java index 85adc8b..ea5f250 100644 --- a/src/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Mon Oct 04 00:59:22 PDT 2010 */ +/* Creation date Fri Jun 29 06:58:32 PDT 2012 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,21 +20,21 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1286179162570L). */ - private static final Date buildDate = new Date(1286179162570L); + /** buildDate (set during build process to 1340978312640L). */ + private static final Date buildDate = new Date(1340978312640L); /** - * Get buildDate (set during build process to Mon Oct 04 00:59:22 PDT 2010). + * Get buildDate (set during build process to Fri Jun 29 06:58:32 PDT 2012). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } /** - * Get buildNumber (set during build process to 2). + * Get buildNumber (set during build process to 3). * @return int buildNumber */ - public static int getBuildNumber() { return 2; } + public static int getBuildNumber() { return 3; } /** project (set during build process to "mobibot"). */ From af780428d4d908063b2daab8e95c1707a571e851 Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 9 Nov 2012 00:43:17 -0800 Subject: [PATCH 004/842] Changed comments format in RSS for better readability. --- buildnum.properties | 4 +- mobibot.iws | 217 +++++++++--------- src/net/thauvin/erik/mobibot/Mobibot.java | 10 +- src/net/thauvin/erik/mobibot/ReleaseInfo.java | 12 +- 4 files changed, 119 insertions(+), 124 deletions(-) diff --git a/buildnum.properties b/buildnum.properties index e07de5f..cc5bbaa 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Fri Jun 29 06:58:32 PDT 2012 -build.num.last=3 +#Fri Aug 24 02:59:30 PDT 2012 +build.num.last=4 diff --git a/mobibot.iws b/mobibot.iws index 2ceddf4..d18b42a 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -34,12 +34,8 @@ - - - - @@ -142,9 +138,9 @@ - + - + @@ -295,9 +291,10 @@ - @@ -361,7 +358,7 @@ - + @@ -413,6 +410,34 @@ + + + + + + + + + + + + + + + + + + @@ -461,34 +486,6 @@ - - - - - - - - - - - - - - - - - - @@ -510,12 +507,12 @@ - + - + @@ -549,8 +546,8 @@ - + @@ -560,8 +557,8 @@ - + @@ -610,24 +607,6 @@ - + - - + - + - + @@ -916,6 +912,7 @@ + @@ -923,8 +920,8 @@ - + @@ -1099,9 +1096,19 @@ - + - + + + + + + + + + + + @@ -1114,30 +1121,16 @@ - + - - - - - - - - - - - - - - - + - + - + diff --git a/src/net/thauvin/erik/mobibot/Mobibot.java b/src/net/thauvin/erik/mobibot/Mobibot.java index a117ede..e6880b9 100644 --- a/src/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/net/thauvin/erik/mobibot/Mobibot.java @@ -96,7 +96,8 @@ public class Mobibot extends PircBot * The version strings. */ private static final String[] VERSION_STRS = - {"Version: " + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + " (" + ISO_SDF.format(ReleaseInfo.getBuildDate()) + ')', + {"Version: " + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + " (" + ISO_SDF + .format(ReleaseInfo.getBuildDate()) + ')', "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System @@ -2757,7 +2758,8 @@ public class Mobibot extends PircBot if (split != -1) { - entry.addComment(comments[j].substring(split + 2).trim(), comments[j].substring(0, split)); + entry.addComment(comments[j].substring(split + 2).trim(), + comments[j].substring(0, split).trim()); } } @@ -2903,7 +2905,7 @@ public class Mobibot extends PircBot if (entry.getCommentsCount() > 0) { - buff.append("

"); + buff.append("

"); final EntryComment[] comments = entry.getComments(); @@ -2913,7 +2915,7 @@ public class Mobibot extends PircBot if (j > 0) { - buff.append("
"); + buff.append("
"); } buff.append(comment.getNick()).append(": ").append(comment.getComment()); diff --git a/src/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/net/thauvin/erik/mobibot/ReleaseInfo.java index ea5f250..f02a294 100644 --- a/src/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Fri Jun 29 06:58:32 PDT 2012 */ +/* Creation date Fri Aug 24 02:59:30 PDT 2012 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,21 +20,21 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1340978312640L). */ - private static final Date buildDate = new Date(1340978312640L); + /** buildDate (set during build process to 1345802370733L). */ + private static final Date buildDate = new Date(1345802370733L); /** - * Get buildDate (set during build process to Fri Jun 29 06:58:32 PDT 2012). + * Get buildDate (set during build process to Fri Aug 24 02:59:30 PDT 2012). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } /** - * Get buildNumber (set during build process to 3). + * Get buildNumber (set during build process to 4). * @return int buildNumber */ - public static int getBuildNumber() { return 3; } + public static int getBuildNumber() { return 4; } /** project (set during build process to "mobibot"). */ From 3b9c5d6431a3c4c786befccf7a3d372ab29ca507 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 12 Jun 2013 14:31:23 -0700 Subject: [PATCH 005/842] Updated Twitter4J for API 1.1 support. --- buildnum.properties | 4 +- lib/commons-codec-1.4.jar | Bin 58160 -> 0 bytes lib/commons-codec-1.8.jar | Bin 0 -> 263865 bytes lib/json.jar | Bin 0 -> 39824 bytes lib/twitter4j-core-2.1.5-SNAPSHOT.jar | Bin 406830 -> 0 bytes lib/twitter4j-core-3.0.3.jar | Bin 0 -> 284059 bytes mobibot.iml | 27 +- mobibot.ipr | 20 +- mobibot.iws | 274 +++++++++++------- src/net/thauvin/erik/mobibot/Identica.java | 4 +- src/net/thauvin/erik/mobibot/ReleaseInfo.java | 12 +- src/net/thauvin/erik/mobibot/Twitter.java | 17 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 4 +- 13 files changed, 216 insertions(+), 146 deletions(-) delete mode 100644 lib/commons-codec-1.4.jar create mode 100644 lib/commons-codec-1.8.jar create mode 100644 lib/json.jar delete mode 100644 lib/twitter4j-core-2.1.5-SNAPSHOT.jar create mode 100644 lib/twitter4j-core-3.0.3.jar diff --git a/buildnum.properties b/buildnum.properties index cc5bbaa..096cb20 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Fri Aug 24 02:59:30 PDT 2012 -build.num.last=4 +#Wed Jun 12 14:18:56 PDT 2013 +build.num.last=10 diff --git a/lib/commons-codec-1.4.jar b/lib/commons-codec-1.4.jar deleted file mode 100644 index 458d432da88b0efeab640c229903fb5aad274044..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58160 zcmbTdV~}o5vn|^0UTtHwZQHhO+qP}2wr%5S+qSvdw%zyLIOpEI?};yVocW`wevAzlz#6ocY`{F-`U;oUqqAG&4l5%473jYa%0%HCLw$V(HZU_Pd z#0~)jg!=!5$qLFziis+#(8-F`DF3zJU_kVXPyX3kDwDMWF{P!I@umPQS`<`5R6Vm{ zeE?O(;jY8;e}CvEBInPgN=`-}ZM|%}yMNpD0kZQS=25iBW96h*r*JBayweiy zv1L5?iXqvFNc@QTJZOeAtW8zALYOfzingZ~NXrLaTdz(j+Ojae=Ru{P^Dao;F1b~2 zuOKIe->2RJksiY&;<&7{Tj}Vs)N7)}sx%uQ=qJ@0uvN?HIjzKxrn}@fn>UOaG+tzh z%PW{Jt-kbLo((;)S*U#)0L8UAQ(ti94{hM3;D~hi z)VjDbF`=JX%+#`KTPVkosdrcmkhsV7o_ceByNO7uWHQ47*6=b=lAia-eb~3mE*d1- z5`k9W8NvXv%nCrP+ZY6Liw%qD*$lLV6MDzlxcKOOv~%sGm@o=u;k% zb#*T9fAj){bGa85+FgHKZQ=JQXW2~cDXHaQ)yLNooTdKumb5n{?DhIRQ$yAYYSYo& zHZwp4_x>-F1Oj6EZ5zHimZQu7>9S>Wpyz72nSE z|J@qu-``Zmu)d@>0uT@d?SFF~GLpiga>}A~E*>u1TDJDsqe$O+1V4t-`Q>2QrsT34 zBX7kTP6I0iu#&adU~lzZo5ZbrJ4~JKg=_kM&CT@Vi%l`U)CetV7O$=^yLcX^rhMpYpPeJy5^hDbKdtWz<(50EEl8e==4{>e$ zeC9$Ms!nqFr+9z%-Mb#Y-=E949oyTG>+I0gA3olu_vN~J+R<*VYpET*zn-6u&$p?) zbndj(A38ra-(GG`CD-;RVH``ewl9E@JECt<`I{V?G90YeU5$!N=pnW|6xER+ZOLC^$Wpt-MZ=k6^+X9?u3JSHnpED`55dREvD)t0*C7f zsp?DhPo=qG5s2o*oVMfb_(M$Z4pM!=Uz!hmiioEmHzU5VJ5WMaN1tbN7Y4HGzbsWh zON{%eJCJyBKOiQpn%2|#?9g^lxp}L<#JmxlO6l=LfV%!mhu1sTna9r8V+)qBmcA8_=uQ#`J1YHUG%D8F#}Msv5PbRRgyH zeSyw){xX%}rku|0BVO(1B&iPA$bB$z3ikQH?te zvd+H2>E`doiH~fPgQDji4r)C_vTy5r>Y`nxWlGDo+uKt8ir_Lf!t(ni3CDDNp`)(z z4_Z(;YB!ch8G9yJfMAYgIId^r0g7QZgjhuX827O6C$?%xh+3W4Bz+%GPNu=c^u7LV zl<}Qu@5`(~TEl^ZWzG6-{FBym0?#lEPqF)BWz5X5e8;Udb@46N{#WH0=>Zv}yC|kD zD|ou$-GHvT1N0VXJb7zw!0+0R-Tv))F}`&6%6TQ&kE_S)o7gW0FjQ_HlM1Zdu^T6e zQ$9UX$PjgdwoNfMfi%bp?)FO}GE@guq?tta6{fb7DGek=gpR(=U(a1Vw=HHz6NdSh zgH}=ZBfK*XkWUos+S#!weZC&u`jT~ecCFCn{6rmGP2BXVjwRK80c`lm1T}5z1@v=_ zWoSq&h^_Zg5d-uk#MI9?*>Jc*{XpXPh+^gq>l7?**1C1lApy$}CQx8istB33Qhab< zA#F&|UTkdNVZ1@zlc!Ogig+sUa)V+C95XIB#2RJl9ixn+E&O8VVER{J$W)Hy1NnmY zmj*&R{3fVXY)U5qs@j(8rV{KvNwJNUo5U3F2-RJrTVXh*Y1poKv2}rREh((z36(O1 zl1<`9f5MV2r7wDkvJqrG6w3iu$F}@6e`2pz`dZwm- zY9Z15k92!iHY{sRtbhF(lrYyQ zYXJqvp-Le{ri?iCA$6r|`R)is61;8L)Z(Ks3zj6Q9hTihCKPc~z*SiPp%)lg>)#|N z6wY4`t75<1sq?B_gOxXCwvvnoW?(sINuk5PwF_Tmyfc|f&SHZ&Gbc&S zxxNKG02PL?L|KpK&+b%CQ5;)0^g41n)I#C}ke+)R!S=ZN7$L?1L@un{FpT--7#<^n zUWBK#U4k?PqxtKHEV~Yt(cMKw>w7wYR*=> zB7)1NT#b)gz)ImOxH(6_M43Q*QYglDs8;t{`3%{{XqIS|P5Pn{{_JWgU514!jj8a` z&;rS*wnX?J&$nv9J1I69K4RTsSGZg;dKsz_Hx(l`q~AR~h^>`2EV{R6*zL0x?ocv) zVz_U!sD-%(!8Ta?X&km-w++Hb7yUn3*$ri`t+RLdf~{1m^3^E=<7`r%&aI`wq({F5 zLYmSz5%R{J{kM_yT8T0wMf8jM`hLHV5KwzM(b1fpoVFw~j~d?H<;XXkt(iJj-+-E) z%RXVS0{g^|C2|)W&hfs*)bZ$20t4&cJ8UFR{GeTkyv4#J)I|^0{6=(ACD{GA4#x@D3+hR66e=XHF3Fp^=cyi%)#w^w z02YHT%t}=9h21ESu>NKmUH?>Yo#rgiybo1iOI$XO&iZ((y1)XtcQoi$CJ8f+C`l7f zxl;ogjGW_KpwFt=qE4+vZb^nqD_;j)mOMgMlRPI$`v3zNcip=0X{+QFZ9C;HM)6hX zx^deqP@a_|X2pOFWa9K0C~!QRqN<2dHVrhTmVO`QChvD5pORm*MLt@JOP{h8Asj8K z&LoUNVru6oG{> zVGj&uC0pHBis>McEqXWG5=rE~%Z^~_@rV)Q4dPsJx(pRSBL|K4)~9IUQLjJvL+o|r z<0Iyzq}Yybm^vv8Jc|s^WmzI>k>-%0WOIIRNz{&_<`eXYaW#NqOw}vTn9Fqa%d?Uw zOskgl1;4^zB1}JudM9Cqjyps%qzGx9M3y{PuEy$k=TKo0UY^PUzA_xN(`QgsDV_6G zi``kwop9%AtTE2E49`)mrXt{&H`-C=I{6Yo(tIA5dcz3cHWcj$ILC7h?S9BX)2;QV zs*;X@2_0~Y(;HsaA)D#@4u@`*uWG3?lwoe^!z*sswZD*X4+C=|R{X2%_mQ|fDoTmz zfMRaI>k73>hE)aHxgdOyZv;Bs@lsKx^r<^aP|3=mVyt$LTk&H$d)@HSzWLqD z+dg!*#0Stb%pQ!b)UO; zx1QI%_p*y&UsAu09^WV8!>&)iZ!^Ci=8uJox3B&88UJYqEOt+CPnf}OZ)Z>Hot{o# zZ)f+briMIaHi??yNyeXG|b=ZzyPPWNpch6gg5^yH33`w}j~-AQsLxP%gaJ%t|z z&v{!sG^1ionr#*bcQ84e=|zEhm0WPi@_Cn(@Qc*~<&1B!{QGyt2{H@k>0@FBespVL zj~HIUkTB#s&cI#rw@bB$OwJ*i)h)@SYi2xrEI3=k*4VO(lECD_>aRC$@K#lp1A55a z2)cR${zih#1MU$PFxXD9-BLC@lCrS2rzG!*d(O1QwfA}jvy`}Bu3|qTFa5>fYjW@K z=JN2zMLI4IGajqDSc{h~>}L{w0yAuzHN`u`4Pk#w>`Si2E>Kb~HyUQaB2W%1?3FFn z1pIW)hwy7~a3XLLp}q|5>FpAKKR0^L@#NYLzK}$O zMs%FTD`(^Ny;gc$>vHO(=wXKIKrZDAA7JK>gwS z)a18akuTC(;_&4^o|@n3k^wUC?7#uvgioI7p(dPef?5ffbUPU#f>qP;hp z#Nqh~tiyw2j=+bQ`!Sd6-#uf5f_tG{{^*EH7$-dyi%D6e3oF){nc{V0&Qto&uWzcK z1C7Ef!HhR`hJv?gr4+5Dxhg68O;OBVguaws-?%qOFjzs5d4GlS_>}y&!?{u!nK{*u zg@|6@rxLLpoE9Ks^k~B`zw+Kqp5ZWm_OIWuhTx<5!dLxcibLuSJn{;a-9O>brzs{d z_@p*}#AoovJ63mclu!r@7nwnWK=<6ftU^8lxd^Nu!}WT%1BT~ zE$GOSNXaj4!50o zRgH5{aW1USNb#TiT=QK9O2<>r!_KrgWsT)zrR2aDHvpeVuN!xB@C2X85^8%(WAg7Rw!opqxgd~M*d$9ozOJ{{}>Gj zXowmJh~{63EB`4B2^l(@va!$^+ZZ}KUuk;!Brmu7&A#|HnI&e2$Pi#>)RQp>)?=Y2 z8n6){wvf;Oqmd;MVx^nxPl=${Jhv#=TGu>o?0|Ha12Wa>F`5yzx*DolTHNgI?QN@? zH@rJ7U%H+0UVQ$_gr(1YU;py~PR4uAeCN9JpZIt_Kgq`+%@Z{Y$x@&4>jd05T=8S| zT$I8+^1S%Q?|s<419$f|V_^A>DeE~c#-Ex(cJJrocT$Gux-a-UzWtSZ|CPq?s?7Ek zQ}&a+hXGs1)HQM|9j4Eiqu+VP{-|yD9hcR6vVo8JJ$6eT#{bZ#kNG`u%O19hyEJ`d z8cvWb#1V3g+K~nF$m~D{dktV*!@_};`-+=Pqj#Pj-gS$jaiHRn`!j7CCw%DCYtk{x zEt0Ez*jbT#R)e^R32kHY&5ScJi5qHXRdB;K6owp5;Tpjdo=#Z~ zC~xrzA33g-#SIe4%D+5bGa$mkk+Fjj3m@cqo(Gf7 zs*h7Q91~CI^t@V$y-?;|qN7+nx7^-Zn=e1L>dXZ5#8Vmbyqi5 z_NIPsbpza7+vsd_73lxnS_4VVOtsP2|-$J{L3}eKLg$>&r4E7!OlNgVkxm94mlNs5^yo7sLni(_9wX9{ve$L(N_8v?x zL>NX6!nt#(cV}L85*obo@L*Vnm#Q3OM7+R_gD)x#H}>v-m-~bd64EtcNVE}fH(h2E z6l1m!mhQ4UxD}A+uwzXBiw6T9{<)mkG*yp9h%Jn^j2RY%5w3i}jpEz}SQj9C$u z0lWMt0ug*C%qrV2(&n}4h!9A=tWZbzQ>U3!2vAGs%eGHtH48D zL7a^lN2W=i(wVuj;ZcbPhY*y9S3>74UnD6;_D}v|L4h1o8lfSbKD_3D>>OzI2L;VK zV;g|%7SlIKL|_vQn;8}XTY8*sCQ4e{1{^3cu!MS15_!}(EHus_c25*J;=$ulex|^b zooNJ$&ohd9_(%45=u^Rl0V@VpE5w5jVBnObWpNUE`DC}qQ@Vf8OrBARt}>=f&fE`} zT8Bbj#1u_>ES1o0C~WulaPV(apX-`{J2(a;sPNu0lAMobX$=MwFN8Uiqy8|H~ueCv}cXgeD$imK;nb>vgSdy}Uld2IdVmo&bn^C+lJE>0(6) zV2O)>PJv^>H0hEVr2+?$HI{G_w=cd5loI@1fl;fn} zW#LPr48?T!TO3SP)-wn|;U8swYPf7R#)ue3&@*&9r+|vIFFoQ@D(O@D^G5WmBq%=~ z)Mz!9@09eksRecUF(SODf2ud>+6*4ZzKA#??x28hBN#JooJR?5yC~dnV$M0>N zXaf%ChY9MSO^p4a#S3mk$>$G{b}LZ`6fI?Vf&{DQKLIFxx|S#<%@F3v$4dl z8+pLinUp+FvP2kU!cMZRa06j6*AK~w(k6-SIUeGxH#!Thy)_fEsM9zeEWU*6WjDGe z6$c9{p2&4|?&X@7a2nQ=g!_yMR~?3moR@?*IWd%<^N2S)80Xyu=y{Zq2c`-U z%NuotjyoEiltW;a*HS2KHkWQ=B4eplPM;WQBbVwfVM8FxhD26*LX;)5ZGUrjF#YpV zq?@{!H}!PBE!NeQ1@T3p-jt+tN@w*wL{)`^%eY1#fSX>qNOKyawq)F;%!qC$jgR0T9`oR+wMl!u6`SBv0uvX`o%Q z2tNsSx5!|*=ai-k46eX9D!dkI=D31$(`VzC)suzUKj(Ok@kzCxm=6~&`mQ1T$0;YM97S-yh)qsU3f9gW z$a7A{6>C2D#o)WWE{Ql9$wzO>@iv`vb%=MT0vF;9)8@T%x0W#|FF4*5J=8GMC^|oo zk6hxF?0UKcJVhZ0`0*VAOJkM#mA@Mg4!yFwh)w*np=TE#9f7kXg61#A5n~lJQQAeB z@#bn&=+qW{8p2YengKch#Uu@R>mi5@BDMwihDbZ)tTXs|!Ivnxl5|tVN3xLdtW!4% z6rwU!7n+(v&ws2^Os%Xr*$o}Y{ioT+~$cfo71gEbOmEM&M61lOv3@WWM zHkb=g%!=FGVEJM8Mih92P#(`2#7B(z3zSiV*>)$t0o<9-_4> zWN<)P;}&W;rmID5F2wQYL7!5%%w#W@3=g3V>|a`^246D`>zwookeXst09JP6`h)=y z(&#)2=d9^zc1zt5*^TMmnzRT5Mj$wiNls_1GlkfACv&wbsrd6E`#A=mb`vN!g+z`B zs!{HP!`CGcjxlP{FY{h^ej5~W#ky4S=Vx?n2%Sr{0HDqVp{v4HV-@9Q_xzFh$j(Gk zJsje$MvhySVk~O!fcq4tuaatafx9*>7)_*oSdd+sqbTtT0UuPHT;=RnS*o+I=ri4w z?_rntW`D-+&5fU(rSEs#e9KV?_e^L;JnD~twIlW!<&26O5K|*HBYN>?OD6)wDUs&S zn2nRh{kSDLQnfESQ>63)3-YYMZYM53zD>jS^XC#>1bjj_hF2}}Ub(hOdn9j={uUxc+Aod{KRn{z@xFJz_D~?wM`Y}a>ICbf z!|kKZQ~J?(4a*M&yjE~f`H}6=`4P%Rs1IA-Wnv=rqQ*z*M@#Rs?=$bW?vvhe%SQaQ z=DUNek^5BCru{CyLiAsO7}5u559>FxN9S4CFNQ#OK{Q4$K7xjPplg&OK%O%)V_CSD z1|c=Sr1wW`@Mrxq_^L7Vu%pFju?NiqSKMGCh@D0S&)?&`-<#wfwD6d3DVltNwvFEo zAaR{F=M;N)w~Sh@tXX!Tgs!##R<4`#L@rHUGlPAnU1&Z8>vC|Tzs|vAjlA-b!yiuX zuSUsmo#lAV;OlGX=@~a9LafP zm=$UJITfYkY{@(UP7{k%a<0=hzPO9H$l zt4mo+#PvM-BJl>YmzK5^y}=i?7Vnpsb^dCzYN3iX>O(Wxd^KzIvR2h;VCQyijq*%0 z`ZZ$E?;S|>hQVdLkgRPea>~92i}s8*;v=(iO}ntnf2It(<9&o*D;Z5Y9h@qtlkXpv zVt10CGB*hnQE65$@_wZAEoNIpRHqyodz0tCxGa&bM{Qb`ak^`V5S&cE6H72GpMU9~ zv@@N6tv@!2gT?$18)vt0tpM;SIKYOVl_<}f06mXzf`ci~>8|`)!uSfa;Ozd74Q|`E zS6W6_S_ao%**0PA>$6XhS;GyQAgMA7EKAVIjd3!Y@+v7rIm)_>}!n?E)(M)f< zpr9S6Vd?{5>!)A|v*YuU6{|FS_LTNEEO`4&$g;_khfXgLIHQbCkk2*tDrl+P=`+z*cU91Nps7VXO|ITE5UaV zK!Dik;Myl~IW`4o)E{vg#&g;{$2U+6OG0~o!9;tRJ0|Yn(O`vQrQe>v4;_OG-HLaA zZ|!FXGQN85>KIJz+(h8nK5jo1JBRMkGd&U+UxL(D0rLhpF-pb491_UXzx|V{|A7OQ zZoya|Y1fPxCAH)y5cUpiY{sBe_l_*J<}YNrGR>^(7Gm9q{}ZYwitZJ=0njhCFWDuZ zcI9q6?qbNP?U#L`p-5ANpssUUc-lm-pJ7PpPeGRPwNP7%F#dCDl|;cq-QwS1jS znSY3On6)cfB;$79psJena9ZCj&OIqI6nSPi=*TGQ35gGRT=1?Xqei}z7t>EKy*ANU z=qP4#SfGEKt@+_XpPUTCeKU4_J$iK)#Gu$F_1c_aF|SC(c1GY6Wphk1ZBqH_#3hL9 zD%6)>M8pAj)ZUAojRo}c)Aq&M46@0epcY&AtJlJ+4%{Y|-4$~Dh-((y7qtNF=e$b= z;TB~(S#Ita&6R*@I~P!0734JwZeJbFp5T@R@}$#|2~MN)kY!msGea+CnBL6-2&Hh3T>-s5&NT@7F|+ z$kX;4ToK^B;tr6$=RTQ5CZ7!?s|Hl9cQE;7)*CHsQ0Hm2uW z70)v~auY$sA^pcTRM8~H`L96x?ziBUhMaQCx1h(Auu3JrOw%LM+WsIw<_D@~X;uNo zrdg6dT*Ei!9qD-R9mNGIK0X=a>45P^G6nYSIwSFiP5N6&q#}dQd|r;)x8qG7C)KML zJeEVzPVw*b`KNK5pZBAMvM4a?cGs4gh4Xg!L9BLYi$i_IA*!lsGEVop>)&4&Evgsg z0;2*?Yx5HI;};x)wa`n!vS{&op1)7?h&#Vwk52Q*>%PIE-f5)#M)wG3s6&Tt_;Dd;)WuBLJFd31{Et>2sOapUp)}`!o%eF4 zUY}J8)7A;k>R2Xaix_9pRFb@DWFPDoDp395E2}9Iqj(>NAA&RPSJ-XRT;XvCgmqSm z#`3>zfg4rx7O^Wwt>COjemVVy6OWnXIcN2_ago@ALu1oX|7kJQf_4a%)z(rKBtRm_hSq7!GaVR8bN$4Yp^Wb|+Q8%g2)^FAAy z##uIG^cBf8Wu#B5sG4*^AF}W>+4{2e4_?Ubwcs0_*;(rD^!I2~!D_PgYkS;c{Rfm0 zY{Q(w8t>fWxYlCM+8AdES`|gw#Td~rNmWde7i5ZPgddL7G&s((Dz0p1t5_sk6gQJ* z>~ziCU9lIopTI2dy_V<4aAkG3+19s~ZPRFVx8c_}mamelIy*{qYk|2Awepo)fOVa4 z=V(_!n@qUph}nUfj>HV&w!o%RvI3RtfRD%F4SFuYI9nATykDF^$U+A&U(eEXo+5wb~%`_`TC9pbhSvZ;&uJe`0z z^x;9MO<^@i!vp+0nHdNU`m8d5=n^D%g*aiW|Fi;OjDz+K-xD}Q@_^}3A%Lchi}qO` z8X45+fauVrfz+i;2CR--2T+^PI+WDH|^{J4jcSM)yxA4DT9_nT;8anI^z`!oI=&qqbpo!G^*v zVwo|ZGNLjkGbA%5GftUMnBOw0FiaVxPcwuY(hc&2y}_oys>1roS~dqZS5lTZxfYbV zbV!}KWGtMqN|iNBl})C|)v0oI%bmG|E}Th~EOM1BdaY0T{7dlV9VuEQC^a{Y{lpmL6f^5?~25FV} zb(!GVCzpY6nYb9BZ3^s=^$5~BDYOgEi3>#w>OMiVOJ{Zk5r&OrXS7zc)DT`Js0n-d zhqNz&2K;$~$v|TU>^kMBkM9ihc@o+{WCr9qsVVT|2**HW1{{9Ovrmu{A2TVXk5Ci5 zOY=R@5$*k#CY%fvJx(f2U@HNZ%OvPT83I)vU$M_SgU|b~%%}8@w0p*cI^;L9M1U|( zQGKqw{lHl3664*~5Ac80iz@=O#(4iJdd!CWrzH1(tQSk#Ik>tgyEvH|+Wv=hoT8$u zjBSeQw_}qSI!&YxE-h&Vh$5_jLz8M&=mX?iDwmfO_Q7S<2R3JIx2?Nuz}R;=jo6Fh zMr+_YhGE-fBc(UsdY#4mjPU2@K9GfR?8|hItv@{Rop{bRW&QnrJp$@OOl5i84GPnX zX-;IHIWCO)1xG03#4x-=y_KTqC>b0kiI&ECntnLJ@-k)NfmY|3Zt5I5t;)uUwyH7( z4XEx|l6H&N>UyrfQcXo%RiN*>QrsvEw4<9t8tL z9aXQJ9oNbe1RlW_#AwGI$wT4DAMUW(VOvhhn&&tz=e_VQQpCd1L4nba7hG{pGMZPF zbe(Op+_RZ>IN9~rW5CYtx8=gVU#N*1yfSLCA`r`^63;o4pak|5QPMGvp878}PGZqn zvpLRY%w+b;ZB9Z19F2Bpuygl1@fu2>Re?zvB}4^(*}lv5(|n-Mt1*)2lch4R)2OHT zG)wS;zaLo!+BIk}`buq*IoeZW?bN(kwO@MmXo8Z|06bgI(WbDXF_u2+qVo*Txr{Ik zoR!4+3%PQK90LPKH;M8(Yl{%b*SvMbp@l)nkECn{HKk|OY(F}zIeX4n>Bt<;>j^Ka zGu@9XzjYW7)N+=kilj&3vRChDa+Mip``BH(0>N?Z8$F|^i4eFydY{Nde^z4Hgr~k0 zFYHb^SBKJ{C7r@hTy>XSi=5VPV~?NB5)dm!GF=lyG`;3&2WM2GMHj81CM?XpnxIj% z&qQ8r+YHqzTlD)EA6=gp!fW%RSqbZ+F=+EhPJs`?}&gK(;h4e$58?-{n6qwUwY={$J`K&d!k2Z|HK#%p$u0r;TW5%HIdpT6ox5cLt zo7F9uW3zC}OMr8F7l$pU_aXfTu^~4`fDVYy}*$s~nJ%QI2|OSLh>o0>u_b@WNQgbXcPup=H+P!6ul36ncG+ov<=}t@K|#qlxTnE>o6U)C?|Kp->wK=d$BlR zuCTC)Um=)vaV(@Se++q^YmR*`t?!)ozH|U*CPBUhR{TjI`G6X|Wu*N^(fTR#K*}>j z%{w6V354w#0_h=Ayy+i-uro%v+1KWJ!tEJBaMDF_`2s)Zp)O%|$VG%GxzvVRZ)K2C zCG1A3geZ2Go7m}EQRLF~hLfMX34EmruO4J-A}%{`w(<=kXu;|J#Q3isiN|O8KH?vb zWCR5S#PL7&Nb;^O|J^02YAfTYB7U2>uLT-Hh5QrVg+%N(Vy{6(SP;y|=4VwSf{KA! zF?GT+bWACD&mS(|*P2$?Q_^+~F_qZs1yG?xmZ)hS1@n*JUo>UuDkWCFE|kf;=Y4nd z@|hpk`TOkvJD_67Yz2?Xx)84mTop%yC5_#Ntay^qf7-&Xra)={u@-`ka5qoEkByKATD8^>*nvXgDyOc<}UQQ=_RJ zbybq5Q_?ciRTUWW-qd|iw~*8sN7}{hln*VLI{O8xInU4^HO&C1QQf}MG6~P7gyiM3{AwV;TJHXhK!7z+3DjcClhsUtI#=-fx#y9<-**lG&u&=c-c zv!!XQ+&ItDx=MstCk8xYG&B(J7)EGi4CV)5Jmv>LBYt~GP7U$JNx}{O{OCDcw?aq< zY5cvKY0;ipx~v@#o}X9~mOEMxfht5*nmuE`e_YTkQgsFX+Ghs=7adHp(16(UcQ)@5$k!uCyn_S8yPY@!xC0^5n6ZSEWKoOhG z7)gR%PU0Op9;8Fu@X7|l_$Bs57SwP{_PFM{l=D~20!0k2FbN-(K$LU9eunb$AH^k8 z&7u29M72!j@wzuZ8dq(#<^3ECTwT@|&hJ$zkb z5RE>Hq-snL;i**S5B2N4I9KzUw37B2C8$3){=`^nzI9H4+Axo+XC%-AC$2a8sD;V} zyZ1mm{ax@Jgt6K{jEcd3vD8^N;wN(Cf??d@VEVR;FVp=~j`iL=6u)8F~n9nRP_ zn{^L7`bv0whm^NLURk8kK42vdQ~Z)ZFBXf|}7x2YN3+t?MVA1#6;XwK?!T zehcwFzHP{q` zGd${$^7=R7!g}bd#p{>Wsc*&yx6iU zxDewPo7|-RB_z>TIYW(;3hB?a&S+ITkLkfrHxF;SVOWO_Fl%4x_Q&xWI@ctP%d!Fj zBJgV{y$7W3Ysy&fo!8IIplx8YV+CWY>ET>bJCv@>qKupT|NoZM;#x z>%jQEqwr6?3!mI1n)y)-p?I(pU8RwPsLU`Y+8~J6M*~X1n1Hsq^sIoiyu7qXpJOSl zwS8>JM%q;kNbO4PT%R5AJZZves*Fr&tO%D)jO=jsl!gY36{&-UK|EL#_&JG#cD^3S zHi$QAdA&d|SU63+e9$Bav|!y-aD#K^exyf-W}l`i?y-ut69t*V9IpIOoO~?8h!UZ( zLriMnwaF4D40c7^P^zM2{17>~n=85UAh7X3MBEU_{NXFdSv`J~C}{9q$e&DjKxWP( zuC2+G29Z_Rcm>y@7VWY_X$%ZVit2XJh$k_F8A%vufB(D&%eKZBK$5H}YJSs?@pqQQ zC{}hZ);R1Q6Isz+n}8kHI#*6ybFtD0K%jzG$({>M9+o{b5|!5$<2GBGbX1$kFWo7t zT?s3u3~P)qHf~-~>^C-pX8GOt^IarMQ5#7&UxqMQWvX1Jo(a)bWgMKwIIS!o7PDfU zR8xb;GnF?fQYH(f%JkGODo*p{^h#E)q_82bjfY^H%qMoM?2c*4iX-X^WZ61btJ9OO zqB66p~ z_R=D%Y}zu{%m@AUbJ)6~(-)kx#j&l!uJ8(hy!&^KvVz+T>1vF#jDNQl<<_KX26S5_ z&|^It_BOu(d532|%nmyG4j(5SA1_RSSU2I|=KmgWd0;vz&RTTHteW`JAM}LtY|R&m z(s3SZzxsie`#vt9^PUa&d4kB_r-5d)h*rvtRhprQjhQFG*Lc4FF<{#fSL>4dX;rWv z@YNmZj{3A=i3fNvqtp98jHRH{8qfC#|QfNT3r+uTMNX zFg$)Q>Nx+J_Sj5yHfKALn^|+7VRzP(wVIWx_^T^?ZYF)M?l9)GXxl#Dv_#89edwD< z=_3G*zrk~%Im^Y{I+rBQ2a|g85p!aM|GXW#+3G=iu+y6EY}vnk#dE5y5|%mn6`hyg zzn-5=!Cv6pwcOe+f>pU{&2J`l^yLajrC?3_3!|$zGtqE}UNp3+rp4z&|Sccb28|;FG&R}sKHhbR?k>ut`v>60U zRB6fZ)n#T%nn^GGZAuUId7$X(fY)=qIB^X({er`f@1z)T_aV7{Ao7=A_RpVrnHSYY z@SD2dNIg=`K2kM1Kx3i?dTbt=q|VdRmK;B)9Lr5-0v&#Pi`su*euhHbjX0Yl=I2Pw z%EOYHSo|sG!I1U*Eal;oTChnf+fcWYyq{s!qpV2j;bsZmnyP0bUX+(++HM$E5q1pt zOR;!^^Cf77K(Q!1@kq}69?isSWQXwf&Le>T-6DxLCbj%0rIM6dwfBuqvLfy_(L^dB zuaA`z-ISwaW%O=|X;gW3mtspH6tZ1N_z%Spx*}NbTHmY>WA9K2hm=p^Ghf4d&SejYH?n}k$#eMKWjA6Nsu?X-FEkG zEJuogh$A>L9V_Pe{C0mt2oYEJNlMtx8?qF zb$31TGIw{qbThm5{;U##k&!Ux%;$c+T>8(v&Aj9}tNDN4*y90N49}_-^Q1or32sEA zn}np(m%L^pYTl(Q8%BNvVU&3EM^9>~I1WcUb)EK~41ZN3>fi2Ww)K(>xzY>mKTQry zVuU`y8FrHlQSaYLQ|UbwqSr;v)m2%j-qWMzw?1>WVVbIli z-^wEFDBRJa{Z+dwjk=*IKgY+sjD?riiOIg^W#?mXbOgAY2n5={Cf)@h$zh#k+_t62 zmYT~^QN)`pa862SpTsT&RR~<*dwE$HeeU0`F@v6H%i{wP^Vbx z33nxS(am!iYJ`D^Psk{nq0ve-hJ38-3Au}Fip6$2!PJ>iT41>2*H8BllOq#J7 z9g2j&K=b&J@7|S+{$tSQiRjxDlmj$v6N`lg@eXacx|OySeaom=R%EA%uQc6r71SAW zPz<#qe!-ppth9Ki@+;nWk{ZpDVqS(E$hcA1j$>b|vEs~+kJDu|MB>BC5-o|nNo6o$ z5AD5px*QJTPVT!pAG+$u<-Jh(SdMEFGaOiQwA1ruO=y`J2^NZiL3|%?m>M+?py8qp zVq|rh4pzpGM^z8Cv%TzhEd1xgBqqj=#u`;Gw{!lOWm(tmPUM_q@s-tTlwLWn##o9* z!BkaRZldh2*W@LZQRg;tKF67(H##~qP@azqL&rlYgHPj+x%C#)YYWsE{M8^rl9;rD zGbsfn(kHgdSl>c|L-9}>q!?j&G*%c?1X!OU7UEX}`%=pl;3KO2RlVbdJHRxSwcTE7 zW#F|n8?G%6>L^f|$YxXu)O4OF=sCn$1ql*BoV)eiLfwayVKvYI*0fgo5{7Fcz^XkR z+TA<&ohwuoW;q4b%fE1I{3#+#9!JUXs7NI|DF0AuPg%ZwmcR;YEHXUHvNf>zd#AsX z`w#7b4N<30qeYZUlSiKN+%1xpG0M8JTMDaRzF{5xY=#g@fO43jgu?;tLY4bE7;pKc%u`ntb6G~%Zb)omETT> zR>0L;g&beo&De0Lkc$6B`Irml9CS~?GQr2e0VKs3u+1SmX6_JiwGf6BgmbjX*uyF9 z386R>7Qa{+e!EeRPd*QhVO4%{JRvyE*mFVT6EsS05(VFKLV3>7@LHSpErnRHBME0- ziOJiApY~%e)dsR9802o7?;?F=ZfZi?8{G5Zfw2 zURCCKW-0I2dL>z%Yxey4K@e7a^*~2EB$y4&cUtR8x8ok0=q6q^c~jjTI%n8xJzfG( z+5cOl(#42wia&k@$Ti?$6y7Xuz*Baba>~TG$KBpE?(!j(G*K?%CW|i@C9# zI1Zhv5Pz7#0c|0_mt>NrZ+*=**h>277IWF6tV|V_lOsr;Cpxpin>-R^eOp^-Lyd2b z&RDgMba*mIH(DjKhq$a%b74K#VJ*EgkW@%?Q^3;_rP~vQcDhVm{`l5buGxQ$rV^=D z^Ce1{D8DkaIrWYE?w4lXO;ZGMpR1{#m*|N*%&AFDwP~Sq+^k3)_4>T#>AKmhgsv0H zf4cc#ec7!G)gsOHMLKOa7|ktuma@kKgR-#Pqzyevt=nvKaE8abOITA+R4OKI@A>0Om3_?I5jmk;Q} z(5T+|zm?r(!|Lfn>BZ09b|uTw==O6#FYDi zD&kVJ)}LK|&wPJDHvbF(e@YiZPXM17nYTEf07uRyyb6K)5Xuntfa?db998g}D%Ukv z@8ugDWJQda4)XP5Pn9oM$WI7 z{y((61#F|ik~JEp2{UKH%*@OT4l^?|O_-UPnVFe6nJ_ajn#*3sj{>a=gr^GTq4*m-Nh3=sVF>ZdVN*e+qB&`Xc&axPiS z+`dZ~Tj?#ycKS0vYRt8LSGw|IoQnU|X-efUM5zWR)${|kjC{Om+Hcq?y)J33LPg0R zkk6l31sYJll0ivWg!@5JUR<=}Prl`bNd^DR`&IB-+rRyU*xCJ-G zJmMsZcvIo2F^9pu9VI;66@(z&Lpkd}>uC_kfZS=%+xA1%kgvCa99i4RgD!9~>PqwX zTW#0M2HS=FA{lL+6s!*80_$G*|?&jlr9V5aLy9I!Ld0au~eqOV1XRyj!jS=8Kd zDgc$=3GkT4ztjeK=oj0s)4^g%+%LOYQ6_?GC!~r$QGcN%epME)hzmWOXK}tdP!E6o?wbe%36C9lVmQC2;U}PDggZL zHT5aeuBTPmHr$y3xG<$FeVW9~bfZOK@;O)?w+7w?2dH;oF7Z|nc5A;BQlC6LYU9Fa ze!0i#Wv4Gf;|h$l(H)c0IHn}LW=S|eU->`%(48UHzM=t-7i#lZ`&ug4l!Kz&k|d@@@{XxcEl) zr|CYx!ozXiIid}YZ()M*=srloY2iO`qRY8;&2H;8bFM9s+9$npPMb3gkTIb%FNuqw z+nb`FnScp$BpKhiR$aJ7XTkdJryT5&}+f;n2< zF++Ho`4iV4??P&yDvB1L(D-&w>zy4DiS>nuPXFI5{G2@0;MV1ijfROfIC4xKEXUC` zth90a>+r9xL0y{(Q1i8efd8t-`EQFtME((N5;OZZ$r0y&_X?r${D@yTDs=CA)5o|c z7%ge+Z=8rtZB3QhX(-|W)b~jJ*&%RGsv|>TV59QRDO!vPCA2CdYJk@Rt3qI0}HbuAlYIPDa|Dis%PuR<#%04(v04VttnwO7NE zHm|vonrChQR@kF1MpnRxbGKrw07{wQM%djaAS;+3Y|yCnMbR0SVKawMfkAQbqQPUq z5By)Pc3euE{(oV$xc&d#>L@|xyn!jHBX1~FTY9?6RkvVdL)fC9)mok)NM`tSa&$4H z(>>{41}yiUyVoG<*xz2jzypZ~Sxeh|hRspJh4E3}e-+8El(g}|#c(-XRj!)no^EG_ zSyf)xJcM1CmCZ#lwY!l3N*@NL?4pCDH?CXXt6m6O4a;_0?oe* z%7|JTIse1vuT|jRhdMTKQej95J?wK@6UEdqZfE23wdZll09yQVj+0 z8J_GPV?O?1vwo5odH_QKP#0<6jVk*&ftf9rQ ziJ_21rXn*Umy7gB#eX+o|MIuweRNn8fP#Vohaz-`QgDZ2kbr7S zSw4PGQImjTN64=kX%hRJGt%Sg_xU9Yag1ssFGnl^#jqB2yd0D?^WJ0Z4rTvckgLCi zihG5g71|Om{@Z#734elw1XSpIk?Y@)ppoMkwZF&j+qwmWehI!!0%`=L!6022G4NR- zu7i35V-o`t10x_{F?2ETGw^gm0<;OxMH>Mb1QX`Q`yl_tM2_5Xx%{s=$?*kG82=Y0 zihG)vIl5Xo*#F18oc@}Z$YGy}vZ(=B=r_+8m?rWu_H6cIq+uPxPJT6%O5pTX(XZNd zQo1i70x3>qY}UxATgUk>6raAVXF2in?H(X+>vP{@0g3F1ZeMJ{Y^XMvrNAeGl`0!S zN51Da6^aE6Vvo~K;fn2sI&HmNkxx>Q*Ab`O+mr;V>t)zU)*E58vZ}c1`tB>AtG$Xh zOp80fym^f=*UgbBi!Bdmq1xknSSinxz4i3drrq@@PTWMf;;`q>mpGGNd}P9OiXi5K^`U(-zRPC zCwV3_!Q)1l&7Oqn6V0O7hf$a|bcfjLoBYts6XFZ_?BTi0$~~9NE5jRwhltnk#Y6t| z(NauUBZgLR=ku5Y684FhW)|&{B8Atd6N=6g>Fti~BNm<<#5gA53G%-mA7_YoeX z66l!X9#rhK2LC#pTUvszy1x<}tSJ8j4E?9Jl-NHcrv9HW6rkgXW`_RJ1}hEojXpF8 zeM(P?5t#}-kYsZ)4KoQdi$Y!py?v5H%Qh*?Ho;pG7>1?RbNB1li}rZtqB69F?-FOe z@AFR_B42k2ehW@{BOj56lXr{$&+Wd?oX@@d&yOd>0Pv0z$!`p=KnONLibTW22hnkK z8*0WyeQbYEr%5EDya%Jrcym+ViPwaCqp|RE?1^T#K=$NO^yng@bRe6JhUQF@XV_>n zU7iV$FB(g;XX20?Q$wp~<`4sOO|xg}kR7wW*)w=FTVMD2kRI|kDd}*p-a+)v&%VCh zgXqHD!6nkaa4DdW)ru!gkgC&J%ywNB-5{Lko(kAn!+ezk7?yF=j`>sMZji0Sq}526 ztjXT~V%a$qB?2k3mP^d^4!0h#uGv<_1%?Jx>57o8IP{6D!I8QYqF$Er7mn8NdOBoEVDhhZ2M>6ED61~+RNPguRyi;4I8Z0<%nf6)UD|u2Z{E0dA*@(=wRym7% zvG$;{w^wB&>Dgs;wg{ApfXTzWVQcQqnHg-dRPbA}8mawXkms@$(~7E>qA zJa#CpzY!^UF`FH}sZ9f95ffWhcb!asmWSZ;c&`V#;@cXCso`n8yP72txHbev+W%JK zfTZD^7cKkyiXouVl%`PM8PsmtU4D1IYx9ey#EOg$GMbtovsN>dNy^kNA%#WC_un2Z zsXwIZG8BLLLG1;1b)WhOmaC(TfHLkX1DSRo55uCKZ~LN1Uk0MHX$ZyBqmX`zu6(v* z(pu^ov+2%cNk}j{RK36uxr`}Bn0Azlj#49D#Xfw=7F1|wjm|I9sW2?6J(vVk;~E1K zqJ-hSfR$16Ofy7hnhAi6+9F%GuvTmv=G|D}wbs~S%qUJYE2gn+PNUP<7*2E(rg(bL z>L*Zl;edgeD66N0(sF$@wcO-a9jP|u47_p~F&?&M*|WpFUmd7h8<%K&n7;e|IFsS5 z&51K*Y)x7|eXM5XHo%n;s>Xvuc<|j9=Su8>1A1i8Hi05z@un(m4R=JFfBO4*8?F1o zO+l!?1skiSI9~1BuWG2b#&p*#*(7LK*u|=auA&o;lzRGP_awq^+mWJVfGv>lpTgDo z;UP8ae|Y}#ZMR$-{2qJmbF|5hPVrjKv90<8hNrWsD}V1uUuf|6-e;O`+vS8VHU+xG zi%O9|+@WAyhsv8ZPA606@>d8UB;6JEl~Ovg5T$}r3Xg5+^a>P~!q7)5zX-&s?JjtP2O*s8bl(@bbzutSX-A+5vkXYk1p1)~G?A#M|-1Z<@0 z12gR%)aJd?yZ(dL(<7JN$?bhC?a8|Jm6G^lHD%n(!LHkf0M92{OVelPqUX+0 ze}J39rvv`SxRo9*c-%6eZiEn=C{(LI0Lr3-H%@9to&1cM?uC=iFnH;cGI7#NY)UTe zofGb(U){)=kW_>m2`14WEmN#C&o;^XE_{NF=Fv2HIk~@zDN_AHx|lTS5G8Yjj3OJ7 zLMC{EjDmTCj!F4R%?P!6#59^!C49nEEvJrt+NbIvLQZ1z+#2=07?2SYwbTlTNI}mW z`TEvmZx!T!cARVBl0CoP@{s7FX2C)8Y0~40oEN7sF;;Yk`B>SlV{WA64V4m%k{c_{ zyZrh!(G#Y+7n2N?lyg|)lcqf?itfCmf2mnAk$swc{zxy>Ya6o=KOGAX{;c+ zf=O(PmSc#J!-f1*Pn?8IoYWuu)Wo2`5~BbmPP)G`qJSf8t|9#VFGQY{anthu6|ngi z>9<$e0DcsNf~!2mEvlyyhYQ?S-mD=V-Mf+S?|w&RF#u#NBPvB-i>H|$1D*1WGx zTXi-!+ABIanRQ*5CK$tBMC3=pOHZ()RlnKgR+QCMv(vV|)GOnjH(BQKBX>8NOL;+& zT%{&XtC230GwDy*W~v98bH@r>I5sz*0i>6Y(?V46)NhHM_TJaYau`jhX&0|@Q;2TJ zn0G(Y%Idc3EY4kY5HkpJen?I&N}1@IN|-8`CN@0Y$bO&k_4u4J28Z>R_Q#+b9H1Ki z@}l2Ex#~N~e?wS2aiNZj6+(N%(kItY#|cHwZcW_M>~I>vEfJ@T$AXMsqB}GxT^gAt zG|aO@6C2gtM8Py=iF4S_mB(qCjl4j|h%_#X+9InT6h$Ma{od*`lz>QH;lo^~UmrCh z9fPiH=R4dXB87_{;!VjqQT$hIL)`l->-kH!cJ7P3!SdhX*#A_%6g6@;b9S_Jus8dU z7#pmoE&yZv^Gu$_g(bJ_suY5rCU`Z;bhAXhMGb5eb&-QfK0PI+gz4er#EfOq*6+T| zDrz4LCh8jvLyEY#IC@GDyEu)kUezij29}zxl9TW#B!;-yd@e7emGz52&Q15($93mf zuFJLi=|uDMHnbr(eaz6fPVvNnT}=Dh0d9{d((a@u){%DXjd@QvP=D+VdyV<&i(9rm z(2?kbcJwy}iAi9hBNIqG7LG$?v_14fb)-Hvj!k5-J=T#6gdL;A@nCvlzBb(+eL*)7 zJX(s89s?cQiJ51*Ho`acO><&A5*Yg%1C1Syop0is2ZS8M!U@MQF!s#^VvM1TY2X-` z_~rr;#)4xUSo%g^IF5+NJh1o7eB*(BW5Y2-O#TxuC`Tw`@i>A;?ckQp^wW#Bo;Tlv{_|9~?oSVlXHqA}jOX0d`v)F|3Xkc>OlSaud8c5ZN;y2J)a{u6H6&F%tvZA^rhD3nsUi=SY$t%Yb=G@s*inW z<<=;Oy}y~wJFS^ND9mNMZx!{&8exr@m?}%bYsmd2Q9~=%h8w36p_|^#Yf8WBObEbU zQBE=Jj26#!hC6wHDO7)LGpfC@!d@+HIo4uOKzQ*WE7PYVlv51{~GU7=p+2v^2eQOqds!6y-LYO9eXC-!`95znyIs+^};MWjGuV66dAUTBm&kP+tMrhvP8G5fPY z_< ze4mXu8YHA{{g_iFIfeOce67T5W481Q6SnwMv{Rxtg2HodB4>@0Krhw#pa%Qos}1fo zk6zD8T{?5tYo%?*k#a`oZN4ebGW2<@cTXd(Yen>(j=0UD;V^TCiB1_=(z-=|9-Ql+nM&~;P(6kFQV9*6v2i5P~Boo@ds1$=f| zH{&=1Da8gA##5fpD(jKIBCx6lNtF zLXTAM`he`RG}PgRDL5m5k+p#xBR` zIRr>m@WF$wz+uEPPenXG3!KHX6W)FLITSK zptNSNL{2QmuOTbVXoVV4)Zb2oU5P{7QYPS0zY=da4-wWwsvB|EjKA57G1?ERZxg3^ z6eEQo0hF@giYC#osrO?%mH`HYJrZI_O5KpTUi>njPs}oJMA5-W;Am2pFzZTC#l+E=+K6+rH_@#VVIPS#E zA1V6_N7sDi(7BpWLz8l!#-`J>c26_dOLB#??7Y=e@5HN5f7Kk2XOr(`rkT*Vi@L!FlcjPDO0R85;E)c4dFHm@llHFAO+ly{d6oRh3_2TI5n$|6-+2V~o0cHU6??0L0=ixvjk2nY@gu$N+4Cq_(PpWsj?@eLNrX&1+pb(W@G zu|8R6{DVb_*8wIW3?-9+HsHT>8wC_tuo=fePW!Y6(9tLTb7^J&% zC@X54wAwr%I7mK0Y4P|`l~|6+n9^vo?}VGZNoqxddmt#BTYHqbS!!5r^oobJAw~3* z6ith@8X+HQtYm0anQ;-M?KhApTaP2<))28DyV$;o5hoe#f-}a*Bx6UypC`tc;933Q z<8fHfwE1yXgjFb|IM2cDT(m~laCmS@AYh3OmziYrGq<&z|>L4cs7#hFyK zdX+Yb^F!)EhlOA{OI?4L(dB2b9=kM4N!*v}Gjq6KAoUHm8c=9ll+U<+oJS;Mk$ILR zW}4?v^Jk(VFhnoSX`l_zR$IkoI=#jd=U8)uR|9+bSFslzyT9P=_PBFqYp%93W=)U_ z@-fG{)1l{>x=ix)yP20%&H&)U*P^3nImt$i#TN9H zw(L0j>jcEPOXhhtb`0A}zuQ(VeNtVF{mphUdq zYXm9+GW{_a=_7W*hko6TB*&)Kj5U3et}uBP~*RUQ6xWIVJFvg$uZ%zKaR4ajqT<&Hry zd3J|wLU_!d+0{-E&_5ORCf*1pzA-c*<#RcM%eGj%a!7ynVG@5p{3}|=VOT>n!v_J; z<@p~<+x`=+{};FN?+&F`8}>^TtyS>Mzj-?(FeIJ~xd|D@FbS3y0tOj-C;^@ff(2Gf zDV!LGI77}DbGudBZimyFeNYCS8PG^$9n@N+UR|qGUAxk!(OTX5dby-txmw=ma{W9z zOBS+r^1OThd~d(UfA%r&J)e8KjkU)Q2Xa4}CFY4Xjk=FOr&vNUFbieR;jDTvCUeb6 z9)wrjv5^DA-4U8?{egVkic7fS+?<-tV}H?UwO^K)ZMU5>GfTalGbJC9eYG9ZxiKh@ zc5ib^aX7ZL-JyC|CzcUM|L(-eS$@A!%V(X8ebT3A4E<~&b(yk0IznJd@@#ID|xHOnJeG#FFmOJk%hcOYn>wk@Z?zBkQ3A{zhd?_Us&?CDW${ z(wO_Uj@WpPw{GiF0=*+SrM78-c&H93FY@|*k=fbgPcoM#S$?Pvi7)h#?K0c*S=VO> z?b1DZhoF%jQhZcEg-CGJetIB7q=#&ew)oLpd{1WDRg^YD8! zZjxt6I^jWZA1zQhaxeKMW2Cs}wqt~nOn@5rhMJ%B(lSCMn=j=X+5fJ2s7TgF2gHaJOnqUEte1J`{^2Em_#5>HF>sIUqkF_a##i((8+9+~ zg*LK3?FBEgKlOz-vOoQ$ZA4JYSMrdEypJ}K?Ue|1Fa3ow@{<|}9!Vtg-aOJT^)7Wd zjv6ohUM&U&tQ_h85{x+fA^omgK%yE&N8MDKK_MQeBj*u~ zW1kqE&{fxKA!$>>vPf{fzCswQCfVh!-HpN*XvwyywTXLu5#>tU(Wbughc*bteDwM@ z@3v>v(e<^ZQ`J50{C4j^lcmKX@X6t%XA3R1MVVx8d)B3H`Xl zm0vrNO>DSLcw7|Jts#rlHcm+z5@iT@LO$mJA0ElhW* zAd^g`XGznM&Cdwx1K%4{4!2FLZcCV9aK}reVA|a!d5RD0i$oTOU+PwG^PZZdEOfXw z7sc>(ie?j=r3iA{%)mc;G?h<7k6)C`+;F^bDywN&iQPvR15V;#@Y-f)vLM|p-Lx(uR& zLH#{p-8-y3eV&M04|%@E*#EsI$y}OVZ!i%wjYr*fN$WbTuJ+18LVH72#!Hy(iUf(| zHEmU@g?F9cy2Nq>)A_w@Rf-EY!u`Ru+7r-QZKct%wB|=*w7v^b!@E87Gf8kYZ(zM6 zM{jkKL8P(S2kZXawh(<0RYDVpbcd7eA`26n!|OA536nGj z_``d1kNXGgkj7{J{3mZj6f)eH7~5EAJqwE$;R(RGGgZF!=_&`?_U7C+s>b2KhSN;V zJ#*TY@0RGc4|+HBneP^h>vXX5HW6BJhz|+Vm{4TnQw8?LxpVTUJV$LL z*ui6j$bq;NVJ&^sFEqFl4GsJ9K01^riO6J=ohfdDgE>c?5W3W*#Z)xAn>i~%`trBS zIH@={m}s7GfXpWPue*p&<2>kcBSf64M=2^PUz%?VbZ3eVg{wniHibP z%M>Wz9^H#U*wwJu?G&fx(8*{o^!>4U`CyVt6?(?w6zAqpydq%|3GucRs0V`c59E{9 zloo4Q*}wkX<+LKU@GL>xx@)O#?}RgErSqnjMM++DCho3T6-Gk8tvj^N2BmBM`qCL9 zW?PO^oQj;wym&c4g6~K@qp5rD6L@AP)||w<@^`NO;Yh6%cC%HoLW;RyNvn?139qk- z>ENoWv9ESeMf3n{;|TX0Xy&cmMxvv~ApnGvR#C{Dk={{u%N&G} z=17P~MG_TRyLdtu4NI+k2^%BUVucd+@esCof?Lf54rzj1tBSXYDvs->`jy=$ZQ?!C?6`@;e2sVSX{%^5DLLKXTjEx8KDzeD0(Q_WEK>RFn@*4Y*nKls8?b8XEfW2X zzlY`P6lT%b1z_Wy0BqOp!es0*%sTVO z=2TvV;5+~TG-5l~KbFW9jT&IKvYRok9IM{@NVZ&(1w}Y;}ogPh&2hL$(4gT0`t)0gCIsR z4U}5U%ZBv(R3PoTo+r8zX&Ert9`k*v(nYMPNyeko9cl%QG7LWODUr!E`TY6B};i3DU*O;HH$Ish! z*-b;wh>UH5CKt}RFE8ZzO_vG7AH2U5cmpCdfMlicA%9efPgeC~{Q9%P8+z|~d7c=z z>!b27+N?S%y-L0&6NKizKlTWC)ZPyjUQl>zKe0|QR6}}SA=da-Jb@5F)p*~vjU4p_ zd*R;h0UP)rr+)Jz6*4>wudLQlinE{NGBD<@GsedqP&s1YW7}Xi$Xql%^Fe4gXN}Dv zIvR`}1mOEjQTXve-Gv2)$j8WJ=)6n0!3pb>yV7o+&_+4Me*!#*voEkA=J_wEfT--5 zGT*;Lg1|!Da=!Mb98;C}6FiPCzg^w@w$fMd!+Eq@Uc#O)7BKv=Cv;DnVj9r9tf4e7 zn5?ey3HF#>@gx70#^=*~h$;4!etEvIFU$LtC}=snvc9k{_*L8IQ$5Q-$dBL=x}qL> znhW9SSt3hN;cv4m;=Nzysd*shPUZ!65KufWkWoO&gTjF@1QxOcdi+UPN=APRDY6pr z>owT>S`G%XYrJ?52#?3SN?yax2_-54# zmRNj@@OgcoK%}nA&pn65PBz@oMIX^C5YU?_gJH{+?qCM_V3yVhIEfslFepj(0hAHZEcGDXptBT7npcKfUT7S?LEq&I(Y zBH()$lC-UMlbo;weMA&`j&K@7JmJ?f@CI=V zKQwdDV^KPPMm5flgmy66@y-c2>#w{e2Nf1eEl9K-DIZm_X1+T8TL*RnGp8=$M;})# z>*6}fLenb*pFQt=IIC$I*q-2gUn5$~^;h3%s`1HE0hJ&v&+@n=QHf8FHo$?Z&da@a>p<0hzODK#g8vdpeHbYe{idW->pnMcjDV&l{$2JBz zLk@sFK3MAd6?^W0%W-7w1`V$=L-xS4k#AG zzM}Z4ic98e%FV2E=Jc$wKzJFNF*1~9B{TPTLuNxU1((pyMcq<&N-Dn4o=Ya#nUz!A zR=&~A_GLOAus(rZaXQ^}>yl-D&q?r(inIN*6Ftsk`^DAim9K>&qP&FgERKuCb zL&=7er^*sD;VuiZT6GC5TlgIaXTy3OAaVO5Tv5^cR#OFnqa-f~4580X-0zSY1LYl2 zMV%1I=e=nX@o6MuO1nA=8%-W+ZB#QXb&Rsgv-iM&_1v;XTfW;P$I>~Gr1ua%8*oEIX zO^ul9Cj2Q!9P1(FLI`06DDA`4>*!~D#8>~(=#`Q%e<`|OM40c$M6ylW*++2iNSwfv ztq`BQ;`tc$Kq@f;Vl+vhSbM^jYSiFujiA1Vk{uuD($hJt-lYnmPOizsEkGroPfZWF zXGpK9&I$>%m+fz{LMO-*^b2;og`dol5D+HruR{s&Z{x| zG00)gKR!wU+3mPq2WWiRKxSPC5=o}%2)H}fl4~BVQ9mt_XK(Zbj*I~r`V&}4_fwhT z7;fcypv?DM6T&$YLVS#vccm8nl65~QDoPI7Bm5dL93qoz^kvoS$*b3rR%|9IimWVZ zqor6VbGb$YD&TVOJ~VOR(%=pI^~<9!O`leCz-Uw9h2NMtZi>>gUe&_1UYPYf$CP0I3DwLz^Ocn@Ram0Ek(6Qmo+O>Rb(kfE~z8!VSt3iYx z9FscAFrJOX*7(v=_Bk-~0*s^x%PzuK^n+O(C${awCQbdA;!S3SF?Fk*l@Jy6P7#}> zh=Oz~X?)?s_n|!@^2Q|{Q3c{~ULX|4DXGx}V#^O$*W>07yjuY z829rJFy3MGM@^iZKC|N9)?Byy%Z@(S37)BWiuI(+jHkr4dJV^DyVh3Up4L+y^a+=K zRlIS>UddC=AcNigf@DtDNB$NP1KuVDu2T|7zc_$tWmwr55|XY33mXT-a0!DegvHm+ zrBRb@F(yN1*&9by@aBp54?>R64~Ke9nh>y|q6x$(5Y=;yn!{RQh*3o;} z)#@m4O)Ci_3vMMwKQoC)J>!D5n9+Flim+yeQ~aeTYRF zBfL@5gl-i(ntjYT)=6e&_&{K2;%U98cl?e^K~3SFuG;)e?wDMs3{^=5#}hTdP;BUMXvu%cIZ4;nr-9;xGMjVIBkz(Im@I zwVzO|;=Jflh?-P$0e~Vjj%Y$lWD60g90U|6#2m)C;ONmmj-?&QPbxdBv21{3Di^a)?XRbm;B93s6O+%mK8^-p2nrS zV*A*n+Z%Vfw!P@rS%x@J-#uJg3CA~4%kj-QgXkp@VdkYy(Fr$%-b$gTq` zI6kzkFsZX z*S`txyEqLY z)PzT~^h6(v95*WR1fz(il-An|2LqzdU?~oJ1L0dD&kW-|K^c%42gwg99YZ~F5F_%( zDt{NKuL;-I1#9a;w|23#cCD%)8ch|`ebh3$0mh3ywvO>if$R~BaZB6v?x*oJ3KLqs zIXktI#q-{Z2tv(qYA&W2+&kX-&)Myn?b*<3))7mUZmK!ensOEjY2dS zd}z|u_6fI#_9J@fOu2TxSUX>`RUqEl&-`5W24(9nZqsYn4^0wS4omWis1-BBj6HRI zw$Uklk4z7p{$9;~<`MtGPT&v6Oj{NLY$z^xXfj*{DUR)i8Ek0sG0zItq{9)*1!Fc# zX51ER1nn4GKgK?L4gYS*ogF;mIAuHj(rad<%;zd?n%tW}D>Vm;U*6qe_t0j$!B3>B zR-&x7mx6)1RSe7&!=9uB6gaBHI};)75}3AZWrLU|JeMraM+^%)+HL=AuV6d1Xy#Zg6&azt1H2cZ6cu zR=B}=Ya1#NbH9y1m2Y(lU>2uE=SpuT%`sz-m54Pyt5gQWgQ6U5*A1IcW7HoN7Ux^% zPje+fe>{NGIxpK3Vr;LdNq%YLGaCI({haA8!(r~2;r16vA^G%q#ZHRw_#_^GMKM#6 zcBE4OTNGF=-*C%BACU`>$+GO+Iuy}+Rx^0Do_+kS?$vLXEBCz(BPqrbKL-PM4i3R0G|UC{ zbi>ho{fB(UZ}~dB5Q_@zjO&v#u6PQc*&$)iG>N0$Qv$xOBM5U|%46H!*q_jtjN$|?bjzr^GI!2H8D=#sGEIoX@o~Ef^)eUD zn219zz26C{p=w^=ruWR^;tFYnu{8q6J8;Xe*9O#%w41I0=&zi=lspF5a!k&uwQwhD zGAt8hy{^zompfk>b!XcWGFJ`$Os33hH1NMIUo!PD!7w6z?#T8Q$LOnw5Q*Q@r$qFL zOaD@r5&7#RGqG%8ameS^7W|o5=x0oNWfVf4`DU0C6}(kL#tM`7^V%6}(uY&-dn!`5 zY}9F9(GK~jd%D=in%@(*o4Zw#T0-KvC%iLm47V`c)z4KQs_PWV$sUV57z4 zQS9+izp4unwXBJpvmV2O(0N9zh!*#yJYw7yO`O&gq6DbL^HYixWeU6gwx>08I)N<-)mZk zbw!Z3FEb$q{qRGeG`ei)@EK|GPcTcz5^J4IZSXg6CELal?T+?;(%^QY7op<))IXGY z7C1&F;pQlU^62uR({!NHT*2%NORY9KvDWOwRj>O;ceBB>B}>8{(lHj{7GxH!VxBT! zay;7(+bCEr-DL2+ILNDMRBU)=jp?3!#utIr;6J)}6!E@nNd+Hn6$mdlMMn-cF(i^> z0-AY=oxK4;k03L&69vHUJ!+S1QM#3N+$p0hi@7c;dMTsks-5Rl&W^Y)PPnqM)sW__ z?nk6FJc417W6WKJ0)=WX)O1WA5@ypDYDm5!rPJw~vQ1aMr$O*E#xi#AHZ7O`Y*bQv z6dMRMz5=Qk*&}YRL^^&|uAsM7CnTrwlVIlbS_AK}S_3_)bcTKSzR3P5IhJ++>N<3SRCWdKtvS7Ll4eN4 zx`5`uMen<7ib&^wYNZZ5-rDT<50Fy7AiQAg|M)MJYgU7y!+CHZAc*MyLk-1$W-kAe z8u3q>p@xs6+6vBRuFw2V?}qJAQwOo)SJpDS=IbcT3aW($GU9}ETPY9&+oWt$t0p{m zPGsLe5#`^_D&j%nVy+miMXRx-wezd)wzX|bJgx4&ElY7rKhFhoty@jra(x`G?L~~s zCv#l-Z+Q2PJo@f)$NPXQ-x$HTkmh-i2il#Lf{CGhvDHy#h2cqqHmJKg2w+=(~)dH)3I};|uCAm)V25)JNThUc51UAo~?vIU}wmFx~+8N)-H|)M`F^&SIqv7KrDjPAl z(?r@stILZXhqxS;1e9{!HxBDDq|1^DE1s;oU8s=QjLqKM;^_|2`dg;tgBNVV#fKU5 zXhpA`63D(ZL7r$y)|n;z1V|*o3Wj&lCQ+`nbM26f_6YL{V?$wF#Us3c8z&9o4CUj- zmS$RR894-RJxFxCDNx_S!y}|Xf4ExmcJnPh0Ih8U$c9NmnJC8^p291#z^C>#Q>w5p zZF?HeJ0ck&qAnk6CP4%rcoM<3q2NSbQ>b*sK&29crWD@NQQAnpy|s2Yu2d@~-CWD+ z$|F*tJxLYOw&{>IFE#@++-CvklW7a$=oF||OJmq~q>*tQZM3L(@({4dq8pcpK||ob zOC5&~$WEeE&LYS*TN+D@vhdcRNE$7xu}xuSt}Vk-63=o*Ea%Di@|IDhcl>0zjJh;@ zXUFhcDiQ;zI~{H|3Lh!Tk9W~gc2R0v-o#v?Ia z!qBja<|3UQSO?;Y6}IDVc%9&pxSa5jSkPy3JdOB%jllAebtNnC#YpBXEZm5}#q<0O zlpP%~QLu%S7q++Dy%?e9Q!s7l+>M<5BOQmXC>xffsKPa9}xYz5B&JHI66kS=?B{xq2gijq|gI#8LKw>aaj)Okv8oSk{pG~^$`n1b;yCtAL+ z?3*28ywVbH8L!b1-66*eH!4L+7&(-BN$v;^CEhn)E}@j-u1EJ?@;4^ z=ZMuUs>A$3DAW(ya5|wwWzFcTaKP6isyR$7r;U}|FxF7G>AngH_fQU%5%np55#;=QBF zC_jg1#yPCJD+o%SAm~T+gVXa!U)f);r*0RgPuFa_-c9aOZEl@GTN8-WsUMxQHoH%s z(c=Ad``V;{6;?A>3X{BUmUZd`o7>j$vnKfgZ!oshowUWRKNsN6wWBFo$C&r{72$8M z*f!YaxM^ml-1OQlz#t_P3`#oMumvu0n(xldMd(YarHRbLX_zw}(J~xfHZ7@gC)1!U zWi+tu-j8AF;|p=4-y(CNkfvBtoU&$YsuTz?%=YbPcJh zU+tF^s^rz)C z`?cc6`s#!o{9wBk2+bTa&J=TY{=6-_9;uNyfG2fG5Ob(^_XlO{milSmMK}0!pNGPT zvDt)ZdK|%);i*{!-}Iy-l75K(Qe=Bd{m8uy)}w*GBf2-Xd*jA2!Y^X@(&ZK5i9Ak* zbbb)(S^K#^$zRIYhDq!Bg#GoSHgnyfoKSr3*t*I^0pa3u|5q=|Jn;tE6=8 zw&#I1CvkT$zSIMlGy|-<98)|t?qNv(KfJwTm}Fs-=iOyjb=kIU+sd-LY@^GzZQHhO z+h&)|uG*U2nR%aQc4uby%bVXauX8d_TqjRN+;RUSetRc((4B-egY-uX+wt!=mTvss z)cAwkcZ46h-3UIp*k>_!FfRh1lzj0!gE>bnx9G3DA1dDX_zQh+?61t8{hWM9jyLjr z_&K)*9}y`BFy3_ChtxNyCC?ao6C~Ty96Q5Sogrn~hVXoo4zG|C2O((@IlpYY!?Ol* zjKDrjD`R_x!e4CFp}u^RZuuGMzqqwVdw!vY4<=JUYn8XEC+!=09|5aYfUdbo5o;AU z)*X!(Rm!#bS1G)$iAn`lE!ONz#0{uA;&)3@mxWI(cZPyBsrFov8e<4n7Es+#pP$~@ z6iRo>wS`tsKRU&&Py?|b7u7m!`@tzAvo(jd%m-f;pIV|kyZMk1(OEG7{dCC&~o08p50TU4qdAbJ);h+DJ-6htSOdl zjO&hsZJZ<QKe~RM~IY7{+eS?^P?ZM6pI&?=b*zOhLo0CmP4SoZi3wV-na*9N%C#m9i zsMWJ|{bWkm{`6tc6=0<}jp!88_M^Y+`Pt&QnJP3b$<8Q$NiTh->`5PJpI%}ai?e;55U;tCSMU-E^PHtOT5znw3u|_NL?D#??P|Jw zu3e{l0^&3%pQAgL$lk5*HAAz|p`eEs99rFT(DTnSy=V2c0*UX2VXE(hm+k*b4Gb_g zF}E=`QnGcnG5WWecS)*RzmZi?K8xFAu!}SDY%Z!w}@1i3#=hIn)6p&RfVA z0b$KZo!sR}?n$V$@X&G{pZ27L1BSLQkmII4@0co}mk4Pd;yT0?n`&vwnRtl}gb zLVIpd)1Z%}I8ut1_3?W1QPh6`aCGDXmITWDG~)YGJO<~bed{*Tb(tDqqc+0~=Poy+Sel%aRwf454go4gl1%O9DFx|VrWA-KWh7Auzt`w6NvjMP9Nv=aJu#9Ctgu)p z^h#-@IhschYSO6P9gi28rg#Q4r|FVYz*t!R8mKDgRdttc0gLx-xQh3kLsGed2x6xD zvp9iGjE<@++F_B*y_HKt?Rj~aO-eN@Qu_u@ER^RRJ1epKA4!!ZPW(B`)qq`IfKrV> znM@7{%EzRkgP2;1$3w~P2s*hcHx+D^?qdKq-c-Kfbb!9&sL1sd1QSgp0mUMh=tJKH z1eg7iP*=8-7836)v1Qoaz>j%;&cehBe}>IOJ$VuE6cGCOf)Dp+zg5-gZhkLy)GMVD z=eB%o)1}RD*A;58!1ze2j%3`)>6i`u!Q2=7XSeK7g(68;Jt?r4_{>4qfozBUe zaquD&^z`*>G0v7DDe9!r`b70ciB(DDYAji)-8{^Iqgw0ZM2d7vqSW}QMtb6mYGW*G z0S41?=nVyNvMTJStbQU2^3iePSx{r(0qB~KI**BcM@mc}7A*b99)%AUBnbJsJuo?C z&+twp8K~>=pu$sF-rhFdpXshNE*y&LES@L@wQr$lwqQ7q*n$!Vo(5z|D_-Ehy!}28 z{H|AEpot2;K8)-yQL9R@Rx@#VG*zc_bo9Rb=+PKEj(ja_yQ?4XkWkvk=|qd5(faei z2+BZKc7&pmy7ik*Yc}zVh?=x2?B{l@yPi?AFoE6OFwI5a8$zoIXcy*A_gTKU1zEL; z#BJ z(sTOWd62w-+;Ab5b)k0Q5qtXeJ-euVqtkcbzuUC&{&c~uW4f zUe|BqU^=~Jkze=y{lV9BujShDA(wakJ9^Vz65;c;!V>p0i$>c>)$cm31^?75D(Y|O z-xmz=n?&M9ITQZ4N3#2OYO%T?d!7^gYV_28>E&DY<|Q(;+Yr46{=Unr@wF(1*mg)tBlbw>Cqg)+&cYHgMTQz>Hc8P)9VrR~Kj~sFor-UM=4PG2 zAmp*QgexiOuBY9NW6RH;dAx%_|9#>lkKZ4~y|~@ByzaU>*E)Z}*Yj$GA_!s+aD_+r z58Z!-e>lk3_9j7R4iz=>ha{*Nn6E<-pbLekfp|?#c}^jDG;Bk0LUB&?KkdHCazJ6t{gES9Yy zi_=f%a8Ah*@cbU@v?7Bsp-jnasdxq0^az|dwJA4D#d|H&d=L5EsuAe@?X5|$>WLUS zlT^`GsKLlQp~|F0hgKHku{_T4c5X5)HJx%^RM{j%j4SGBJWX|UVGP75trAitwKSgE zoP@fn;rJSzkp%6vZ^_ovXrxjF2>#^Za_w1p-hovtW0JiGMwmR3UJi4&pDWL1M2BH2 zY;zc<$z-i?b99UEVL54q~epAMJa*2)>a7vL7Ygf3Ilj)$7kXikw~C*87k znuM<_#I5YFb6l7(Npg-mDe`PG=@44KLw)YkYiKKX(;zOEEbbj|mk%~9Q`97ja6a8X z`bdta3J^vT@`j!mXk@vXP#7%TDiFlXbiTU~^w(`YrqHZr7M1F&v|qTNbm$V1dVK2j zlj)<@h#juLJeSZ-Uc2GJOop1hbp(moLkAze4fG=%g!M!R0Y}1^wO{Imc`5Qkx7Q|U zWYq^2-XQWGE^})FBJdk1nJm|6w5q_WTB1%8+8!6XbXasG`{PfNULz`P%&n;#zFvV! zO(hF2oiI=>d3GG==f-2bOWJ9*3o3_|nsCLt>15liQ0ri#U!GopUKPK!FY>&M0l#Fd z2lbGn(VUN|Iy)(Wz{|cVBQFA{JwWZ?w$$s~X|n5l){E^r+=~J)S(1%%DtU?Rmg!Tr zI68W|icx0oCKpvG_NiCgJPi&KV{nsgwPPLZXYJft=gNtW@i{y^@6z47&BxjL_o7|= zcLwKz_g$mEag&Lbs7t&?ReD95gRu(1vI)v$bDKkM&j8)Tc6$7NU{B?I;w2lnhgbqD zRUIzUju}lW*oob#@r3mkR_(-Z!ETS*OFKnounCgFM*oFU*U+6z3eexE9ybhlgnx0p zQM^tM(s)JPV=Aj0N5l?1aPG)yGNL@lZSe*e^b&ZcdS~A{fl6v) zEEkRYLJL#@G2FqVtWx74jPua8TrVSNCsaxJM-hjA!m9?*7i|0R--#2o6!^_jp_E|N z_uoLEPY%^q7|mP#vn9(OB<0VDh@b6uHk7e;Itu`noA~d@%08a=Y>mZ1Hg_P+rg?H% zDQN@L(MdLHO2oBYuHl?W(Opn8+vax?)pikGtf$ubu5%5yNY%IDg;m zdn;tQsbwA{}lDUG@RdF#_n2J>o83|NSI` zhB)Q3gAuGG_TQkH&4D_I!Po>J=nOEuMUOs{$7zX~%VTtqIbe|Xj@6%xop)spCN4Az zn~%z!7b%I@V<9zcL?2{MCC$lc*&)A7CO=DrQ3#{`T7G(I1gj`7S-)h<`OtQ4vrx&i zk$p9_R(?5z7(woq>M@=Z_YhWE3-GcXI3@mxzv_wZ!TwWoRbZu`tA9_&a&@aCcoeIB z<+dt@RC>p3K?gloZ?-?B@M{d{rY3nB&)Ud15!WX*S!La&fhhH0L6?pUd-u#3n`IB* z$>vM8?8^!ycUh3+Fk~r84t9*6@$4cEC3U#bWlrLELq zt`$dZSjAXd`sKjeKkUatO@S#>zEs=ujfjY}Rd)RdhF z;bEkRNJnv4XnZt9g3rsFEjA<#$@1I=%`~AL(Qc2^HMLpg6nvi#au7mCkbmGWGog{d z)80hLeT_O0^gut+xv=`xFjT~TmAVR`ahS|yQ=T8Xwj@XG5WS$&$o?R5zj60iVt>du zL}wa~lMu`o8p`PZfQjl$cS3@_fNpqwK*+Po&0$!5C&c^c9kXkoB1Z8HXXkek;{M|Q z=fw@3y(@gocdKLYcMxUyzgpb^<5qNIdeH}@W zLzc5B)91FjD!MvMXR5Dkeg3`x_tGrO;P_hi_f~2i%S;|@GE5)%uj{hdNsc`)v_>Mx zs8gh=_o9MCfLB8oqVep&po2APe@6T2+N>1yN=)4!N&R*d25-rV`W}$q)O_A}#o<@- z#9M&J))n1rRfWr9WjbM>G4HaGkzw5G6C$ zA@D}0SaYl!!w#={yF^FPm2Uh))1S%6Q(!kr82Oi}9PPb`+M3Ie>gn7{ZwPRKS7V)r z&hF%oeDHv|Lg>k)IE2`EvDMBkT^FI2vEZ^%nNb_i>dw6~vgj+@sYqz^JpwTr?E!JL z@m8B$gFWzM*v&?Tp%FAbLD}7^l!gk!32;@K%Kh3P=26U6<2@x$GKTvvvP1qkHFi9S z6xqohHnEsRUM$XK`@K2!goH$_aLdTNlXAD|p*-&grXCKP(scGiXA7oCWnQ&upIiEH zI>NQNj|94c`g-Mx>a9)eelNYXC8t&0o%MLZwX@Mr*Xw6Yj%~3kn>I}qpWVd_xSy8c zA#%IF`0+L>Ew&#PV`2?^spugio&(&Sx~*u`(O5G83S=-d1l=X^y%a0TMm*t+)v6c1 z!mb$(kOv%_+#lRklH(phdcV+IVqb#ov#E>;?;Ra9v=-_99Jn7d%ApKD;+l2L_6(0Y zJ>`)Z47>^Jc)ma5uT2sc!tfKc1uQP){PN_hGS1O2HC+W^m8THEGe=RMVCl;u{Vwv= zh9g(r&>XvVthJ77U31ap21_dfoj@@KD7l7Ym%n+qq9-I8X70jT`zt2ffodG52?QC- z3mE2&*kz)roYG#gI_E2RM(=?_FT|||y&jT|FBe3P7~1B$)9-u2lBX$(tW#P*Wkz%L z@i8O##Np-b+-4FriU>vR*GO2%qhyG7cSz`+A-W$jGBNrLnRx}GU`CaGFmFJx1nF1v zAvfypOQT`d*1`)0kyJ!DF47x%;EResQ&7bnk3qVJ8Oe*GWQoP;p^B1AcIQjPIwV;i ziG)BRp5QoL4VM~QP3Axr`*GF0O1pVz}b;u z9UnXzvgz@>dBStTd9vwo|J>8F1#&|(?oEOc7^i%La_nK&v^$dTf8N?RP|^9E9+|c63#l!Pkwy*|ocB7tV^>6`9++b11nbjdX1F z_lIkWR|3;^Lf)9fn1poff`?`qjSN&?k=JE3+JvP$TeD5FgS(5IKYjLs`3uP^8WxRXnbyjrD#(}^$4}! z%nY~eOdpO6_nLGjs^i)eAR=7Vlp`aG?o;*1ukWCiI{cMVfy3r9a4e`8sL~nJ8R-v= z8yuM*j_@^8k1@Xg-iF=9GCG&`JwHtpTA#U=Hj%` zj_Q+(4$#j$G1;G_hC@D@)vx^x8rE6(s?<^3#rj{$suQ=)+lm+TTXg($P)P4J)2>d| zibsZ-;^loJ!gLiKH0Gca{dQO`ARk6U&Q^j-?17wcU;oU03HUBMC79t9)tIlRm?aYv zp8eF}pR0#4WT9dKq3;jj6frAWXAohOV^)&kK#2%6szl-x`AzZ$!-JsyHfv4D^YAqv zfQ`108|9MfI@Et!H*7qJBX@l4x(DMkqJ=wN>LYj-FTq;`4=+8$dS1e* z6Wqu72y*;M1kNu^xOC)jx4_7w%-y9{(A9i0?tt$S#Y~(WC>WJRXqm5`Cq_ehz82dX zgI=BW7*(w!f@{LKfZ0ki-I0Vg4j%?#nN-x`^czFQx)w9`j2!-@AzUxWt~ndFj6r-g zOc5W&8T^OQ;VGXFQ=QgAh+K9v|8fHis}PH-6NMqLo}h< zh(bhkZqm7QbhP}~@Js8$0{_o9a0?Q5{5-5_)iyYBj8(qDw%K2BF|m{4madVid4yON z7<_{VBQ`o#K|4q%zYA|X=I=q)ysb**H=;&S%PyOO?GHl=FbB|5G0bYBtv+4l+R+4?+w14knmK=S(`? zj@NOW1f?Sd_c`x5KChogA0zzlGcmS5TG3ZJG4Au=5BL@h^AkHp4)Z+_lUR<0ae}qU ziWUtrRq~`+?5l}f!1U0?+uLOrfq-0#$om zafHkX5^}gmtF8t&>R)!^rnWO8D+9Oz!eDioX{ljOWRrvgg{c({Ed^hQKy#D|%Slou zvb^Tmi>4020hCbCFJf;U9^fJE(Fjix(}BJwB!*?JbCNzM^@}{DyEf7!$R-`W1$noH zz6}v4fwLJBSby8rO5-q>3z&lgW5dsw$sc8eys3*9GUaCFbZn`ZQRf!it=2;a0SyhH z$Z^pIafSL~;Du8dh!9vdaB5sz492OvNSK(wT3A>S#zW}xv=gn^a=+QKO?HX;b{NTs z-TPP|i$s{R?6($aqil$42=ig*GG$>B<RQatFqd$-x0*GYjrg-cEEe$&flXshF+Mh5Tq_LhCs6iqU}}KdtgT#5U^?9 zjDgon2r$o=P2-T0r0ig9gcKjWCCtpb39+NiDECb`C#^nF2%iwSi8QXCS5+cAk`o(D zwa2&|cV#Yfx+t}qL5FRpUB0Kb<4i;(#~VZ}dV_N)I!L~e-$T0R4qlUlT_W7D`yeP}|^+Pv5A$9jko7EHaRVoV>&WBfn zdR8fZgM*O_1^m{>=9-0i?QB5-wiHBf4=cxMI^C-dj291U5ZOn;D%wg6U+2ILfug0! z3xcxtpUn)}5iWzfyyC=x-FaEvxL&=6}5*+I2?CExa6#<2s}9zZ!6#>eU2 zFPyi{;1@dcRHqASE+obl@3rJEf1lZBWrQb^HduafG}4 z&UNrY=YC5EKIVb4Gk~$(V&IdSwMRX;Jv`4hcvM0-6z{&fP0K9+Cyj0?o>@@)dQo#O&pp0dg=JPdT;S-H>q+Y(`S9Ls8w^p zwm{dC;+J2#&*4~(>z(QFYuXX7grzanA0$hYLwn__KK&9Og+13~9?Bh)14y56TE|(T zJ^5N=AU`*aU{d91y8Y_#DwtKVR27)gR)&((X6e5WE`Skn+D9|gJ{a(20Jg)u~$5{XD#3Gw6yg-ab(dt)+Jb7%JnJ78o7EOE`m;!)<29$xk(*BRZiY zQ!pMm?V*3HDb}WK;`m}{^iNL7{6sI_e&9_j@;9hapTUd%rfQd-(-b{NsZw)l$%hMP zqrw8PRGKB^PlaD8vIza@c~(gJ+I4;z`^9QmMSwGCc1&jOVq?&>Q$cl zWKQZVHpeJBg6bt>alYvN{fQ{)V`F{_N|#K#cF=E5#oo-6IyoRd6;@BsKO6)ZoJ3lj z&Jb_~bwJ7itc?>Bt5gAf;^q)&(P zrLT_73%08YJ;Z-`Jr0;vA8k3N-d)5XBf|iZ4I|`(Z1Wzu5nG|*kOuqSgF^uisX3@M zh7GLUetL($J*)3{1S6R0nZN#n#L>?#zjyMR@GFA!-wbE@pSgpxt&_2l!Z)wPN&kBe z)IVerNos$!luj_a^&~h5@hk(uQG>+HttA8+l}%L4p!N&%6%eVpv=H`w)Dh;5g$Zq} zxj5r*Emz|=XH;o%cTBa(m$NdMH(gDfuRCA1{H^c#)pLcgy1X?rMkoSWxFWh;bMLwN ze9w1%-~Dmvvr-MR8mWcHb3Qa6;Z}!w28gDimx{aHCy|q6CbRs;NhKULqHEoBj`k2N z)OgrqeTNjufeAd%0T_JUxERyI*J75P_~Q+%?t(oPdeyxS-O6EVtnQPc=i78o&#j22 z7r)5uSv%!7_98dwfeqb7g3CUQvmHdwYxi5>kI?|f`n&8@sj!Q8yiD{RO2$XSx({SH ze8@$FqSvxu7%aZ|8)i8El^Ym1bkxSv+Bm6s1n&atMW|TZ31xZUhKr%_8E|ulqmX|a@EHo=pcX7#i4qzo{cPszMPzwI zpf>S46Pg5WGN9(N3W^kvFGeW`kW9O)mo|!h=95b_(XG#c9?=Y+fvpk;Ymo@P9SoN^`{g(~5qfS1(=Fe(0 zE|+(hG?sVK!OBZi^v?coE|+o1a!r;Trw=@2uBUGZ`$%i$UU;;;oeE*=;+ATu&JD+9 z{WG@;K}R>S{e0*MIPlE+$DjK$Ool8SR4EO?hr;NZz0_!qP^NJGp!bw`*oeKlAefue z)WJ}H+pA%-)cIQo?FB{xNEbs*u>s(NRF=)$@HQ^QlOOL6=CSZ35FpDhrI@p~lza>J zxa=&R)d$6{l~5v4B1Gn3Z&P4I`tYYO1q}EyNdk5PocF!ZN2JJFSfQ}&(y z<^G&o88))whMkyH#}xxYMyR&FuQeS=0rrxx6pOTF!6$M-C|q2&5!mMY6g^ca>k)U} zsSSeyipD*RLfLrKoJ!lg%p|D^Q1^+#cF8|&t6%?EF-9;f-yb!g(y{DOuLg_nilrsW zb<2AiSqH*r{2}ysBnCy%sAlfmOBD|C&eA75B44&CqB`3WUov;0EU( zt^=Cc6Xc2E>ii`Pc@$ZIEx-EnkNpo`r^^$=djfZJW02&%pW;L6#LioOkMiF34cAaR z{#vquHi8|n-&-$3Yt2lB)`3S8mDFMz6;W);Ld{ne6mL z%63%P!T!qBwZ9DQoY$-K#X@)c zKDTb2KwZ)3d;d!CNd0Mf<10HYDkRG>8FwL!@OF_HH;0HrS_VBGH7NMsNx6z0STu9)SRWSVCds7<^Pn*1$Q+|LXB{NUG^Q6(41^wdsy$HU^v=|r|xvXbHr_=p(4gddUs zvCCSC^(QXOxtX+xt{DF9LOHw(C&2c=7J_`9r_25Kbga7=f05@y}G~#(}rckv0g5wwUy0daNO$Aoa(AU zSv;B>=tS~(t5p7_*+rHqP)8xsDzmSDI+eu?>V$`FwIp1e_yJ1WQ7$eVbyM%^<1gxzsX2a&>U@zWnNBJp+-4f3Rdz;R zN!Y8Xfp;5$@WegF#66~_Zfu^v3+jwFk4Zngw3UlgrZCn4GGEUK&L`ETVn7jIB} zODSWtDAQi7S6e+8HTfBxqd~Rtw1>^O;k69QMI}Xb=(5kgmPpp+D&Ye?OYyAD1 zD^laQG(^nyPl(t0?_`^)_>?5p83kVu|6E+TiYKL${FcShe;1bjOJ>TyBuD@;VMbOa zrhiC`qExhOk;PFy$uvCB8)A3kePK{i0DlHSg`rV^2}#MICR z8w=@2kMe`19eTcl*d`ntC0sF&CRS^3BTI6eiK+5xypPHdE2>8D+g!Fjc(T!ItW%ah z0}i9UQAB6OG9hem`Jw%5GPN6k3fg2iaP+|j4ZdcV}!mf?N(8L)zf|1 zq>xeM8~>jXTvj;o@QE#CV>4~sRa2&>k~#7I6l$V^?jBW5J-k9xwqSr-8|8BQ75jX6 z`NdPshK<1{CCa_q3i76=MT@b8AaTw~^yJJJR?weZ>j=`7Sc}E4Eg1iWgE{SqYO9AA z>Hx{PTsIihBYS<*F`0sa6RODmF@N)od8R0ts=oO^Exv^xa|35#{e`q980i**oYylo zP07$*8GSUrnE-y-is{}IUtdh}RK>8Lj2OOJ6Z z$*6D4(dV~?im~#K0ICXm+q_oGJmvfc+%?gh5O=E)tyQ?-may^xy!;)(N~s$1c4N#c z0ts$j(!n+963|N)BgjTN|GyU%jdNHJX;2pdI0AZprGFc#IecB_<5RxKo`KfI4}1uE z@%V+e7K#2qZ1hV(`HeMqeXg&n2-($FHGw)~Zefv^srI+S7t!YP2#%%%t-NbNy&X^` zx&Z%(j61ORXAO(u2qBZv$DURKFQ$x#DRe~4Y7OMm9ww-MsYOW6Q~5Qq(BX}Dct()* zXB2<{zsCSJ7xq2yo%rye6yFn<%Pz9?faF%#I3{#n;;9qILOZy159pyau=AFXjYoJu zxz!#q!#g77C%k%=AM)*RH7;qVi@G_W`Ad0j^}_PPEdN{<*H40AQ@cMI0(DQ7(h87B z%~~UttOqylDdtbw`T4V>s5SoMhHX6e5Tbv>JWr;&S)=Y2l$&dD8B*ma3jPt(MNV{4 z;uqo^dYQaFbrl%2jsn0WEWIF+UO{*(w{fSCWjkMFt2kx~H?!!(9qG6JF5dh@Vf%v! zntcw5-t4vXhi}adO!u&oXTZ)a?Pve*7l@trA}q?1X3g@GBtTQ_6Os#hpVhSH#&v`BeQXFD zkdPwDu)`c2pdeK_eSHWPR-uF_%k%4KaioM*uB@@MLh->Y2zOMn-wADEY;l;Mh|2+5 zd~w#~L(3<`$Q%P%=fJ8Y{#od{LlL}>mM069_;&g zJfVRQT6UG80018V8WusD<5-Y)9>7IcBJXO4RC0LGnE4(JA-^B#-vFKMaA1zm$w8jw zrN-BNBFH)|edZ4_v#UYLOOC&zcGKw}fmhiJgI77o3C16bML!-xFIkaN^bqb3#HkCo zDTg?>n~YjHd}$YX3x=@W53$isX6BFAMFKq2vTr9-AVGW6#C?hId6(~o`{z)8t9U!0 zx0UQvde>GqmtRwTPf^r2v0{o~X-zH`^3A(qJ6*vNXG@c1&VmKZR}?Q>RneCWh@Cy6 zj7Zq#Noq^IY%TAzp9ZQr(%(;uLZzsy6E9CQHVSvwRq(bXJWYMDZ0YiRg*sK4sBL(cnNPHW2L=JUt1gq&5Jlj0?~?p zjd%?$n+B&)LS7IGx64p~?lms@`INt9XV-h7VvUGvpZ{^#E=`i5!A&cY0#CvJjHXG9jdgOn=F`ncAj@ zQ$ihtf-VN@NlPYR2t^t;+eGNN>QYkzx?gH%IFe>D*KO^zhHyQrcVSR_lBkp3g*0i< z*gb{cSPVZx%`U%9o>)mw6hS%?>{3~G8x%%w?+N#)lH zy}I(d&h9BS6KQ)3ic;^@If%{&_pU2Bdzm=mA~D(@$CKNQxJWb5V56QYhtLZB~BC@^t}R&^kr4){?UTV3n0a4MKoN7*RQBCZ7dCJEM zavl*g?p+D$EKp5zl3Sf>YSw89noY)Zd^70bmS0|7Yym?DZMko?i8IWwe`wC!(t$D! zb5Af=!M5>o`ZRf9__GflpWLn{Men|V&)nnsx+1+hfpJrQ?W$xBTQ+tJM6k;mxG)-J z{T{=6=$C{;^`K}wz5dxf)Rp9cbMQx&tPH(wWmBH=(a?7=i8#t@RX%azNU&cK(3b8( zXHf)9mc$mM6U0pgQh4(#w_e{giQN^GQ``WpGk zd3%W8H)g~hj<0Y?9QM}mPxQhQT9qwBX$Iu&FPtLpNK=F4YBJ?rUrAYrGcJiSNgdRf z1SW{Cr_@2zJGoEm1dcQ}$^^Q!jCRSCU3P_x%K-lD4i{b9mizUoNf;s^Y>dA1L<3Z!rWMJ_cJOpO^q#vn^8)#atF=nEC zzQKBziic1BYfjsu-lVZ-vW86>DF&0Jyi`3ji~~$H13K&)3|3}B^3oa@2qw+MuUFS+ z6jgYK$MJP2<*mG^2#?F6rl7n`zaKsYk2|kN_#b!jCcQll>Qc3-9;j>qHPd-zkfo!n}XA{f@G?1KL#tN&UyiO%Xt!8oA5+U7{OFX8Uh&@j2R>Y~S zsV@_kQCri_bDKWqpf|y62jo>x7`d9|#(s}C9oMxCRijxuyo)xUM>fZ&UY&*vj`y1j zjc!-=8`X9rf&6wJXC%X&N z4EM|}&dF|^BBR)2^0dLVL3+cPI9^Q19<0?%KF9N>vLvo9IVvT;UB+k|;4 zjDKUO=TwT4jZrM?Ww#q(N#-lr{hSn5>x`F@h%sdm`v}Bt4L--R0%5gcT=hxvCPWE7 zVa8lW-NAJ9*~Jmbdw{5Vy{&`9%y#}j=1b0Ydug_W&T&=VWc^FP)Rv~aza>k3n9akQ z!TnTio*PX*&I-%1s_MatS^mWj9o#=eWcx$A?C8ab z$}=X<#@Xj9mG>P$^~%TfR=@Nr&WaBv8LYq5zA27t=@Wn6H>lF?@JBLk&Id>b*WD2k zQ@^x)IYqhGo9)+ivMNj6NZxnN3%ycZL zCXv=8ZEh0UD7~FA)L*UFgEpbK*1&-kkDHLPK1*0R^ z@vWSHu8Z9I2H)3x|9xowJC^nT;@kfx&|JQ6V(vEr*99E!KjG&7-Hp$m>~#7+aC3G4 zk((R-QzQ*$b@rQ_>ykV7Dth)71{OAkS@t&e+SVqJ^+xb&n2@4E){*h1*NRY z{{O-B{{*Y|Pp1E$$a?<|rc?j_WjdD&IIhUI^BDb>TK<BlQvtm8H^^a^0azs4v{IZq_>BzZ*hF zj_MORXy6}rHR*^KlLD}>rCqxpPo|~m_4IsGoCPTTBzZFZ{lq1ChKv2GNSusdjo=Pp z58tvpk z1^9ny|7yN!y=&bum--sCXvWki^=Mw=Yw{F-Ra8Kj&dL@rpFD!^lv}y0>Q#av&z`8J zx2BFY1%9o=q>c~91&-@gE%w&$YM5+3YMxY^f-#3kI*+rG6*xx)OAc{B#JnoVuP23% z4}HxD6G4IB*{H|n$gX(n%#S}_52q3yG!k(lC?|*#890A$=5a{$q-HN|sJH=0=yL^u z#)G>6R)Z*OIl{feR|0A34e!w?KCa!MCjWMf^jN+5EwF`9vyXtBEG6U(MI)A^;*$PR zE<6b%uVpDO3wGv9lB`Yq@(0AsP>5w12o1n?c}@E}5)f+;dx! zaikb7ZJL_BN)NJp@7R@4|9&5jDtUDJK(dEhLST4$PNclZy#^UtUL^Sgh$2FM>jzb`oYYt%jcyQKX29fJP_Vfg>> zMRvB6=#z8@jH1%0G~BJ0ugt;Jl~JLo!A)C`x>3;7(Cqo_@6Z4*~? zq|~Bc9;Wz(l;Md6;*t>{7E^{Nyxa{kUn?CnqDn+0M;9qoxV9eWE4yb2p9o#s3*?Y_ z%vgwIl94Fl7DBUv(Znx3u`x@qF#d9GeFl}jn7sUu@_Ga>lA!yM1au0+V2mRonUsR! zLBPK(VnjF)1C0VH&j?YF45oW9bm5}qW>bqnhm~NH5 zaY{m&ei*5u3L?=Mj3fQboB@+1gTBQCp(bZ}p5#rq3j0Ey$wR z&Ev1^n5}NHo_4PeYsPUuAKID4S!L!CkEB+X0B~n)ijt`1(wHS5Q4Y}%IUi6_?k31z zq3{7PFlrgWZ|$>S^&cO?_LjafD3W>}|E^gb>lZYPi0-G5y_xQ=N8A*eKCsEG(2xTB zdp>m(fNb!l5T^yJAY%m9P-dJ^WOHS}ebJbK;7!Mq3mTmnZGS#_%Gub? zifjR}Ig`EgY)K)0;jU2I zu<$(56Xn`N){}FaT3G!FY$&^<=ZjemhyhrDqRi#HTaR3WSjHG#a6`-}d_+q`^_U@-NRF(N4i;*K>}5F-SY>t1pz&!Aud4l9&%Iwh4k z2%1&~b$YWcTiC>981ldC7s#arRzsBinSAZ8)i6{)@H z6h*6RwTFgwcoK|*URt^OB6>-@Fi3%%<;8TkYr0UG8RhK69n>Z&^!OZ3MMSLv3m9qW zL#E@0=rzU+8WEf}R>$cQ^4jU%vzM$&u+X@FrXhH^ag$ZDW0?fnrh?yl_xqj#D`K#l zpufqDxc>~vZA|b8xp)yn)pb1$NQ$B}TVT8fKIjiG>XM5pWH6uQBglBiGeb_gsfWA; zfeZ&SD#hB3Zawa5sZvTw);+}zl2>&_YY2MoO8bYz_%#+yx2vw_%8YcE(RwIGn~j991~DT)lCs0_M)x0eqCKsVhsC8zIqPh)AjI>&#>s38fm0rT&aN zjV0M#M%1-DSrQ@>DHkCknsmu^vqZKQm0eAiYd2l){YLaP-<^j0KhHDI49|0Z?|a_w zeBXE8_nh;dcek=-x+%Uh#e~R{t(OBL^?3d9w4j-O1tl(-0zsbpHzGdm(@UC1$oyFI zsE_C1(==(X%v%`0*NhsLa}8sX6hb%lh)w?S8v>QgKeZX&Cs9r~?_RF2;l z_u?jGj+MoDJ||K1v$q@;gl9VT{Pd_-VVh9g=gRT^c+Q?6{piKWx*vC39N8~2SCQg- zHffivZnD2@9Zm1fjOJl-P3m6Xs?L6#_}zPtu|K?P$sw^z)tJWTbZqFD+x}d^ws5;F z@}k9oh!Y>sv}VM_)KKN)y%}vHjv;cI)aJLfy;^GB+YS|#osF9e8tJlWiqOjDz~Aq8 zlRjhITk&>Jq;`Q@W5GGggt$Q4!+wH76>ksP3%vH?+H%5XqpegJcLnwHP$42(amk%; zlW^fzl`#@|^(wO5{;?RNEDUN; z$>A2K$c<0iH_VD#288lbPFv}`-Zdg3Kk`RF(V~|&$5~~Kb_GOkgmWf${6{gLye4W; zT!L0q$m3#@n8a68uhV)`1MKrHH``__jaaPfrg_4)l$%`#7iUxSi@e6bNrG z%zJ;rV@S4Dz~Ph5yG#dyiK4vAgUU|zTjKhaL7I0?P)~_Y|3X@F*BE$vow7s~Pq8GI z#+jH)k)1TXjYOVwKOdD7E7kBsmV5M+vOVJ!g6mG(z6d(d)j*1{M^hSjNtcVP1=WaA z+PP#WRiEu!M6XB)+no#djO#4stRu4R-pEHNl+m8{pXKIj;8wO;EWIk=aw;a5W^bP5 zV-?nT{;sH*dQwr8jp{gd$}HyNo{5|@*6OF_Tc4<9%Gmj*8qAr86jtA;?x{1@2~Xy4 z%nThx&5nO&^VZE3dkpssnbkGuK;6Ta(8Sr$mpIcg2DL|Y${g)01ckXRJx*@Hpc`k| z&`wyXkZ0!Fms0B+HLSytaaVOD{CEq=4!?601{qGhH<=6TWG}ZIhLN;zt{2CfPKC=* zTw5E6Pj;bxqh)i?cLb@5Ju+^Qk&ofm4Ra3Qsn)FHv0p%y>rEvZ{aqgyNHCI&99I70 zatXFA_OG|MGu=O(4DQQN3a8%1RNMSYc2CPb{bpJ*-R}*NR8Vs8!~cvrigOir%Tx=x zCTiwvXbv+}kCUF9aS|q<-PkWelpragG$^l&j!6x;$6J&=-=yf4m0cQWP$c?sKAHEv zUHYG(>>J3X)UQ9lIrrNdoc-ZmtZjs67 zG~tcK^G7PEHjTZP9XVx0%4#N4!({#KPExaS_LtMR2>GJ5okfT`P8W@YD{Tvv^ZA#L z&Uicjlzmz+^+zRHn!k0V!_*SBrVUpSCK{BFEyzx_pUI+R{FIcYS9q9YB(`hw(}CR+ z@de6k9mjf;>&AkP_w@a3li14sTZm#8M|G8uPj&jBmUp1NPRNln>fB3{k|Mq)sJh_A zhbhKwzLOs8$U8&XSC@>=8@(JAY9Iu1v$qr|w`44|aA5rcCmRE6?RL%=haBVQx@2#5 znWrQzTDZezNERW#cs6)8kn$LkhGk>{!DCJkD0o8m(P@aUsVvQ&(?XN zt|~)&?qK(TyM>0FTh8CtYH@5%cw1-D{Ja{G+ytFqho}4eT zA{IV+n`6O8eO6ucF6Y4quO;5M-;n5U6CBejw}M;h$lPUnWs*ED!H1w=t|<={t6aXC&^;uRq^*T~)xV?N>*#Yy+IJhIgS~4o*NM0v=enuM{>C%Vhn_W4|oq-W90r(vF z?;~9=0^SQpAiCquV|>h;kV(%1K{Mq)l$9n>_t4EPO-(Hdo#`bnv8*V-P;nCwBBYC2skq|Lump%Q05q5bRz0ilH()0N#)B2c(cKeExWG~= zM%rCgG-r2L+<78PnlyuMA6U`8rek4svFI-7mqNc-dw{FW#O7a2Mi0q?D6Zw6a;@&P z=|#Zjl~;oHHD>_zwTq%m^$m|=3=u>>B9zdTb*az*2;-#y{RHd)Fc>+MALzVa2}jMX z0R}UGl=n5sAt4y77I29bR~rL-lS3nc|9-bMD_2erdmg8;?@Pj5sA_p3_P-wnnvn)< zZ`;p6E(2t(k8JAcm$Xb>%S|I-wjJx=DK^jGVh011~If70Bs)l z>|Uh;-{eqDBnvQ0GiapOqiz7o(x1fi<%_F@94d}ln`!9hg!3Z0<2{)xTv{4F`Vi>S zYW&d7!FiMgeB~Kyu>7cH(K)WxJLfXD2fsB7Jj;tmyC;dg$tL>752@;r)H&dl z-wfR1^q}5qA&2(cuu%BxTS6ZQRuYy^R%dQ+=z;AZAAV{*e8{=Vtd4Joc6#U`&Y51v`ROQ;NJrZ!MUd+d5-Fyw86!l7Ag zK)6uye}J>N<)CRaKnb&{EF{p!CEq61fGQ5%p$=jvfgAk0FDE+o+x_ZLEOhf4h(+99 z3;W%zY)~Q41>m3%b6^sXrSAV;4i1Gw*D-?d!Ll{rjH?=30)6|wNT#!GoP+nZ6Y}|=f8nhCa*vPaxMGW m&>#WGk84}Q28@c+`C!Pv0=Wrz9n(iX%CHvT!XRJJ|M~~EU7ouD diff --git a/lib/commons-codec-1.8.jar b/lib/commons-codec-1.8.jar new file mode 100644 index 0000000000000000000000000000000000000000..32f84c92b7bda2904ae0ff464351e2016438774d GIT binary patch literal 263865 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

u3ZpN-*>ImFkg8&rLFZ$# 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 z89�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�?h~ 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 H%0v2pHnRiy)Vw~=!)owu8OXV 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

TL{0eT(}8Cu9%46FN{} zK4Vb(#wl5HhQGR^?Adu;_LDa#4<&QGXJJF6^%Qy(_8I_15MZ+kyMmZ^_0ym36|#wI zw}$X}0bxgszPmu}dqLBe%;gOcOT5B$rLz8}FN+d3WP%)UT7xOZFp17LYu#wAlV9>T z*82q%PRapiQ-qjA0G_9*mf)({|#rL=9Lp9@V zAg7o&3)?rjiAXe-JkY9fcSAIHEhQ`nSV6rT8tWD?K@;{$L$Rl z`A!qAIdr%FjG+Xx?eQtS69+&RTN0e~l-U+^LeS`_vOsuGDdS&oX5YTRvP~5cbM=fY zj_oYJbL1`5?ITtcWzT>-PeP*6Ed(MK3ZtB?jLY~MGt2+A>*TQ#1s$< z+=f~YYkR39SsG4!dmlpVUUmT(%8>bnS`EFqi$GUTh(FfqiWC5(W@tDz!$_WP9_hA79$nMkz110y}EcTh}pKq z@orO)XZLz!54;mL9KnwT2qBns&Pt5fj`7JGU$&_s%}_u{wne>-7hqw-0T2tyVP7E? zL^IRTu7}R}X8R*Bp;XJqHpeLxhgeuu@Zh*|N$VCo;{<~{5@m2D$Dwc0jn-{%A~xbl zu6J{S4eDQT%p&P&F?SSXN&~k;nPO%>6|(OB97ytC9*`8uv?kbx)ciUM6T1t0<`UP* zavIE&3_=}zGHCa$4lKxMtF)tw+D6(YLCK0;>c+H;cEE1 zFl^Wk!Jg|&*HE6f*(qgxnsYZGLVWVI{YEj0FFfPj0fXh?(k}$zmlc+%K^R9^o;`}8 zef-5YYTJ{5ar;Xo@gQiLGoImcNZ{QJO0w|GCj#F^YLKYwo_K95rgXa^ni>JAz$8Lp zGp4I9P?A?<;gLc<$5`GP5nk-Q?X$5&@rwxV7FjHQ29JclPv@5EIFZ9-ydf8VgpxRu zAsBK#o6ZSuy~fSOJzMzQFmnZ9Lwwe$m$?JVfZBp6?Y@L4wo?DoMNvwYYWl_mc*A6s z1}l7Q+=>?zK&#+#RBMGm){fC`me?atM5liEiC9P{pWxOf9GuX+4=ju(pC3lCJQC3o z#j)HcoxcOj5ZPvt+)_!-kCtzVC@fv~g{376`mrM`hlg2?CbQj)VMjnQ&mPN_HesHZ$T z>USu>#(ahB{?Kpi#xt3WNyk%cC{pq$&>mnB@i6J>g^W2x=F{htO%N|MP}(2VYUYpL z@tAB07vfyj1&QNFNLh!w=TyKFW~vs4(UQDbv8p%em8M*Y>KUW%pIY{E*W-BVJtK72W9+-SI<4FPK#Pp#5G=_qU=cQ#g6sE>IlK4i~WBOuE@?-q0SEJQTsCsup%n6Y^q#QE+k_} zlAY`bIXpDMGrJEw%O?OMGnqByd!BN^TYmoA98y?|VOdG1E56kJQi#YDS3bWEQ+9pm zNrXpM1Qiu+E4?dTud*N-A04Qvp~d`RLPP)_YtCzHd zj0exC2IN%>$vD9exu`Q?bc7g|Re)i($w`Af;i0c_vEh`06G2eCODI_j8r)2r^K;M{G1 z(olyHKzsqwF?%i+bs zQ9-f@B92+

6@hk31B+MHe~g19zp6uYKGs_4$99SX})*f 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

)_foH--bysfZ=epcg- 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�KMDd#eG8Rt2veCxy zpQ%xnf~1P)*m){xPi1isJexpD3inm}`BFuGoM0K<+2*R&}xz3GbH7E*)NqL&)(>2Fp zXlHFLB+ol+H0jY>yzwmDB2sQeDYkAJI}C&gK`NX@OG86^a%?Hk%jC_JQ60i!slRc- zGzM75OBTey;`DT9QaOHMm_$jx3|h=*4hgOjG+TRbRy$dN0*!(GNsGKZ$Fgibuf`1_ zdYne1Hj4)rGtGsrrX$c=-wCC<`nrGP5#m?Mk5!y1jRR2?0V4c+_W3)ye`E=;G6Z^n zo){qaYA$|id+ybTv29>^xLPom2TYwD{*WliGd@?0-F=DZDtnRe<+$0|#N2U-bD4AT zD$W)cyODFd)@-~iEO7Q3;2hz!FC7OU`N7UoQp{Vb+Q`8~uZB>9l{COo(%n>2(lSmm z+1pUKNC2@PNHyrzhlCoX1nDJg9BiCaY$4yEpg6xYKgb~vj}SiunOYGGM1WilY)*zR z85%g}3(h?UYYR&R5ECFG%fMvmcOm!-l^1Q|LZwlq9wzj5;R0W1s*zD5-zb-EL@{+9 z@PK?HmmLQBG*rNfTzvRrI-FovjAL8jYiWACFBCT}aufXHi#LOmAUa)X09gbrh>>J{ zm1@f2zEo+^oDH1GL)!U+xoQ^?n6FDe4>}9KYS1?+$y}Mn2k0Ga5bY}_ftNFRCp-UlRKQ(8x>n1|Ht_$-J zUZ(-Nx*CEg!Qkn;tbJ(YA))iVT8Qt&5w`-S-D$wPux{TJf{4B;r?vM5c2<_S104+; zz~Fc}|iz51!x9C=FB*?%M!UU?q@3QvdgkBIfDyAKn5TFOe zNSjMh+vL_68p>e-qgS853Oa-mBzQ)GMp%CQo9_s!Z=?L6Hgl1qbEW(c-Oed*vp*c) z#4K`x9ALL%H*=XDic8s}B59QfSsI?V5$`arzCGu=I4CgA))DymK3xFUPIW-AP1)|d zof9KUovUT=aPnuovRR92Ynp~HruDYSaI&Qu>`H^R0n=jBnOv$`ho>8vkadY~Uh5h^ z^2;w|$Z>$#_B&A}%9&WO$Z;f;DTqrYlSQmXwp`{Z#Wf|vbu&FWDrxoINqzcuSYBz$ zWpm_4{d0pR*r0Y>H#IEuGdw)~T=5RmNf#*myXgk!>FQOHMMon3QSd8zsEtbl*#u|v zub9yS|0triCuUimbyf<9=P;ERYp}!&KayPkM#SUJr41V7np~Rz-of8-TPLfV0>DUDq-DmXXGWRY2}_!K6Cy@*n>s)qq+pU4x0vh~u&_|&G%x*?rz}-Bu`p^P z8H11pnEoobgb2@q%lq@sf~#+=Z=w%Ad!mpY;D=TMcE~|I+}DNh55;XPX^000;)3$$ zC~s$EV6Ec#+zI(-ivO?nTc?GQ(Q#)3u|H+Zg&~bw88l%XG)ng!*ot!~-fPr&o6}nN zb5JiThlX4mA=;+mv@_0)u?a8lb{`PzoU`pA(Uhr}rPYZfxjC|}6#rQZNv`V&@XMm8 zt!vn}wOeGFi>0eJoVGfJNOc5SashX-W8C1=?>COhwsVoS##kr>FMMZLH z_X>6}-F-4UzRE7|L{(cM@zOrFM7fZpauPRg#X-+#$gUn(8>PDU z`AJj!@UxVlqQ$PDtRZ4lW*bLU_=1@&3N*sfqBFECSNy5Oe(1VCts;1<^vq+BbKr@= z+e8Ese7fZ=&c7-4oGdjx#Gcjzsr3W6{t(W8gYgBuT%wtu`)Waxg^4Ws=w$_%>`OA3 zuSfufJ{1`rK#d7f1C|tUlIN4`C|az$AI1r_y}1a>&Mio+Sof} zS`Mi8$>Lk~sNgUzxYtjM#L+B2z|piYkrE|#^a&{N(`ft@{IhD5z2KqTtoABYn}S42 zFj@oeUgYo3)=!MwfsFz!Hv|&t_t~PHowJ3hh0(vBJNSh~Hgcng-3rM3BGlKRRVch+ zka+b~wG?q~bmySz2)46zGkP(0wdbG$QgviU?8C;U6Pc_|m5rB>LQtXv+yw5g({!ty z-L|Qllam!@oM58%hcKZGczV`SS@)#h&00mxDbnse??^R3MAqN*aI(8nNSZ>}_Xxzyz<`L3oH1>SQSW73zlS`NhH7P#nu>+h%0 zo(JB4z~coS{#F~?{}ea5b8USEnJS$R@ndykyM4gIhVNfp|KHkR&$%5Yf;=*LX1r$P z$FN`WH%!fmU*&{}G(hWDIboQ8mXnYllqQ7~DOJoYOe{>yz94XLq~NUJaIv6yn%!)8 ziIaeBaMc=c$=@rU3z;r%XdnYlfa~|hva*4ziLtVYfuoVR$**GK0xTJTW6<~7)fx}x z5tRHMc^0n-+YH()4E-;Bee{E1=t!pMM9Tg>1l6%t3jZw2k><{NHa>R$;PC`vAEbQ9IiX|XC;j#20#(#;xt5I|*ui?QHI%SEu2sAYf6uD+B&~7U9g3A|QMWB5XKuR7 z^+JMuUgK_8_4CGE6 zaQ!|%`V*ReBoVNRRC5IYo0-4Vj?J5WHFJrZbA5Thl;7|VMdbCbMP%j(4i1D6?v)ej z<;?Y8;l|e$NDyWr5Fj~XjScld{}EThy13$=UEnKlkpS1PF7Ou*ei4JedH>&{zG3_M z4Z@27=wD8_x@P9`q^x9}<8tP7B%@47`#%~10dX#iVK$#Y$dP7WkH zvFOXXI1*k78i52Eb5-s?&0l?E17m$teb8^3etgK|fW8wU4m5D6f0)X%nR^!wG_ef0 zelL`NG4+o}^xs2g?OFPh#SAX_mdjlIK6MH*HXW-MV|Ht|10Xs6lz6O0U$k6fA#Oh0MvmUz^=_#h5sI=;V*}YX&{aXYKR!C1(16I!L#cXeSaU? z3WNX=2*Dp*?++0C@m&9m15lsGnQ~ICL>(=k6F)s>UT`GRfRbQ&AUpH^)phc=msB*> zYhl;WwfDAAbS%|2m7^gIz#j@Ft}+mEf9b6Jv8G~xKe;M$!|lIBoQXw(ge^o!!=AI| zKXQ)tZ#kcY-Sso)sDkFLfkt-nonx^Fdx64p5Cge359tOX5C{234+cI*=<=(MCn>K0 zZ>K=cQbnhJ6}}DG_Lf=kyqgm}>H-x3Kzaw76kXppRn!{#kd6e*W=!xugV0;6rn4<` zAB<%VpPc-w9>?!e!(QmE>L#wtZxWvtLT|hBihx&2Tgx-Yex37PV|Y> zWqTvQ@YuL*XJohA+Kh^y&9ZBehAOaMQl>R4X?*k9^;zJ>w<9V8j+O-J>vlyDX*GW_2ICh0(j8gY- zhIz-)NTnweWu~1WByv_%k{ov_i7n&0Sa_wXmL!i%!=5Q1-;0q0bv4UUq`d8ad>{HfMi2=6?@W5UNuwR$7-e~ zTnWgJ`6o(p%V_n`q1Hl-@?WRu_eV^19+fP5|Kt3Zj6_vLwsYJ9-gt6Ea+<82%}%qQ#S{+w)--bU5B77>;b0-0z0=>6p8;iw}5%IcYph?{#xT$pP@NIdb__ zKIq{fxbN7LbIKi%z8kR_dx-{Fsf|}@1<$i{Y{QOYYcmr*Z08QRj;(}`@({RV<=p=x zg*1L+))EXv|2@$AexDJjSlF0YTiE`3PekE={I9?2Q7wGLFY1vWmmXL&c`7_y2UqJ zeFHGLE+D=bbakx}}7sD&9qqlihW;it9;hHy%&?YT7qmXbTmJAW%YS+)hi(2!~L zLI1~ec^q%?&L>)+Ga)w7;u%!Ip^SW|icsK(g%#zkl9!-^2!kSqc^$7Lr!*qjEesO> zJyr%J{yJ6%#PN{i7G!rw#CTmv$qtgbn0ODpFf%VvIo?sK8$Udzz2xD=%@)rhnhl-g zjSf6UWS|rMo)iD$7+<)NO{~0p&+`Cy+Zc8PJ3LICaU$Y!a~zE@l#C8l_{_wUwKa0V zS+qy}5;*S>!O^B>V?NI9k@Xp z6eFHulsEL7I1@LjHneQMhXrZH&J*Z4B z>;Aqs!d&HP>XU_m7b7lC-Srqmv!TB_M2l8?jur{c7~vw!W36eAM#V>s5%iGqxoyWr z8~-C+@KO zUlYtf^eloD#^r!z#$&_6s}h_3?|I^A0azFMKqritSB?rvPrYiGL0~{N*FQg)=L6oz zn&{vwQC8@{$b0#7+*qJIW6I&jZY(e~N7yY!Kugmq( zN1QKQQV3TNE!NFrs71798)1RX!2Z3t0@%)Xcwh`cLMHcaAP@Ft^{PVCx> z-}K`C;V%m{;crVNi9o?VZT3{Aw)@WQ+Osfwzg2sOOPbH6LS%iGVH!^~=7NoXf)w)m zfuv*6+FR5PZD!8IxU+Quh!Il1_MmAK)wLFK;whoW$f7RX^wm#JDNpobk+{Y?NQOPz zk+9tq>Il{tW3C9ZrbV?gvmbBl*XT>L?0#)ywCEQK4gi*vb z*#xjb7Mu5c7sr3@QUlEib+SNFfM@8=LxGIn0v~B2Gu>C|=@Vu1szDt^y_S|{ak|n) zl~$_gKKkKz)T69iivs)0O&onBc^x0gac-&mqx+NGhdqdmuyN3(Mp&h#lp^)T=y$qgco{rz9VV+X4oXDtaf09K7n=ZO~b*rt94b}sQ_gr7KFnwY|fv&CLz5Dz1 z8#1}dr=RU$zVF>W1ia(6#~CY0KKZHe%5<~on&YW|lqBY%rg(?=Ta4>*3zj+kwp6RN zia7i?vrfFcd~lqP_$MqHu2zAi$WphpE6l9@oB5t`diNO$1awqKelty;3vdsi$F_9X znbEa3Zd}7KYAeo+mei)6)T!m_3jTvpYMqUP7c(bvgVPC*qHoPV9pAsUm}V>c(1h6$ z3GU|*ZX15%{i@B*S^aAayY_3=9S1XWJ>CE$9C4GO@5b=l%hfQ7{f~J?-`KL@OnB(| z&G$3!A6U>uCZIHNXKY7*X6m-YQ@^qzGUkK(d}95e3zIc3_JvOR%|azIj<{R&{L_-y zon31^$_Zkvs_`|36NCx&2%-0NfJo|%F{f>)CGz$s%kGj+=(Tv+db4!HDgml~Ug6tp zkiCG8PQ3!*fUcx)Np=4477K0nD!`K__l;!`PGa}$86gSZdmH?DMF ze{v^?H1Tif*(k=K!>{O2`iwRG4j|n9j4>9VYiuRsi1CuGQFjZ;KS6){9M1_styv=) zYeOby$U(`Rk&rqrIkrz`>U3z$wO*ay_@Wi~EcpU$ROQq_+K>W;>GuiA3);N!Ae$** z3xo-~>!KKKVAXlzYkyyWSz9QSU3TPfJUuSb8%ADLbE&wgLV5;ob|h~Q!Nh9%d2$}a z2k-|%tsl=l5MODoFn!!^cwu=A!^@JAH%w7yQs1QF!{_KE>v!2(1$fq zn{Sq4pN^^#2u*R-Uo;$bwS!v>BfwN8=P-3zx$vgJO`GQaRC0^wI_y~r7gvUNt!9Sx zJqvo`D%eVPPc)jQnwUCZu5JVlM zcA~pu68zi8RiyXg9fOrio+td@2|Z)l2NyC3qQ{%T6JE&GGt@?uY*)`?6EI+s^y_^o z|H4+~um5pH#{BO`8I)ISXN8dYk6RqJ)04?;BzowlK*DD0U=lrp0!v?!6z0s(Og2q~ zH?eap$(gUCcp;(geFuhD#ZXONSiiparSzWfCdRV)S$Xe)W2p}yxJwek-Ru{wm9N6^ zXp$8b6@vsqaMjS~Fv?j9?t*o_S9aEYN!DQ3HHEv{uLJ4{K>5j@zIMU1@KwnKznQ9k za10<3#Qd>D#I>(ytmI|b>sY>t(mN`pGleY>*x)|p!mEr=KZ?|Jd6q7rSwBu}*FrE+ zHC2QESbpR2!P9e~Lsp__FFkmTgHL)%L;>>%rz?lwCDdt_!|MZ@QY4C&t&+eD%AR&; z5*=7N{DFL|9D8+Uho=N8gQ{F88V>nsZhwv~LE3|?eslCEU$&A~+ey}2H0j-V!o_9} z{VN2T-ejZCm3G{pUfIB=gL^ z)>7q2=pA|M5WktSOQ83XX8*~$#5u$!@s{bH00zc~Q=vBpNuzJjK0&9>ylxnk0hN6X zD}9trkK(gZ(;aMO@Dj@iI=N#~3G8$NnX~M`KSrWNWCLv^Ko-hF{4-Ac%0i~UXc(k? zDu*VF&OckJ+hX0Hrv}Qig%&@Hj_#2m9A7z$B zOY*|ug}Of8!PYFf^eWA;rF2!ym8!}f#cG+M(j55@NPn7DOM}=Yf^6MkK7W#&5py$# z)wBxDUWruO2&Kd{w}X82Q54kBlJiYG*oyYrU`4X(m`4!VwL@x3D_zv=i;sdewvp>; z_^=5Ke_VR*>iZfM>MrII{s0vLYX#AZ;P&A(?*1gT%?JZLq_$?rTU&xDFx$E;`{ZjX zxP_!yucLq>u+7GWwTxmd=h9sz?fMC{{bo)tG_tN3xfWG*jfAsj(ngtLXBu+vlanMZ zd-LE3lK9LQyIk^UO)>Q6xa4MSpHKjNg{oAZ-zt|e&Iyv;2qN-L3idK@P!%s=#eENG z@{eRuLQJve0gmSR)9LI6^#KHM-Lnclj%v^m5qA(j!wzU<%5D-hVZc0*Zk+%0@6qr5 z0p>A=n#{_>#r<&`Wqy10stM&WqDk6iBZu^xFxz1z?$7_qR$e*o3G9Q)R}i{#KrgHj z|Buf77rOq2SG}6H>Z}m@OGBYNUQZDe9hi7uz72RC^+#cJl({s|G%$~Cl0pvRPoa57OqKz0w{tVW7%^o!JTB?1%Rxif&?343%@BF|@H%y>cQ&A1I!oDJ7keCKH zRSNYJKtY>gtn|0rt_(upAerzRN)e?hbaUKU19e;m`I$$f4{eh)L~~lTod9Xy$hcM- zL~Y|3zb|=Fx?vmV&HK!#j09TxF9w0w22JBuv7gJ!^UgatoV3z}lJg}EuP82*? zxE(9&DGQm)JkBod12$DzY?U*f7p|8O8{3y?(9{Wo8|ZGG)to;j&+rD-?R*Ps?4Wf! z<+^E?U)E#e$_kUt@BP5#!+24njLOdJkL|Uq;wT%91IhWJs!vTPwM;E6y^ln>r2jn= z<{0y*%x>`M(Nze$lz420>>7o+MAvAPps(KMi_}EvJzHkcA;SeH5KN=w9`I>48g^r8488*XP0aCvEj%XK0D!z(!Re4G~mw0 z0oJ4rQ!)qX9xZVCG!FWX643`7S_f*52A)*Xy6B~?H$f(4YDactqWTa7&*h1J1ZBL?w7(I zBAo=eJLcmpE8RklWC=k~A>TN`Q<(2Kq+K9e;G?o$b2t}(6R(hVeB1jub!_DKh7+6vNs*eCz@#!N6U-$)F?yDo@L|DSmOd#Z>0Gu62rwt4Gl zkfo$%-+*NnTA)#1yb%#mkD~=h6^^tXSEbwN;`L|D_+p~o!3g@th>R^M!^$HxoUHQj zv3Z$JoK5Pj`@K2Xavi*($2^6Zr^qz|8n-toTT(+E&bz6>!wcnP#m64}`DF7H1iWRTr98`;mfjA~ z#+nT!K9i?1EW2tU=#%!z`+E7=lk@mf0wdEQDpYpTG{Ob2H|E!I)W%rmx2U}BLV4d_ zAirCFaqSR#L~hm1_lsVMtuzeSb1_aN!*t+LQ0v0biJHYw>fz8TIu2(gq5Z)bWm&mk z>jU{1F=lqK9{cTrs<{mRD+j%)#ukE%bk`!ZxEeJ>mSg%c`+JMXUG5fO5_ZKlr8>+5 zdwx1Ube;{tf2V4%Y>IMHJ+~ ztlk&!e78B zj0k|=+3RD#BK9180mlK{4Ijc<>#9KvrRp z*zRyMsqkOGMFsnK*yYZHwM-1NGh`ey;YCe3v3ga`>ues{x^1TSSA~t{QV+LIw*9X3 zwB=ZjicK71JliC8tAH~p#TKQ$;WV*%%(4%V*Awu`rlKawx4rpwOz$bw+lEvvNffyR zCG_4vNqel!9bd&NsAt%s4?zkVS) z5|?dsi&S6dI9(M7y5B6^-Zk0b0wD6fWjqbpeSz=An5}Bu+(g#lu=194L*x3gT;7ps~+5keu)3LVG{uUTU;W^=LGdc^XNw%3XRW z=(kLJo|}I-)Zmn&{lSj$8uM)o>?(7URH?G%;Se{oA#@4;@bfNO7ODSt;R)e`lLtS6&pDkYmeqf%0LO%rALC>E(S zx&eI{#Lh}WqTnM;$KK7ZZb_P7Hcjct&hb6Cu*?xnS^m_9v>1c3q(k@xpIbQy@~|>& z!YFLO=m5o*`O`G999ZMqNXUX^Y%0250%KSdgGLgVhVZ~!s1rWVvPh-lJWAzAl}I1) zCm2+eC-_w4$b9%AiIkw+wB&?@T(s*L)!bJ0VD8t&1&yH&cvIqe&}8XPH#q{>G+$4aW>0+VlvW3F2~3 zR7#@-8MRoN?up*4Z~66(e)p%%Oj!tOwjAZ+Go#n7w^`@&^Zq{58;;N9d7IHstKZ_f z1PI`NvvW8cCI`KRrm57ed@C{HnC?WZVb~+VCWa-z ziQ!kmh0mJJiMpQWV01l3dLoe?O7w*uvp2N_MbuB2Dugq)m0O;GWDrLWag>-RHQ>yt zH_8W>X!UG<@q*!NYc)Ar*h#Z(PPVYi0FiWnYSJ7|mp(fIxK6$NFBlTvWsH8tZwq3ELrg(bX;=erElR_yBTe2pV<(e;*k_jLAtjumn0gfO{`rU4KXt zQndH_woG1PuepXaw4Qk^J4Sr)JD+t12)i!tRHDYK31<6$1c3+|PnPsILG!jsh%kxw zqzo8HHD9MDK=R3Y8Si>AX(mS%_Qk>$ndf{bRppOVS(3sCuHufclxDW{Wh3n+%d%=KQ!;|B+ny@3-{vhanS2K0K^m3=*yZ$kxy|e zKXJ3;W*W44>pMr~S4V2+_iIQuIT?fE?>@XM0`tweiEn*$^$E_U$4}9o*<(tq*GUpytUiXc&Znn; zDR*ibT*fd`6p2c)nKf6BYKV2tDU&(@=ub5=TsBR!;)kM{4ZxV1meq;eLq^lA7cv=_ zRrfLbfH{|kSGTw(2XF;?r^1W?r9klTqMpVAE?U=}Mys!%$y@RB86toDH7}h& z$kEH*EL!j}e~;;B%OpZ;p6wto6~G0wM@sE*;%_Zvq698}{BY#!(j5OhuU8ep-VUMc zi60m?dNw*8`n2T;fTqTcVWSnIli+uhZq%iIJDWC!rOXeYaFNqmEf$XD#a=ih}otMH=BJ-ZptjH@_=EV#mzdT4YxH6cR#7Wj+{>>ME4VF z^IQpn8!2D9e~PhKgo8;b|NNR?DSbt?`LJrrBSB>K@-ti0>5n~o7}by8!O9iRBT?_N z1K<5GWILUW5eZ%)`=IHl%D@ofqN6D;tR4S1uvJRgwyt%K-v{H91gb z?oM4rV2(hB6ozkd_&UB-RBvEFK&R~e|}`Kinm@{>&`6%^*uNP?Zhy4&tW*CApUeZ9{- zj&o+-%q=84y93zU1usHXYotu#Sh4qp?n&C?444VrMl~Ut(Mx$0)lW!O=)xXkzf#HH z)BP&5uw|}+{G7$6ksUm82aaY{clSJ-N9UGQMaebp{5*uigKjwJ1mm0A*2}_$hJbgx zP|XthDavDkQ+tK=hg5Hjy!DcMAVYv*+Z*tr)c%@H$ib7lxi)Uqb#WZ<`%$MFicr=S z7l{|8kQR#Ji0?g)IvUXY*7B~oC|=&>wec#KD*!3mB#U)7R_?q8mE%c&5o;rM0l)M$ zM+!#y`d;mXy_@NMYw6>O=|Er9rv>n(4-+4|H{gw}ZYGiIAGEgVP>IMI>7GgsfofRr znJKYz(VBhVsFgA2?FD2~#0j~YMn=QHQL@})KxWi8s~qAEl6$BDw?fpP87ni0WrUcj ztV_i25Stc=LrG`9%1t0d)2(d1M{kcu@E*n&R)ydl<#bEHzo7LI=XqQb1g+^m zL5u630d1v-`?@|c(tp-wP%6!>teZ&=rLJqZHArWdKtnx|?Y(o!>Y_eFbuE0TO`q!! zyoR}!^&dcq|b?hC|NiU;Ey!>QM zD?=+&qJZO(3w5~gyU1XzYAL9`t>~*+(cl%kT^tw1z5iCPwz}0N?>P}+WdX(BE+1=#Y-4o!+wjS}jWf9*_i;WShnIf)AX?;JZN<+m+Wwc-%e3si0? zRX@@LrxUe?X4RvnzkQ2UYcCAqhrc0L_Zm!=SjXQR?vSoUnnKBAQLoYoOqzY5sB<)_ ze?VZ%fv^vJQ3fY-lmTr`rs$b#azZIRHuR8%G{$iS)Mb6B9_Y`6i?qpAiJksFp$b8_ z7;!{lmOmQ<%7jpm8cEyr6UOYP-4oc)kU3TnK|4GnnvI~Q57HgPIviZEO0%U}$R--u zc}sNX|MJ`L+l#_im5BDIk<9%M2>;@@TI*Jm58VxzG`a7~mYB>dN=y6tkfr1zBxD~t ztf@@&S1DHWFrU>ax8{Gpw$xT@W0~OU;rPmMYiwo{?QVO&cc!^&0eA?vZ|B+JkY^d1 zqTOOBMLdbhEY-I9K_qLnt8M0Ng}8&RlT|GZ^JSbS8tz&9fZA~Cj= zBTui0sQc5+;-l!AKsAzXmC6r-Wt&CaypudtjZ2V9Y`+Y19;^@YPcY*UvFP}?oS#@H zSdQqByoZ82NwtSS3$SOqzFDI3^P|2$d#e1e&&L-6d0(ZY z=~k6?^F&TOqmNO+$}e8q_!QMF|Zv` zx&sVgsGQXX_D0d9qa6)2GbhA-4(*%G?w+b*%bC)8t{Xbtz<^wNOxH0+K9d2H$sXchl4-(P4iORTSL_grq zC8)sY8(UnD8wH$HuzSH1u;9)XT|#NnROkuOCD9$PGhgvQQXLXeF&p}jg4?hbY+N$} zoh@_*Rix4-Fh|iTk~Aq|@RXg~!<-&@OqfWC z3}gGC(!-Z2osu7z(|bMN8Xc&vD@hcolPVOLZ}qg%yA8ChU&&U!?7;a~}<&Okf&g z*@F^EgYNPV?{dYQjOb8G!p)j+3mp~%{gFNj9nQEeG_(r$awB=1&&Tejh7glDteZvp zLm~LLP^0e(AKB_AKN))_Ba2${V{(-A??!*S;T0+0D}*B%_j}f}OBG?KU)~ec{Xxs& z&4S*+4JS&V@QE7Y6U$2I%DUsPu$EB_gdVBTOI4E}z|nYy;BIuhp=mtil28M1>2J<1 z+j$*@M=!BrjazaNQ&O?fo3y8E!lIz6R@n*!T~R$DX;GRv5?aw^&z76H9(als``{4N zbWCcVPqaupnhHDe3F{E_l*QO4Z`KTve@3CQ@S0NW#%G-exLXKiEmC&M4w73mPsn{? zfC=_<@Z!Nfv6AH*PeN+R3qYg!mub%okyY5Xt0ugIJqM&(;qNHr3cn%~ZUcaR|I46L zM2}LhE+`%duK(L?`QJ?GpWs~We}Z#zc7_xYh@B-$r85lF@(3ZzW{y{bJ>G_ZFJg5H zWU&8E%_ZLkr@G|FW_a`@xAWE3noVZnJU*U(S%0HejS~dKyL3^NL~nHdOj<$~(}N#D zZ9#3NX*AVZoMn@Nu*r^%%J;&z`Pf$L(DcH4Az&JIn#Jpz@VaULl;1R=14N3(DYmN) zmfy%l#8|^g{#3*s)6txY?L1%F^_>++{aQ`NpTk$+^)Pqo0_`#?umOPQ>)B+A(SB@G zWxKOsR0Y|fRT%^%ryZ(E*C6g}vw!P6$|(?&V8#<{(V$iIDG_ai8%;(FT1}E=U^s!c z=deYxZ&T)s$bq$}bhWXRdzlK;?y<*(AE8fp-6@6-(~r`a^@l;eRjBDaIY9%#sH=cV z$)LRGw=SC%7X6LP0BmQ7s|99ztULCV6e}cle)fyEeJp6S;Jq?KT9eG}WR{6}_cLPA zL2B4Dvo&G9L64KGm|wy%8_V(mWh>V$oey8yDNtNyzcxvZ>yqS+=3U%NW2-Od| z7YOcnj<$1RB+FtJY{g6`v@{vD(C_!eUb=jqk|Zk1@ed59=c#|h-${zqWv)i|+^Z3~ z^+<0Ii1>h;5VD1%Nm7t4OoJ}FtC;FR1UYy}kf*-^0~Uao7PskK0?|ayXvp$Lo-~L; ziz2y6476j*Ht5J=fS!93eBH6b7Ma4Re|q^7LO%CFJ;5PL-*F-!N^i0X&q|6pB@I+S zwxXVN0FMLgFg`@YH`5l1oJYjF>Gl)-FB+fZ%!;HzXdL>3sATy&;jC0Wwgq|eFQ%>4 z)w*T=`rYN`%FT(Gb!&KNvvuJJH>kKTb}8XJM=hrL5?X(aj)RyO6 zTUWCQo^|hiJ~Pvor{^0yAp$%H?4}~$M)^Mb!9G?Q%8}Bsf(Q%<4u}fCblxgm1R}(; zL0Dt7?&7WrH+rn@Ce|?^QvQ`9ACFjwN#)@Of*OTE@@HXds>B$ww_XW@Ycg-e{lsP@_n>mox4>*Zr_kti$#+^+%g4Lv`$Pubg)397;xubXk zn&A}8q(tFw6O9$R567WFGkm~MC|v^4BVvYR@KseZPFH9aYu$rbkr!c!OU#$Fe3Kod zD&rKJ(C_idhodp&a6{1-@EpJ*;1hA- zPJ2}E&Hl@j%dO;|YL7LX#*i7{OQbo*(Ik{M)nweN+b&eY{Vc zsa-Caj8VA`4;Y=Jv(a7a;%Bhyx&R%r)~QCtuv!&oAcGNaG|ghSKcIJ8MXdcWQ^ z-qjcgo~KrYt8Q@JH`H3S{VzIPsL@T<0pajZ5hcriBFg`)H>Y0%>jKf0mCJ7-sKuWs zDS`TiDwc9_cLsbj0Vdy_v4VdYT`!6~dHzY@s>wtSSqpok0TGdags6)wfatXDTA2WRbYd$?271oNI2oWKjynxa>7d`1 z7M8)F9g?Q`5HY&U{W215wTN>^y9BqQ2F1+c;>kDA z`yr%L>M<|@#FNh8vxe;*ViY)`YK|mv^@ycxIy^xF!=&E=I%UbLk|x4tIZWEnBD+8G zIrlFsJV4cC@Pp?%Q>REtiuF`cWcgZ<~diS`%~TNT%Q3Jp>gND zAsX`RKvU?0B-A&Hx$4rqDuZxgqI6e_jUT=hqg`5n;ZxDUa|b<5fI}x$n@W&D)u|tH z$K+#$*XU%M=-?9UI92cmwCIZ*);4;{fPp8fD*3a!l{1=v22d(9mHxh{H@B))cY=8t z6LB54ZF%>Zz-gXRvE@{&c$MW*WQj^ay&N>EryawLR(~r-K@rWeMV&lABE$$Y5seC{ zYpF4T4C2U%sZ}bJ`4O`XV+Gj9zWgOSe9fnJCY`}q&>5|b+f?IECz4v}C_3FGwopn8 z!`|Pq%rYoAFAiwc1Zl>=*I0&4lfxeAj)On8CKQ(t#~m=E8CC@@%0h~bTimEfWn}#^ zRmw3DUi8eF(Gt(ixS~NvuMH&`C;yw{)G^k+r#E?ltrq6p!4qd`!;RF#2sr`<9mnSD zsAjpaViMXTQ=f`p-aPBXF&p_+tHw(`BQHNSt{w}@YE=0V?h8%qB$Gwf0j9$pQneaN zC|ZRwMQ(8mNqpn7^;$B+~U8u15h5NK<-a5dFGmqP)fJS7*rw7j&d$Ff%8~` zrTD7tMVi#PmaVTF%*KLS>#*-4W9M`Q! zs0P8=*?a9grqxw5z;-1X*9ux*9ZvjaHUL!PxR>}T49vUGxT|0aM4MkbN>9wuNvHzk zuw5AH%=mZiFQIQ{&l94&l4DglPX7BP)2aD(PLv!=DGl}*M7QA1ez(k0jLE$T&by+o zUJAO?pauJ8gks7i^pzY%0J9?P9Wo3SgL96Y$@MZIauf&!k>hH#n-F;w*5t6NKv8CP zzrHR0@|F*^02i|m{#dfx&hl1~+G-(8S{1d2R2ag%Yn%Vhp(C48BM5^GnoY(Aps6kW zSMX!3aW;o*&9#tiPfwHU`!efe?R687Flgn0|8C!_0*nNiv%puvAfuHR3k2 zw|c#}b+GE^lSd^-lD^U)E+p9|VaYVulL9o5>bQdAv7=QYfz{P3JTwaysx5X@atX} zq~?)7C#N8DkPylhA5i0c-Xm=SROt(Xj}u4YiyyM;2k^uBCRk%+R3^R)@CgB#k~raZ zQkFkk{mbG}*I5#NqA;+|GaNCoMp{A2&xh{uMsdC6BvN1Rs~l|!nVGD*g?N>ZGk+q-e}ck)dTYGD+vxJJ2Q2#bvkt9Y8W&sT0#sSZdoDL zR@UfzdC&CZBc}mekXr@FwXW#kWskw2GP>!+?=t$;e9D(MM+;ISy)O5yd1L_@yrB0m zlqF1X)#awY#?qIlo$4>OL|$ zU#Phixgs&(1l5Wp0h2Pyxh%h?>c^j}mU$?qP_riqv}WlWapvS3dvQ#)dp7x==O5b| z&CZs0q#Jcc>y}LwrMwQ*inO4avE@U#Ic;XIxIHk*f-&%-TtO`+slj)#EEF&YCThVtj{asBH*Ul-~^;dPfsfyj3#tQm7nGf5Cm`YI$1_@IS) zvkk7^!W_FPWEmQe$rc-Is}F4j{oO&bR~M(XWV0gxhB)f}fR-7ZG%(M=GjtBe=;}!< zzCZ`tvBZhRn71dvtbY4ptEhSmxhgrZ*Y`p1QSz@&5pI)0f2H_L3YA5vRv9eSAytvl z2G&P^oiMd-X0EZOE4PufnNIdyPB>=`G-o%A!d+z*B1KU_3y&5u**rVwTH~COMW@oR zX;7BxgRi4T{2x84_y6lAf>-!++$%l8 z{fQp`PX8)Zb^mF&X?}k%RIRUGR|>k6)jHNN%9odyGmpx<6x5|OSw5{YXk2)qM>~FX z$0ZB=2Hnj_q=HWqS{JjLK|gtQI!ohQ_w##(&JBp-W?zZ#T;?@wDlo#KATUuwEj1Ju zmG7nPeez!xpFDxzJh>q2j!_-RNhnL)wQ>Jsn24PdS z4@)bV%r!dZR?IKud{LODYWNiKTziI)M_oDx3@{)YD#T@T<`D>5vE?Gi_jr>EGnIy$ zmAE|UoU^qF` zkrmxcIcLas8>*~XNo%e^l%a);Wv0k)c9a19AbRj%f=+6z}khZ+VX8qk&|Me^}^VL1MNOG-ncg^!htksyJhIb3Fp8Uv| zz1CA`zGdCjQ;2Kf#2c>!iS`Rbkib2%9_zP~kTEzP6AOgUUgE4mVFVhzWO5mv{Q2sN z)eo6VwQW<_#u`IMxR^sZAlw`jds{xO&R=wOiN+3yfa016lZo91B>6}yH=406d>qUUft#2H7YPedwRm=G#L#mXp- zKi)Ug8fWx-`9D_U0@m1X7z)?qG$B7?*v8{Af?(Khl3zS3^y)Q=X#+I$dT*v0gHiL0 ztKifPe)bKwmjAnO`GMPNSPKM0gg>$4f3m|<+ZiQnUj+^7<0DF9ZhKl@HNk;5S-dVR z(q!Zf+Vknzh=V3++?~GeU?0P$y_h>nkjX{vMmbM;&gY%7?|AmTe}Sw< ze`rqAyv^jN)$eg#1O%hhhtvhEp|tPW=h#29j@`+XP}Ao)xefB~0m{DTEwUX)AD+!G z2iUbfDq@>1`*3k@1lX|j1wm+|;{hToH?jyISM>NkzeDqxUm{3NBwDZ5<#Qj=PtK1Y zcoLoGL_%ykOB5Q#1amVPhpcxD6<)n=5%bP7Y4zUUagTXy-OEO6*!oaB%i^QGBw9jy z2&$mFAEF)<8^LuC*P?gVEm&8n%8t;~XGR(`puKK228v%7Y$y3hQRij$cZRc3C#DRi z8Wk8`II9IT%!v^_m-&7$y!0fmk`Ja;$$%6j!@MKE@Fk$h!W_3L(hC}(!^{l7+Qt7J z0J|$n!xf4VH5Q9=Q_(Y6m~8=;aO=Gu>R0vH z|0jC#wL7! z1Cy6?d&*w)#r7y{GdbEM(K%5$O%j6+*An+V&UL^c`eWc-Isx=|;Wy7!s zU2Sxb56l7pdeVcB@0J>Iw#)70{#ivUzMnJ40=BY%#6Hp*Cf1HVGzSy(RSZR^G z3^;0--vq!jEbmS}*de;xt@GOtC0KeSAJ&a$Go03_CcQe4?7CYEu5u*YzsnVPQ3xRY zSPXE=1JPsmU-Y<4;4VqNCG%T9?tKF3$BCN$^@oOixL5sn^#Ib3^(LId7DW}IdtHSh zCr!!Qu**IIGsSrZ8=Pm@%=QyP_$>jD-(QC|!kx)U01yEJNf`HPQ(d1r7*Y~Q6J5$y zymy~QPSwn8thk@=bZEbM*S49w{Xr~0IZ)O2MGx;U^(dUBT~hvCU9EWMQv!*+ zOi9-lG5@iUa($^!s=HYZUk_(GAz#&_fRVEuQyKz*H@J)w9l zq-&H@=_h^i1TRw2E%WJFd3=*u=$F_P_C!oNMd^g9V8@v&yG@D-F~jaSq?*`*C%Y4O z-D*7gYJ$JH#i2zQkreA7F5ftZZh;i{-4fZ+sFrrEGyf_Lof4jY2JxyMQD4;~!aT$} z>)+~e8q}>w|2GcLT~RAuZyD7=IQ)MlgMWLE|EZ3>-ZF;#=avyunTVMZC?rw}x@EjG z;md;P-}<0+g}E5LmgTiK&dr2ms1EX}nQ@b2Rh5XJj# z`)&)-O_ioOd{J|uz&FOAdcqs-UNvp0m$RStDT4%lT;#^Z1W>*UH%100B<46<0Db#L z@(@iVpCWFOE1EXiN{pF!9*WA^GW00XW5Z9QYBVq+*r|oNkO|a00T2q6MRYJ5m-vge; z2FfG%-Gz@-ZrBo4ze5ARAeJtXT{W|K?9*b)Xbk3io;tMSz%zQ4lOcLY-53vfQj$ay zLF!D0SgGLPmT8poW^0UiIRMCkYo+HfRngSlE9eHNB9x5kgR!3mq$j*Ay32Jv-|=`# z?G22lqD~!j)d5_}p%SSeQbOircfgIvp>{)};44{9gIJ;NQtRGC~bH5uV+BF>H}AW|c6W)dm& z;fGM9u{WBx$x5!sx-xC}1}6RRp9}B=mLjIho3Hlh{V&UXJk$_W0HN#b4=l&}pQO*q z?(gZFI06X?qaYqSVgg9L>PS5Z42R8Nq4!>Btl|r>)wNneT%2TeSo?i_6M0SpDV_Uh zoN=3Z=a_xPf9^4vGYV3G>@@_TfiPQlO5%VRwawk0Dhb`pCY~9HGocZJ&P!NWd?s)KM1-tM&sGFIV~>` zWh4@rplEZ0^Rsh;#kcV_@lwfwx%d8}HWpy5@ zQYgac;Ju=oyn$lo*IbabecCRc%a?rSk;z{{A}3opPAMHPLC!-oqD5YZZL8azJz}^d zx*sgHLZcT)#oqrpyg#==68hIpM)WBSF{0%>j}yYn1iGb4tsIw_M=7$+ay5R;2-{w9 zZjk|642OABF$to&D5^GY_C{Ht8R@M~H#`E9Fh2Y%;{hQDfoEAt6=P%YPNXGT*p88) z#l%}|tGzMV2w9?`?dROPr}+uguyR}M70_Z5KjTU5Z9)hfNlteHD!t2PgxeLqt2zI9 z`r+B}K6gem?#$uSS$%LFx6siVWM2ZN8E-xLL%b&CBk#DNh(z$K6a;%eK8h7vUVro8 zM(+a~uX-J$!b+L~36g@+g|iv(8>~&?NpIML_~Fotpa*F^AfFf)Ns}wpI|HBn<$|se z;&Q@dKrJ%b`24OYYIx4!C@gud;Vn+#*`(ZNxozD*2Iz|OP2UY|(eSUq@59TPeGPYP zhW>qPkW$h1RS<;5;y+>WKXIQ82#e^xw)6E$ASKwespeeT_6cFnTTjlpJ2?10p5YaY zE1z1weV+TwtI7j%SkR?)rC6cCMNrG@9?yWnKty2l$+C zo*V24j`8`h^hnc;89G_@;PG$2#`}B@p@korGYHc;B+UJMkM0s+#AannLyM(&e3{ug94q_|l7msok^t^5z($P9ZjsKFS;^nQr!&}}%mMy}`y6s4$XB{HS{LX_`b zhXg`e!0JqoF-yBTo5zy}9gE3BD0(G*&k)N%Qe5vjoob8#&jT&PF25tbynk;YN&}5P zuTxg_PuTna8MB~QRcj#|@e@5-C(#qw z3-xxEO;D7H3ui6c-AWA4-C9~sXUDHMQ+?Y2xs?Wk&FCmrTF$erHTJXKdZl_LBW88Q zA(@0t3CVUWnIGK`(@Rbsgb!Y#KVU^o@A%aOs)7}76~(FN*nk9F!v+8%;V(@`;xH-H zydt!O@rw1M`qpAFnFhsVA}L#`8Eik8Tm1|Oae+bl z5tf&z^9JY`=t%AURq>f{Ife4FRVpaY-h&>vLZZPGICPG7-x!Zn%3z@}-+SwwBimvx ze$Jz3i94LbJm2<VVJmkL8{ldt%`dt4u*uNU_M*v6|Wyr62TL>L2i-1GJpGB~hz5F(_2leL&x z%XNv1Zo=_m+I;^hsYY%wz%-&1F;v`qwd-FNpb%qC%j+_+_$PS%UrwjE^t$9$qFa`L z2hp^Ik8m>P=bqUnTMtl>H)TYh*JpRdRZ-NjK7Wwivn9HPxGhL-FF}?7-Xz#8IjxO) ztnIgFuKW4EfGc$?0C3Mn)&&TZSLSOX+M>ZDRH+T*ML+cNiq@GeHAgS*06q^O^KrX9 zVcv!GBmdCu8CA#LJ9?o4wY^$>zW(UpbNk%e@$-|n>+j=MyDI|)vWEc6oJWScpQxg` z?t*Rya+Op1pxaiXd=6DCL_eR0A7O4t<764^;Vo8auDvI4G)Rf%#-neb3-*u)s%$(7 zsm2Z8*66r6MY1YQQ1{*|#n0r8RNF&+0CluIZ8M}+hV?c%dm+O}jyJ2G7%D5~A53Vz;2nfSV zGm`rS(%0bB8}Q-uutW1y`Uex)-}E{6-3#3D&T%sa&4k}8akARn{i?&dG*AkG{_1Q} z;2eTMZmejLTSA-@Y<;Dqa*#0q7_P1Jg270rql_$quQrrv_gA5>OzJe)a@xQz@e9sG z`9Mmn6bpN(V8`n(yM?(2K1IG4M?zQAM`}U-Bse^iOpEc_)0T<6#7KCDtbD=b&zMCz z+Jy4~Hn8w96ez6HBtPXWYyM@XMXY*pYTL~+qra>GjmK3QT$Rjm3WNf#8|n+XAl||b#L~j?TVD%|e*R`9 zrF0r~YDVJVw*hlefIMiyLA&HnHYk`AGkHJJ|IW|n7g%lx3{Gm3L2r*do{5_EO3$hI zN`O_KW!`{(c|dFuX76scCsX29!NleOng{MftI-b% zU}xG&Q8s`p$|ww3f`5_msuk@nRG?lkB$ce3GHtp#ftgBXs2wlw`?1<-JdG#k^pkkN5-ICp+$x9-{4r_sn>joEbGiJwJQWg79UG zlONJz4dFfR@}a@P$0j_~xaKa#ntbD6&$qICZWTK`GgY!qi- z)f>(6d)lUPc~0eGxmEZe1YyY66qp{T!?@|ZsMR54hS7#=(=08_?ym{je9DR?c@Nqo z)|@7savn3CCZBH}2NmDAoKWO$Yj_?>>{+tJvm&341L$|J6)Bj(rp; z@emlG@iWO>ac-Uggd)3nQyvMJdM`gGhxKMz zpbd&wvA$$4qm+?g3mhOMOyB%qjmBl5kN;iL4mTF?tXk@aYC@$YVh<+8PSFQAheuPegK7=>zB%|Cshc189?`lui zfM*?#fEC3xyuqkEtNXe_uufwJe!KfYd{LPzMIBcmrv5gE+hgA3>0kUdE_<`40RkcH zAIxOd|3qzn_hCe4fSSQeGIX>u-vp^`P=54t7lg4*{C|wS^;?zu_Wdmaiw5cL?(UH8 zE=fsgkPZQnT6A}Zbhm^c-5nw=AT1#&Aw2iOz4!T?eV%LY??15CFY}uBe9t+?YhV!- z4_;A!7bQ~?kQ4o>J04p7Xtdj0DbNsXT2C0FYP&iD1Tn(iSHGQhwb|fk?^BO8QA9Lk zrhL2HfPE|*ooTiC+R<44EoJ%|AM$1URcz$?L&~?qgs2{91X+_GRswgB_r_ZYNx~Fm#T<6jK6Lb1o>1(kzD&UT(_`ChwBUbvjcoA zww%zIf;*6lhC1ZyuT_?U`YWoBv&~neZA%EuT)eTpSQ-g?|J-y8nk-2@z@emz)Vw07d!Y-#X(T zhqnK$@c!feY_+Fv2tCbZCx^ilPl7Kv9s%5+pLOd5HGk^TynX(gaIyr-NSs^wKE4dN zqxi3~(~Lg(-N8(Dzd@xT0bFa7(y&^DF(;k`Dqr%UCyTBG#2d|T+NhmUnem?;m6BUamZyJdwBWopzGY69NR zF}4S^s#zFvxOId$9oGkq8BMl2yziARIXMjI!(X~F7H-#dzn#sCztF(5Z1wq$B4pE> zd>+V#b7}>=EoQnnw1Kz94&-ft@qxL*=4WXzH{8TYW?ts%6R`a7#^lKc=Crrn%j}Zl zI%M$19DvWEtl|M>!(((&uy|7`ex4()6<*t6CvL&nn^Y8*Y#i!W-$cSBV|N|!z4G*t zC*IbBMWibnelG`3pxRk!FmGg8Axo%=7jbdXgC?-wrIjEZ5_8YC5MjU z^G}^Wqiyl;lpJi~&WJ6>+RXzu*uFXkJ{?X>9xiAR+k9&8#2faqw;iVZs|Y~Q;gEFu zsT0J5{P(68Rw`!)xuEg?ThsgVf~NWBm-i6(@+yG{OEij(g1f+g`!47i7mdXGSYNiA zJl7ZQc}>8b3D^<;yo9I#IkK0S>n##hnyeoeCoRC@q~X)sho19qSDG}cefiPp(bS-NOMa<=`btqxmL5gUpL7B5JYJwB=QvgK!wXr+9%Z(Qg}`-N zOa?@sM7V($;C*_YyB)4{$a&fuHdA*#);iv=JS+oKd0A%i}`yfR@JWc>1(F&*K+v1Y`qiH;Fy$?==VQ)YS?V z;It?5#qC;}Hm|!^j%{bA*lGjXXcP@N;(|;}G0JHGWok4WM^n*eUYHCZc=-vO&P)S* zdrr|xjVR9UWaE1b@alG4rfLt*jNe-#&)UA2namUlAW!a+P&key@|7t_0Z;`;rnWHq12$6ER;Qt z)Gj;VvJ1%f*?Vh#SyqmCnA(kxl$^ zb@TIQrtR&cE0n7yAt)N|^lDP{X$-K>gM5vj+UU#xU*qvuB35o`6!OwXTX|79v(ashnIC4PlQJcEp9A-8T80|DHsHJsE zTUwM*6FHqzW3cI=l)(BgfO!z!;~Q~8sNfc(gB`_?KUm5^TyY)*-dx3Nso*!R=!fa{c*%X;fM|v@Z=6)zf3(wcuZ@|i$ zk#`^>_oa5x)!dm0;A{NqjvNZZ1ut(Id@G%RZgIi*jPLwGCgXi_PvzSyBbAunl}Nk> zN@^leFiI=T(<`)yN7hvrKtV-$#zR^zA#iCyWI0mLl*AJ5s)6b10w}MPr`cb=^O<&b zp^Xz?qe!OfP;N(4WmmB;f_!={<{+P5y*yE~@rPaFYS1)wH)}X*fnH#vXI1$AE4ZO>|U^M8ZVb?D^#++OCXN7I&z4r*9eh zYSzL+z2*4m%^%xj>_D*hshg1u7Mg2Ce(w9w>8HabRwXuor|z~W>p0zh9gv=)JiG_+ zG)qEyez-qhvh`k05dS8N-9?|A0$!5Z!56D7X54T9S*!_IzN8>qkTY40K6gEI`m{+h zu44s1u`~WHo4nC9LTtqyjbGv;Inn+0`L}mh>D&HeuNbOcEi%{B76`kR#fw1WMb4x8 zJ1iCLIGt7mv5n31(ot?ja*TuHlc6r;B1gW-VXvRBm~A+gpTzK`sn3$=X=Y||MtJ-I ztvPNh7LGVZOj9(?++B=Y;FSv@1{5wav-plPs=O!92mvht`?Ej?WAqFV;Qa*CIvnwvIhy%!Se5i@L5FHMJsRMVteO2-7}{N8|BvEM zPoENV(Dv-5AbeGCjxKz{70i@uousn?0tP`6U=TE9@%>{E-2OzF|MeKE7123Tht>^hZgX<*?v?M$gjHn&mhSDYjkX z$2QIXyiVYog2j<+lFEE1rx82wEfd8C(gG+HKNXvVfJ_7A^!6s4rF|a#$VA6QS$f4N zIRHxuqSY6^I->dgl?GSxX)fbb3lf+%mz!Y2qDUoLsP?Cahlevre6;;?jYfQQfwxWA zCBq2c{K2d4KD_PrsjqxuU^ZrhmvI`h{AAOePn3yu5J0u#-!^$p)lv6~U;8Aoi%Bw0 z`UI+BB(TYw%Ouz28p`87i<9ex9mn9(5xQlntq$ti!N=E5$CFkIK~#iQ)?bu*>LrDq zyo!4QjdwneI^?hfK6Vk;ruevhK*%jGFHl^-8P@@uH(uit=Eo4Z<+`H5;nePkpaLX# z!J!8>xj3BGcRCXr!Fo-iE=OCQrX&y2uP~|hl!+_A2P4N7Fdn1a5 z-Iyu4&GFjZJSGhmcTWNLUg$jCntXwelL0t91rHj(EMY9>-Ep&@blXTU8pe<$0al|^ z&*DeC&lWAu3N`I0WQ_bG??D*xrfYkX6QTxkPFUbL$qA-yx(@kv-@()(9xVo=cS#(l zum$Q~m8aMmIpWR%tB7%Vo>V}Sl%L#F4soVuGNs2-A`fJWidU$3M)A#q9>)x6YZ~}W z$Ck!*7~cqDA^71=!pI@8Rl9aeP5+YXk-~9|D%exV?@&BMi60BDf4!j{T1lb81bH&= z8nKK2?~}PNfabjpP?XgFR+N8U-5zPS0O1SrWVS;J#+hZ%^3E&7{s^&<2#jM9v&>vQ zbGIU+Ni9ec;k!x6M1TGJ(fmQFSlil@BhCCu(EIZHb;ruLo$BM~PiAXLLc%jP+!Q2` zKX5bUmE;ATVln7wt7)m3g?ga0ofP%7lK``$r!6eh)#qsKY!?sx+?cSsC6@pmG^PYG zgvDf|63)>W$DAACfn_9#4aS3HGg!cVY>H(KLn^8-WYq>|21-vRb=*1TmvAY>3H-QD z;t1X=+B!+en!jCM;=HsMS67#M6+d|d`b(s!IS@5< zmiqL9>{`I2u}*X6bL@WS)+6?`X1$N^Xr03s0z<`?gt)KeAl&W%;13-JIIlSs%oB** z^aMFtLD;46~MbyGE09QDe)6@vSOCn;* zhK&4>iaH|u+=FGvgatY6haNIQj7Q6k%i=dj%bjpF{fGTeBy4#l_(em$0_$!JZrth} zt}nqhDMD}@v;7&QP#(>G;u()cDNRrSi@IXGle5o+^}a!#`P<%E%+#@{xV)WE)(w3q zBlUPUTl}t^ar(x{>=HOq&Sqw77+;5DNU*`?yp1fBCPD$OLp-W=6~NNqV{!)K|bAZ|5lZMTJAIs z0E9Q5a6J=16cMG<3@_0NzZAD_@uZ~zO{QY8(yu?%C@%WD&AY+{@v_};(QF)spna9V z|74w^H^cI2dF*J^L(rvzlk<1442VMiqBHwR9~Gnd&=`sz&I?Og4d7qLN4)|a1l{U- zb+Ty*f1~~Q{VexoRcR6Oehi+pjitX0Jhkw!CJH`ahp*SVP-dV`KM2{Pozne!*rYW4 zfybO|&J2_tp|JnM!$44+yE_odzbrBl-c^aT@s1?01)l^S99D+h8?niIo8K~W<(sFE zAbfQEqiEKBz}n^p(8T1x!L;t3(P04CIg@llUH!u3NDUi);t@UC3O0k5cYvKFFySOr z)?maiw%A+#CvimAg2+X$K6MXSofuK3p6Bv;uSRKG+V@wj6@b^S33>vJGQN0htUo+L ztD?n`ecKc!I>M=Du z3`;C2mNCK=a(FCgcsb4SZe_u(B#)py2I1$ z1O?)^a*uZA$;q@lw(Y^8%rp=h_|N z+3+8kXzlTOWF}qkF9MGEk?gUhr+2dDWjepWx2hW#Rr*|S>|S&beQ&+>rEodybh#z= z$ZZgQilSuja>dPB608Ctq=_!;9i91-fe!3rNS9` z7_u&VUI+7i3{#~e48Y3cXj>h_$Uv)zXb5C1^UcgbUwtWjmeDbfCcgEFz!_rm4CjW^ z801R@Z=Qr_;2c>uzcN|r@)deUM}__a*XT;xg6{ve6f}L-#o+_gQ`*10P5!dX{eh#WQXg%!F`OsXHH{4EOan^R8$?Ij>B8Z@;3Q$lzO6qhJl=i+B!ojB?g< zo{f&LJ~HI#Hob`650=pv7k%{SLj-NtTIGfovL~v3R-1B4F}dq>=9oTmJaW)s9%s{C zYDBGwH*1MfUpa>VP&@jpnzRMN{jgx04QrYsi>kYPjEB8O?t6GI&RxSS$Wu_3gkmYcNFuo}#PVT*Q9O z(PA`hsX+U(>oV_;CumBFU+WN{&1hImmL;FYC<^ss5|0cUOATmXmU~JINicTBqE21F zC>*JUpnNY52_-o*kT&@<&W;ySu&-z&V?v4V1f>1m$u!^u1l$u$qxU}cl;7|Xp7>p)K zxwZicSM6U4_s=umAK?c)C@3gucGyL*tXhl>^eNh1grSd)m!OxF|3ueaH;NI~tLM|_|@4z#KJ7~#RRx3~iP-%I;^1HSVtpp_dMJF!Q z;PZEv!G0TyjoEbep8?4h3V6jQaq7ux$jcwOdxwU(C5X?zvaFOwp3=|a&oaM`;Z)QH zXdxb`9$sx0hXMwtS9T+%`u5~oA|>+ofM$z<#cRcxX>BKHR9bZ`=>r4c^0N}p7zY#wR$+-6WGL+b#)gei|AkyT;_$>RtR)^ec;Gn_&UVYF!K2S2{vXT-g z)WgN-9SyfP%Ul%%0QVV0Ra%b310^%bjzPg7+eP)lgALd~(q%!z7MW^@q4fr+)E1xb+sutkl^b%|xb@Y(>N! zA-#7f85lxWy=e};eR7_C3s@~vxIT|3`Gw|GMDl8;E%+oBixcbK5K`^pqD#oTzf>0f zmJmO+D1w>(f(W1d8BR|#oB4~puWP20fLlNQetFWdbu08z5rTxeonJEN z=Xp54ch84^I2b>p`;lNWc+~EF^&LRVk3AaWOH3I-C|BPOI-==Fc6G#F%0Tt;1Km}1 zs%AL%N>Om0UBBC!or>UI1|qR>YPjS%0kHCiS%Pmf9)U{s>sw{W5tMPPA6*6(k2A1H z7cc;KJZI{4O)mOFXXD7PCU3r1dE@|4Y111ti>L&0TN3_;jD)g&GG9y!_p$9Y$rpp7 z4Zt4H)h50O{fCic>)CD-M0zvf3_oK~sfHZ-)k5u{J zK&2CSH_7)uG-1#R2tUHGLS1>!wP?g*Lo z0Cy8UQT)+s0+RS{rGO#@2vBJWCIiKNt!o7!h!8=j^6GgHl0$7pf1e(=#aOvXfu{Ly zfkgjv0sJo!zqT3)AmTR`XEKGk?O?(bsQDg$6mU|nc=z@ZA7Z%(QW)u97zGZ19!Hrj z$D_aVV$Ap>kdO4)esn_8h|qpm5yh;)fNxy&i{R0wq$heJqP3KN$W*2p{WVP+^Z?&YMXm`d1#9|l4M z>*^1Weo7Xl=k44ue|X=p0{1vKUcTvkR^CA<6+dYAv|8OPO;5deRv=4OO^}PFAFpVb zsN^e^5LpQS&U3&Z)j@4snf2zBPTS;JT554SHi?JOwVpNmwOBsV?NQi>Pz7u@sWiiv z11C)n-6)tUTz9j;>&((q`HwIn;hrc|okRcY(a?`BKp4@8S=@DY zMueS01MMGxOdhZA%oJu&ZLBbWIxJ=TGCaId^YgO3RE^&0_1c#Dept zt-$NgdUR>&)m!>f64o)NJ?!OabKb+QsDH13J|wbyZF~aloV1S{D(}{DiBhU!_@VsH zGWYcXh=z}qqLP?B%yvbT-(Vohp_vG=c}Pn{RuOZ`f1ZE8(FfpwYzR&p0i#wD);;Wp zhQKeq1)lT5xsywdZ$(KskrmQ135i~xg$k~$xHK>Ig*VH%0h6Rnb(&;32$1wCK*Jyc z!9(y*Am`=kbd{VxHC_L-<^JRP7p&n2z!&h|R+Tdz2$+;;h&t`hI+)ea-^)H4M2OPi zbtCV!hJ%S`8FkTufV8XQ=RhhV-KO_pK=9O;qIT~b?_(e`^yujE$kOnm;GK}eQjOz) zG&VP(hf{Uvtt0a+GZ)@z8Ed{?#=71MUo1eox^dJUv8!{FA=lii`h_nUa$cr?NQ)I0 zw)8x-P?BAju*xcb+w40o`a&{XAzkB`J#}QA#}e-tama|+=gUT*(rZRC^E$$tmy>58 z@d1g+*1bSg6}fJ*6}!FP%LZ=4FFSTh_ix%%GrJmn#1!90{k8ayJ&UN@V^Q zf+pS_6O;nh_NSkcyxjNSgX7n?&|^zl_Lg~t7K`Va-Tn3wVJ(}a>X9_QTP#;FT#&4! z3K>2aIEB4oHJL_ABp72gj@mXjAdFOTT9mrk$y3?WkJR`So~hgm z!_pTl-yF&4)Plj#EdSmWon0!F<)d^Z2_r9IPFDxz74|lo0ozG+b0y{%LB*P@A5W9a zyN8~>Qq<6mm-9_nJZR{R^1Nhn=-0viAWOW<8ESJ19F~>-&d}ULjWJUIx)ucM`cIYh zpZndP-%xQoSQv!^Xl8~9oCc9HV#`eBEGLjpW6u?rp~+Gz8&%(>CBK>PANy($DDT;G zZeTCC+U|XsS>N{Q;qDGL7hH+QwH)`UO(4Iy*bEz%SSoRY?tOkrKaB_Oagm&z6MU?ob~^tb*Mm^LWYq3`U;C(T%+Dr6P6Iz}CT%4ar2x z?AoMk=vTBin^`X86Pn-xo(*S)d_uoMFkC0|1Ejz&0v_R(M93$U!&|(+r{K32^N&wx z@u)ofjE`>u7CmhNAECS+&MYnacmnN|st2CgB*I6)rmhPsEK>6mEZVTn-+1 z#p6)aVI4m&`Au0wAeaS5;^Wva8jq)4$a~%2h5cOecI1Bp|54qe6sy49xWy)ejlG)j zc^nZpI{U&`tp#xkI0^kZ?`egd2849!gYvNS2d7@eOmDr2P1IK!5JJ7C+B%A%2nGOL z>ON_2Alm#z%~Btb_Hdb{s{EGx1#rfL>qEGaA!Ch_0+uh^=KSqxPgsJ>br3~022InX z!>Fs&q`P_Plg;*=_kbX(l36+<_6KNm*aw`+SLmC~-6Aj%=AZ=Whytgq+`Us(qryv? zuI+$@4~~L^z=~Q+yrY_PMlF9FZMS zg$Rm@Z?!NNDT29&%C8by%B`t~S|H9(@@ruN7Mp)e?heO3>L)4Q+uLruwshRj6hOCN!wM#`Muy%=tr1>RVAA^3cT~h7}#*=`x%PagQpK8*e z;Wbhl&Z(N(04+k!!Nt^I`}!_hn$O%m6TRT5ThC5unnG4pWgP;@+U2FnsDj2qRKhd5 z%rPmjFMcE3^l=@owMuGmYHNfU}g>AMp(%!d52B0&2^o zECpH@*MAM3wzH*obllwe5beu!gd%?sdS<+k}yhQ^nkQ)F~r37HA{MG|? zeCQ2FEM7$Df9#h2QpL*qN&2G>+Fp*FNAM+{v-mcKXVT~Wf<8XlO3pB>#-9Bxy!Vf; zO3*;B$?7k_RXN^iTJvGtcU6lbHZPdE?kXT5dtwcqndZ5o>-C`E1s$6H^f63cYzR9q z5;xq4juV@IWS|@$s+Q+mcRDynN5=!;q{|xSzXh9i>)Cm(vuk-ISAvq#}|*35JP#^dFn6C zi6L=|R^eTAef)siqebpDtx}BaT9b9wTR@U!p;{~-n^m}=p9;7=gGbrnZ|9?XkaZeNP$QCZHWR z7bo!+2e!hR&PK;_a;YeRpPjI2$vE^yZyGbqYkOk*k0?GW_!gIfRaVX>`pD>04ZnbB z5^J)vmNlo!z$&@xIQyl>K28uPVKUd(=aS!!6U4$LtoSkNzOMFs6ul5JA`)+Ty7eJR_QM&dS)u;&LNFr)>Rn zUg~}BORMZo&Dpk(7aK*{--(I(*S2`Epm z3zSnbz;*sg%bbwB9Q<9-cFn7=A|zIxbY{TUljj7cmmQnose}O4XKuxZRDxH=9YWe> zpRmB+%$dN!AY5;QOeYr4UsA>LS}Yc{q<0w)iv}2Kyvz}|vn$l+=Tk`gF)0p}K@hSr{-*F!xaNOOP_4v}%=xpbW%n_KYu- zmJxl*2UU|Rw8*hrRT4iEGW>nnjQ&kYm+Jqa-Tn0iHGC9eDNEE_(A&acHFeRclG)E! zGD4GpV<))^tchjy=dPF7cO8pu-2PT#70>N1O7S^=_xADi9dEz<{Q}+e z0$DGAFj@DHqLAky*_fgt0sL)EswT4KRlw5;tgqxok+{*G^sDg2RNA?|uS*hny$g+2 z3gddh0}^@go1v4>N{ehkXt~T)ttLD1!9}51DGkNT7Hsp~s1On0&>B(u(pG!&2f}9@ zE6BdAR^GyShfr2F5F0rUU?WHRNVgFL1gJ4^695g5`gg-S?fc#ENJ0P``D4Q~HfH~F zP%QFXlOi!L7`D!%`+fF=5z=GBb6OL;q(eKHM(dUxY!`tvya*lVzZ%|xxI~W%Qw5)A zxMcy0+H(nZlgihgQ&4|2JOiNNodOL{xT>i|VBO${GUGCZKmChhh`UY3b!A^ONN)p{ zA;vPPE}IlOEJyHr+t_;vNW;@wh#nrT^cCN27T2&aUtVwnE*o#5eE|U9$MRRI^q1 z9QsC0>zGe1U364~xR28#jdLfvW%IYitzY^n=%{R77}IH)xAt9Tm=K~FUB0>TR3}E@ zLeq-f%qr$8Nsp!!2@<`R?JwhN`(5w;*f0DS-$YWnO+tr!(alGY>wVK$12iT|-4uso z?BP`gcaYs#7)mGB+_YMjuLvccVx!9*p;YaukIdtrD}Cz1r(x*ctd3bdKGhyub!%bh z^RWBi@FA$K?N9I@<)qR)>Qm+HYxtstIqou9!MyO9xB{*2VX}U&(}M7m#d~t6=Cn2{ zl_V^WHXP#d&tW|okI@f&se<=vBkQce3^WTWu6vyhO9eg2JI!xJ_g$Oub!vfo84^yF zj%;TGTl8ZRL?2;ZK(zPELumjuAkB&r@Ma(jKUKkuaQ;#s`9hExfp+b0^w0%@|CUNC{? zp3A5pZ06uvh~UB|RZ~jLizw$np=j(+maY%vX4B8X2`jvR;G-GLE?5xU#Vd4bRTY9} zEV$sb3O2R=K`1Q}G9>qAAh&7)4kEhGpWfre>{i+&u#lLvE?}2YGYvcVrgoxLr^Tej zpOyt2YM1$UR?{9UaaI*?-;#sZkEDYdSjOakhDS`TOK;% zMZ8Im($ZweRjyl=z|;{n5;HkAnzc&dm@$h?Tc$|`wfOz^?h5L$gSQ|QIc6nb>1~?F zUOe(IdbIR%7d#tzwDbxDzwoM+(~H!Y7D$s<6J#=oWS32HChOkZeaAQ2{1C^5an1zt z-kRMBfjD~E6fU)0XD_a8^ZKxE!p0B12S~O|s3DGCN7i+Vydz`OGgRqV7dFI!JVaI< zo!JviBqcbNACa?#`<8Tt71Ur_w30>Nvr=*hIwL$~fMVO*z~0%dyCt6R^}L)>|J*_i zAi9rt&=0gxRcU{ov|gY-jU+)f<#OxG+agr(-PzE;5Xl;k4oCn;Vas%%q0m}9cdk1R zIycOi94ejyJAmBEpclN-E4vEN24C13b(S?Q-0L{p6Hw(1wV7rTx25^}+U*x#ZVqI~`O$Dl1ByeG{E8Zl*3?W?b`!*EcJ(evMQtxZ=Be}*9 z;O`Qh3ttu3|G;fxq7jeVt9lx2x(5t-*F$P=LdaMr zydfQrdS)->e0`%i+>4Ye8ViR`p2;an2%|&3DU$jzWG;6)*Qe{;BM9qm1;|DG4qa6f zb5ApLlz&fyU_fitDhV0#u=5R}7NBGv^pO z^8LQxa-0q{wBUa(F7(-iHt{l~I0go2X;geRP6s#X;hD%MmZ?asTDjgv$ z6>{q*@dUQ6yV-%$Xgm&6etl?je?745Hke@j2EwT#Nx7UXm2ZN?CpQPxuBUdqPS}x0 zuALqxKv0dAH6~Afuc#vu<&?~b?cWA{{Ep9n^?_0a(3(m_h^-0LefJ3PuB!Iu+hwj^ ztN*@Z5H1IhgN`zw6ZUKN^rClMJXpUKnE*WDBYZZtS7fk&!CV_Vv zD`iaA-z(m8j#k1-0nme@DCv=}&uV3J)-F)>yRm!)FdK<7Ps$$_xgf8Mk;_tBLe1$n zOg^uOz&42_E*#%Nt*Q9299y$3J=qiwz4EcuG8khhH5cio#}ucPv|{6mWwHbjuFb#s zM*|)=>(SUpL_UQW;Bga#D&L;N1cdhD5RV(oko65M*>Trn@sZE$jllsPw|=`PfXB@R ztMI5_>s0oqeLzaBLFEkupFxnw+I&Kc@EDtnaT}`eU86=$#DHs&Dg zrZqP-4zL|wiF67!fU|r(f$BK^@02Q37K>q3p!gt+(|=m1{Kb;AeGE8!?1r5{H}p|0 zG$l>Mk^CVBsH1udtQ6%wp)Da8>UhX`=t11S6N3;r%du;Qp49=ZN4=oS@iOGi>kR<% z-$LbvAL0Qi>YuPN)r8n;BR_^c4d;BnVvCa$t!yaDNJxJPh^l>f2g1DP&oS z_V}4euKoPXTtRlgB6Khr^II7*68CjioO%Oq3hAbxd~)tV0=gbo^I zas^q?o{D8>V5V;KG|^EWCUT4`QdK_v%xY#pty0GKa{f6Ds>kU=s0O{%N{3WRy|+yS z<{l&ij4##g(@awzqYO1BDNSq9EIF~e@X`geI*^`hywVLK(W_yzB?rxP6Y($u*9mBS zo5L{wVjRG8X+aa-8JksuLmT?rZ{Qt!c`;7@R=457bz z-Q1@HsfZm2h~m5L1B$ZqT9h|2x-7igjyGOTe265HDmCC%C-=$!R=N9kvMIXs1(p|3 zjOqVUjQ__E_Za;J(N^_pmoooU2co~M#WWLfxnZK@=2GKtuA3cpyS!xHEa?GuxQoL; z(x@9q@E2mRGY!?tx;D;wKwKpx=>6#}1h*S7pcYBVSe%)8N{~-yl`5eofpCTi+%8PT z`X~ZFvwx4IMV`9E+n6(aZS7M=D^|MdqSf6YsfAdS7V_!a-g4z4Ab|p5+eF3zd$vL&lYT0}YD~(N&=_<*2`b-_kG>4mtxq z-MTxHP2m~jE_@g&@8o<4+L4g%!evl&JV-eN;OY#S(@0|Ts z2vhKWlHZVKpGC)8O~?HbjsJCyNevQ8`>T!$5)_bj5mT|v_>~P)0zbrj1Lrmd* zW0Y9of%SL0++qQlkq1zEf|&(Du`v}`rNL`mO0!SSfVUyHma*-TqO46YMt85+%W+0HyR@aTBBzd;ejR>8m% z7qgdYOhTqo&IpkVPjhB_-W-Uuet#(Kr-eKfw z&t11rQ@Sl=8SU3QwwZTo*5{z>FKD2F9peZwB)ZOw`&wCcXbGt;srWw6mRCHvW@Drq z+d0^z{8`~F<#BknOUowrL`w+*5M8%$9^9_BY(A;oPGgBziIDPX_v6aOA-HkNF{#*6 zf4pc8C;S+Z8~Zsl-l$uO>I>qLD}Ff|3h}WCR(1JWXnB4@{6_Uu!6+x5Hw+PuSM9># z>w4>?Zx{;6i$~OUKBL;L#zQQ1NWJsz%1hf5LGm;Ugu4|CXmciGjl>hhr$M!Q&9)mP zM;Um1QkWpHQsmcwFe;K%on8rI1$h(Q0ZL3pPsK;A6|NI5NAf3`_p=f)SJZDZ(ImEg zm~51a=NhQ0J>YmWLaly{JO~&ePSx0D+;V-@+P_ky@~I^S6J&nJ_-)Vzjq^Dw)vgnss64kG3jb`1I`Q$k6-IOmUM1BK9t-*fh`?2# zzVH+s$X6sL*B(YDG0D};&c22@^fh))yX2dFeGYA%@A54^64=2 za(wepDIjpB7CQB#Do_Q0(R|i~G$-umk>w_Ad}eu!6n)f(rC0co%4}beT@1;u&!$F% zNiLU#b7F%XzP7wwLkVsg$ju7|xV)>$l2bocfW4xoy6YSdUE(Nvu%5D33GruqmIn#? zLeatZfE=1h@FC2qpHv_D^XJ+DBYO0~!Cu6Lwl2E!pBi~)^ve4N!j3Tp@;%AtcVrmO z&!;T%UzuUs@>s-vU`JXidRm>)T)vKinZa|kE`rU$>;twLRyS>)BA`Z6v2hzlM( z4FrOn-5R(_n+z82tMTT>n-gcDj9dtjOue$zK+!9mFBZwtlz-!dXs+XWe-ed8Iqz(v z*3kU*wUfd)mt4C_HMPuy^jbA+mJ=e*0Es#`ZQGe zWt|zlBLa<%not>yE?>OCHN)Cr(la=^nc(<);<>b4eiGHBLhNd28GviqJ?zeipeM+kxhImyg4>GD zYA~$SyNOqLEMXs-o_%bp(f1r~S052YXzPtWvG z(eOM-q>u< zI5oUBIAHHs?YZd{>xb62G~P{3oa&V!c5t9 zlof9bya^WX?s=$tPIbwo&dI4G1}=Ad1MuHS+?4tKBphzC9MocydJ!X?5-)xz)-JY_ zk9&$AtAr6(qECU*I31Um`}&y#Rk77&I)~eg-}E4FS7gJqWVfsv&y-2Q91RxbbKx`e ztmA-A6M{A}l}(SxNgqUF4lL_P*iu^d=&;;Qs`lw&M}!nPN`>}(9c#h~!s9NHyok35 zHv+pFBBH+{k6`X`rVBW4{n;-$>5>rxW5T005tEDuVeHrp3Z3Tzb`J(YtwhL?pr4q% zdOrt|Zkqzu{(D#MjlTo00@OC$ztr|$*TE&=I`~Z3RxuOLMQ*wP=1m^8y-RSxq+ppQ zIfE8rB?H}Yf9+rZa2-tOw$x&1hiu9zwQkY1EDvL8V9x%CCCTCxyqx&7=J%w;gc%CO zCO)uLFch*NU#3fwd$x57K(nK!eNdEnD|K54m+v5vW zFDJP7b1kD@(^nh`#fIjbYLrc*p958m#OXmXl};%UJILO_bd!*zoCc{8+iXlLN24!C zC+T@G5rWA3t@91|MTE$*ktW=$;(-pX3x#!mu&3{&sB0ri?8 zbcTMt{bIjBfuCZfWpI`{S7g*l`{^9G&G7i0bF*6W6I$xNtZX0@oJjXF?<*jm&Lui$ zuAXW7o&r4GQE6HmUb`!m^?+^pvnN*C*?3Xhfb0j;&dvgV7*}Ww9qyASXxu{awF3m% zc%F#E?6|rx;NValtJHg_#*t1R2T3y{M2Slu7J(#X$GYN5exv5y-8BQ71UU$Pf2o+I zTCK7??z~TpfZ?Tc?~rO34jTqu)kE4j9!<|Qmx^ryejyEtPVRHj%zJo5_0xvZ8CtUH zM7wt#g_E4Y)^(OvmpA~TK`~{R*0?YefJ4LQJetUCPd-efa@(t4eu0Kp{UoN{pA|TTF zoK}Dch=wg3t9J(JJnfFv5M_-vJ#Hb8J6i!koPVd`$_bWi5e8yDQ3GD|R$z)R&I&;g z=YUCmXLR)K?Zp=3I|d#MHb;$L4r!sAO<}NDidbstlK2rI22e6yMb+U}aKkGZJ6`-R zs1o_BXRjg@=7D2PGUKThcp=^AKz?ZH0B%G@a>C>CW}7m6!O{1Hh=r$GC?dfRG=nyS zrhsjHB<>foM>E-3rh#H>$mswIbNl8_cjrRn_3^?jiI;j~XKg!p#XU*baX*|HTK033 z3SLGwGYsuSA-w5zqZURkaa?DMY&3)M7FEABz=)ea28Hyl+pLp1$*V6D>L71_j_X;I zo&@xEH_Ko864VW3VlvR5B%#0vJ9k0&Pj9rntCH64)1=?-Yz7~AM z-QB0lC~-)_VgSJhdEH9MZrlMAI;A0YS_yD!G|Uxjrs5twiDrer-s;_ZUJbkfI-c0~ z3IxRp<>ZC(vWJ2Q7X_MHAwYOMm&KrnN`1;C5Fcj9KpL1-cf;PG?p1q=j(ObumFkz7 zYRbAGByCm|NFtE{l1NN>V3f>I&j9mi^a$`?Z-c4+41{%i*Qfwt-6CI?Eql4{ggZ6G zzQeK93JRfTOaXwt;V+W-d$RVsu$2aN12Z;lM331J*!7PZwZBEi-?`&WEpy~u# z;-g-on8o8e7fR5HrvBxq;r6mi%Jq)}V20$bpf>*6bhE;aK46G>Mwd>PClrQ=gRF_d z#Gxa+#0)c?ykcD6*V%-J5k;xe)PjX7q1Ys)TEag1dCNaG-b4E;IU7zUyB;%4Wyn&= zQ(VN%(~xL~tKhi2sm1QLk`vhtxEoHTRjMb58C~fECCDOlUw7+};^F??FomnM_4td! z%n{&pN@_J7c_ME+LmBA?IUA-knuTI(Ytum;#$t>{ZIuuutC(hP%S%0bIk85IIv=s~ zvovwT!N`=27ZsB8xrT)N46c6NLlD)ET$5wSBtOF2eWFMJh!H)2>-Hve`u_-f3%08E z_4`{TBo^H%-5}lF-AGG!cZ0-Qlr+*U4Wc03-AGF}NJvX40zPx$-uwKH|K~dUHO%|^ z&O65V42}=>8>BCobkb_#U)h)tR^T3Q1qX)@Uk!GeAp<*fH?!!@_-AI(umbZXP56uB zF%JtPKW^L}&F~SJPZpF}JO|or_U9G9_<4{fYe~YNayD|61A$5YHrSBiWMVaT^N9U; zx8%;D#GFmBu>-3S{h)7S*fMPLO56L1{rgk4vSqka-<8FM=x1kx3nzJE2oj%99izDS zS}Ub84jAuL27=xh=#~cd_l8gK$^F8gvG^`0nn#8;{8!q;C^YSXRpteuive{^$Qq5e z#fawcv#xg05~-hI$VM2`02#9^Yu?FV3LSn(Ivie%y-|9?o!f%B88m>c?v~GnwWg(F#T!zCW6B+99DEbCe$%!d z&%4ZWPpK>y<#hlgMBn@IW?D28)cXF;X?`>QQ{SM!r{b0>@Edkz zf&fSQB?D47mNI%@Hv6-dU%s~31vUbxc<%Ogn$xya{x-w_yO7XL{cGgD>?t>%%nW-~ zV$SogaNP1PtOedx_+WKg3KeZGWo>Nid9tRn07hEgn$^!XL*#;X2k}4 zFpO5f(RycC{1VurgMloZ0XlrWtXHCPG@86^3+ODNL}`d(9D^n)P?t!eOqUZzt6pYR zGH_4~1rqDvwA(3*H=Lemz}e7L*$uJE)pyvWnG-f3WB@se9)u^V8UBqwS&qT&KG<~f zHnUJXtMV!}$)BV+-n6s6E(E6FD3hU4Qi7u3bweKnG z9qANFvnp2c0`ASnER4TTI5s8q$=`>}6IcAMkDGRvTYi4Mj>~<-pcB{C8F1-mOc51M z^Cf{tP1FUGnhBqdNXPsGzbD78f`0woJARlYAo|_IaA94Y7lg5Z9$3}Qs_~{oTz*wg zNY8i2%h0OBsy#2wV*}|&;zoclll9=5*OBwW=|L)!Cir=if?qu{7kofZg&SnQ-1*1S z)d0-uVgp*nww$KWfFqEHtm?T|##j#`)757&vFCJH+ucq_CQs4VrF^_dfH)fFs`+^2 zEk0h8I!4&iS!`s_5#U!!F0}sW#Fmz6g({K&tao5~yOzH#GKY6y$SeA%?QJHkRF|P< zR!v{{Z;)OA3X1wu1V&sYgF5t38xy1o+|Q@;omh2OGo2jB<6^`d`M(WIpe2!FH#W{( zl4;)f?)Y}%Xp@8Z5knRgzSP$HW(Aayr-lGB2`%>Fe=MB6jP$1NHz=6DZy;+Xz?2`m zb>t4~fKXdbhS4uL2J|0N`FL~ zz{qFQWy%i4nZDtxHMPmV#lgxAfv(W8LCnB&NQK3awd(#TwHIX+Hc!r+RiUC6lV|Ad zx);LBcSZ7jKqf@qbY4K$o0ACZ@0q|KSr2Kh&OM%LWr>6m#^X>Id+zBR*1m)WLo;ImP(oWQ zC85DQZYay_UpH^vta;$(eb6=K_l%J(OseNZ)kYrScBy^K=-%B?8SV#zTFc ztIm_ocByp3^|Xx=UY^oF%o#xk#rj(y4U44>r#N9PAHTIq?xyV%JTu@IUZ|HGukC5y2{hj!ssf5?wCBo1B{;byYoU+s2|fGjGmbRk^D6t zw!bgkALP$-{ATLE+v(F_kx(^z8ghBFV?Hj`?V3@G(Tz}R`48zX6>?4_|f!zIBN zc-yZd=GfB7g?gZjgY4J&b_jG3A)2qMXwz27QD!=Qe5ocs`r~h={xnUibgLfeL);CS zd+9OAjx}-X^~<9hFnR*l?#T$evMsRa^@1AD%;p{fU4TJTm(hzO)G<@4m(Nd= zz%Jp)97%|-$?wRdfX7qbiQe&ikVNu+p2E+t0 zVdKh@k=^!J0R#9BNbe@J!-7OLoJl29A@WFw7N5|tPXvU}k}0l1ZtgE^IwbOsZyKf> zcg?;v#)aiR7(Fq>wKe}Vsl2bS=!J5J|80c%&*kxV0y1#+MyP)yf2LJro2v@j_jKzu z#iwL?0IDNYDBzw?qx_Ni!itZw{UI_U)@xdsnfdGi_*!i>-t-smRr3QzXt2P6*PJBw zwIUOR6-8V(XA~U)@M4;NX!d0Ms-W+AYa1Huz|rgJS7q-?PYj8$M-tIDNqawJMo#VY zo`gl@+OO*498a$LCx|oHzaRL2rEqROYxHci_leqn}xV+7b~K+H-QRJjI_(=Mw{xGu_uN zRhqCHo^6zy&Xm`2g4FbjgwXLNvPVRIqp2MjUygmV>wAn4scD3P@nzz85h%(z8=rP( zw1@GfF-{8^mqjn4tClsC3mNTnqO&w*ksmG}&O@dzx&ikWZUQ(098bvX11`?rPgVar}1699~OO#bWGRY*Wu6eOPFrW<188Qas!X0-{+JqN4CO)%?XSU zuP>g(Yj8Sm$XV)1M2t)Z!)nLx%m8{r)Rg6r3rY3p1d}8I_>}Y_Bii9Tm#CVTf0v=} z$JOzYd1F=WHeBFPBft{$@mJZ9K<0~&W7I+8^@}JKTe3}0%M(RlO0l_8TdV4s0ayHM z8P&0I7IpbyjH0Xe3^S04cbRZ&Fq0yc!QVJ&SOj1IN zkjci|)|gS*mvYstw1P3+sI%QH0u}t? zka1HakDeE~KtaNSl)!=D740g!Q{h2|5n=8-8MHqftv>y*{PI9U)chd)1R3q}Bq4^1bSX_{Rr+zC zTJi^DlgVz(p6>+Lt_eg)8z<*jUGY_JdBD4I z@RDs}Y3)a^OeY0k9G?X|Pr+E+p@#S6#&o5>lE;0<-!{q@qC%Ile3{lI_fpD zdKQVS9xap6z#ff?VZyzxvm){F&aZ?LYU;L>K4T}N#?7~h!OwKbP(7J>Q4G(dZQ06{ z3ly3KX2KoTE<+-kr?)Ll7P_!1d7W9m7{k_wx>WK5;&9xYuk*;rcBn6bM2e@}!6uHP zl{Tk)X{ho8{>3t8`ba&RK}?CHTp{1k5^|Kk{B-&b3_GQq1!IQVlJ4b@aOORUz zA{H3|)p!>5d{g6%FlleQ&o-Tc9akyx7_HoDO{BAEzdic-Gtq+Yw zzPD_#FlniWgMI8|t=5#B!mvt&C1J~%|5CkMVy%7zw%8D$#*y7g(cGAuwh29uu{n^- zB%1>cFfG4ieH-Ih<8%AVi1mRW6hh?MSVkG8&;LSoMorx3F;1l|6wxV*s?^dclOnWt zx?4PFmu?sre8k@CiAvHj5MaV4d7E1fNjj!K-dB{z-eG@ zWq|}21DRvo*M5$>VWJv9LW=dcW7$|9_cE%LjxADn2XX7l)?G@IRTAx_JM*+-W&rt7 z-69@!#OjfrijFhPL+LSK^Kq7e^kXera-5sZ90<8Ve}&LSi1B=gXbx^^ z1OmEW3rzsOa?1FAMI99UbMpLZ6W^yZI2vD<6{7~ySqoLZnCcw6cChVTH*Gao?@hFo z_N}KFW!r514x$AP>ZMh)sy9j3iuXHZyN8#fsbRW!IZ#sZh{_X`A3v*@No#Mv zzy&wQKfbnBg6h?HZfUI2-Xq63c?lnXyUJ-hNu*V~CRuWrcgR0%*U*OMe9j7P&O1BM z^Z8V%pr=$o7m#6?qlz=7kL-5&r0amOd+st$7QloAIWrWME;W>1WYVJk!bVt2f|m_# z`s%fa8pNo{erA%-816X-EFgVjzbb5yT+|Zid<1OGzyk7N>_*i>jyvn^)WGqxTyFy7 zk;SA(0%Nyw#3e|dRLux9hBx3!TnLQaY0>Jb%)h>eYHA2Q%Q^W}RR7u(x&Tk~trypQ zLpz1nZ&NCM1}wl6lTm};+NA4OIg4gIST$whSivQG$miO z4pU?cz%HQ$7~Vx-g%lg&F{1+TYVu8{hagDM9`X{3aCdE8^0W=^SD{HdTpa+=u!l;4 zclT#lq6Bup0#c*x_Z^0MTX9=WHZQ^gh7tY+L9sx6>Ko75ieotbUr+P8LP=US5@>b5kt|g#1y?|; z@)oGdHuSLO;|2BdDLjf&%L*I@Ie~&ij)Y1r@g2^7T_7Qzkv}k+*I?|95^&R81Hlzd zF59P`z~?6fR+9BW_`J7G6;bH~G|lCJh>|;O>qp|uz6*zHwewRiW}UQJ*2mM`*yXqY z$UXQe!xiu%A_E!790?SxOwzr+5N7=udWmm8+_+HXO=RQxO=Z2?*ZA!_5(M)ky;t>N?MK&6km31&Myto?ufpD_0eK%xHdrWewguAJ%>y za6mIYAs{_8v5JXqoy7J=*{c~jwVQg>Lri1PIH#Z&GZk0{@eIAFUNGI@nCIE!v^HYB zCt*VYK<-+JSEzV(*DFd>ZBn61mM-ADeV78EIJrU$53AZ^i44~;Ym$p>XJx^wIJ8dx z&o<~asC5dumYn*R*YTe#q2X^11pE_n2RNpbdImjBVR;3{Krbju2|`e=q3^S=fgn&r zO#?!lzh|E@!l$(n1uoXW)P(u$+0@c7DrET zt&FnPu*IWLHIE4M0dAqVBr85q-TE3S0y^4c6Moe~So@saM!J-8vaI$c8k7dtUtL4V z6jy_$6J9ERQIupu1J|#baf6u6+0W$izYbWg74jeQ0~Dq1AFMOQCexj`q=c-jc4SB^ zox}{k8QSbungHHwT#Rr!7! zqO0#mZ*qu+9>UA`8!Z@Y>Ene(lq9sGRQL#96wV-C2poHq+87-Bwtv9eW*r^Q@^Q<$ zbY+R4HIjiG+lC!ig@oDDkZj{`C&k~9{1p~YIChm!AqgF{0aw>72OW4*Z=c9g7_HOg zCL9q|Bqo`sWJ#5^I`fJpl1PU*o+hCmQ%a5+8q#<@m{JWk-GG81IO&WqZh}9A1QG3F z7IL~6ZiFhS_egCi;W{Uk?)y#{6rFKTS^^27>&Kl9QA~ej#Y+yh` zo2?H#lGnS;=4PQcLo_ElS=U*+g1d=-{iSmTFeH@ieX4OZd(||nU*q6VSJJ3TOLb+0 z_<_#a9W6viUA;P4=jNw>`YuczDL3u66t9#eh`Aa6G(66Pmr%sNzCg7y_JEYGakbfl zP4YK_*aLD~X4}q@os5YDBodb25&^+enZPA1Z89ye(6{fdYC7k9f>kJYsoN^VMyO+i z%!$u|f@g`<;r`|{uWyB<&B7%i!))(!)p3S6DZNEk?qiVN3e60&!nL4sMiF}X!U2B~ z6^YvLzUt?0<0GbipDRt=>0^PPy&JyA&pauL(zp`wXO1XCul=X1h2XRjGe70qou(Gc z((oy}YbA#td~hI2*;4a?EMqUvK~)o@n~Cr^h$wi@Y7&@$%eo&6j?3gj{(2ciN$~9h zD-qF$2&uV2Wtzj&C`KSCloe2^);4!z?e%+{$Ew>}i|LaRfY^tcHN6$Lxm=}1_ z5D7f4(ybblgf;xg4C$Kf+H3i%np0Drnt;ZQ+pV>6xYmY)8XjT20ls1&9>LAi-u(ge zt}N>&e6I*6<%>YYf^^;lDm}R=HiP1&r-Knw=`7fBT}U=R@YPKMzu+f`yA)lZobPt- zARVc>2D`uVX||%H;v<(r?sC9Tc@u%Nsc+^o>74|}Ke zckmB~Lnye$g{Ot%dbI67mhP$s#gM}7veT_asi6em#VY;9i^V1~gz{qh@L&GWi_v>cX`3Ba z1n47`cX<`waH-y#d{9AE0RNKBIEZC>?DuUj>1hJe?L;^Gk@Uog&pCL?lzmH5`)XMt z)nopm%@LiULulJFf72MQ0<}2al@(>cBz%rZaN<+~@M6cErX84GB5#|1hCqmpKQ5U8 zTSy;T2H2Y2T~kec>6pM4=CZa+ZgZz)+4|6&sUtaTcN2Sn|1}t@Z6Ux=EmOv;YxG%! zaR5upvY+dk=80a2B8V*a(dF!_w@jWsEM=a7DiRT?!4wi!T9fk*ymj^&*W6EULO&5R% zZ~0m>zn^t3{zQ=8M^mI!7$>`P;W#5XSMU0Xim=ItXgRrn`y9LIkvnH6*}M(JA(%SL zS9&(#VrvN3yglYU$ls$sFBy_LEVT%07lp+hoy;ec9Oh_fUjVJ65E_DePXl;;TUXXt zuk^i|)FPW!mIBo~%opRPS+pwU-eLlRRH)j~zXmm^i|%szj2%e@Ze@K<$e@Y}f_jQ@ zk#Cx8>461|@+4Cx9L~3@@%)C4@GqE`y9|MyxDGyWMSS9cY}aVD-X0~ydOFcfi9TRZ zfB^atrZe}UkDN<>%|9@&e;SbV^i=On7_h7%kaFo)jYt{r^w_(6pX|7Oe^i97wB*v5 zcVCt8c_i|*NDLqIImf!W4q(G}y0l}7EtRg)$(sE|EJUruOE)IYhFDn}DtPy3>Ox|I z^&M(XMj-J)#)~#*Tbm0-vRU*Ie$IKycKYI`Kj@3>WaKq|#wuR!?@<|QlTWJ41*kD}bk}?6+Y4$Hq z;9r+Su*QG;utTEYpgwFVMyL<_31W3%Z{odCeUfM1r$oTkpn)HNh1C)C{gH6;*{Aw0 z@r4hWE9rh;K4rTdw|(0^dB66^);m#*Fq)fbxlT=VvR`O9w-=@nR~~m5fbHO@Rrfo; z&0hs1;yP>kKBOcHK~~099OfAV>K;v)!aqcC)Jv^E?^g;@D8Hlo4gv9&xgRIc6E+jC zlJ!7eq$j?BG*HNYRNS{1k{)}v2#H$~2A^E&y`-xZf>lz=dJOth4@4^qJ&)og=fU#E z`DzESCn(;1e1Prj<6D)fqlO8Bt|EI{{elZ4S0_wA@WcU?B1uMxtYFR)8o2k3Ilpfk zkWrlYgabZojhi+y%o>h$%lrdl0!CDtco$Ygz=!=_&%1O(2KNA!)GTI(8t`FL0ZA** z7fWp8MtA$SvrCN!bDu>Y%x>5+VjI-IEk4&2VNn>KMTDlGVQ?A5467>H=TCXRq(5n5 zi#O>axeVCyaQAB6nS8vH{#CCsSAH;^!ltT=cTVa$B&`T{rI^!@aV3bOnT+#+RW}Pc zWEy+fS%^27!f4br{HEx@3qRMaVdO$3iRd+2T7l1F`lARNgGd+)7o53GXWea=>=*lq zrYV0RcPf%QUfqcv5RK+Gny&`|$Q}G=Y!3cWXoG0nS;X-@#NZ;!k|wz=X8ujjRzFfN z)5=$H>Q8S#1^nJj;*2!+e?&h@*U~IN5foqlrO^Jl1407>fNi2~%TY~;Ly>wbx#kgA ze3Id1L?n5l2h5JKmd{rQ+$? z6t%Yn$f>VK{1nDRDd!gUqW~3|RwJMyoBtH+N;q+?sc*pDc0&M(LZI^6AGNIN&wKCV z0YJGiSb`P;TSo3k{2HV(B>UVBUyNOW6aC~3tAQcqvYYyKT?z?o7N@oh`be_&gNjTs zZu>tfGRJ`>T2!8zU#EcSP+urgz9Vq&6oy`qhgFn#w0!Q0q#aO^orytJWCO+gl`YC6 z$YD!+t`91*WBm`#lj5LaFk&(@kg^QJ_>YQgnlieRRiOJb&Y^Mc7gJ*qj$|_t9LUUq z&P*+QDpC8oqfn{5etpmC-ol6J_xE2<=^p446Bj9^wS5VvO(yu|TK=*_tTKvEvl@;d zge^RRym|1hI6uu3R1(yG`1Sl4w;1mlon=)><&{s>lMv@hhxaEwkSlG!rjBVxR7~`b z^n9OBDQ?GZ#0x)Ca$#ouqg(upv0oVc$>m07kYEw8#KIA19A6ZRH&Rs@^TPs(0R%Nu z)SX;Ybl;gnwCl^n@1bLJt+kQ!h5!ZOJv{51B!Ie87iSW$d95lkh+T!nX-EXH6ITma zKW^;nry+ZM8;nmK)_Tj}o=f-tqbU=Thg}TIro8PNfu;rQcyk|#=1Urlj=ub~Ahh=5lf}2+ zVjk~=-bd%Q45ek(6p@LMa*F9`{i9^VrTA%F&bcOguUeWWP{U3?f6p<-FFQJgc?p=` z>EbJ=68chOXB?8p=C^mmgqN+Ve~Cz2OVffweY3w&fiiNA(RX3q;4cwy>F0u zBFn^^Ly1Sg{mmt{`GREDj=bUS9Wtt|G99?Y-nX#|mmB|rIw)G4po%X?$L`o6$~fDp zSz6CXvh!Z*sVB#46n85uxb=Ec^`ewU%4P;-vS!%4lB59@RONTa+b96&q1f*JY zN+8DpCu&v}eO5N>4NM4AA*AP@KR=pF-^-W5AQR{mOLP9KlgXY;At;jSj{k5O_@&YTR}E3OKP_HB0A_!3Ex36t)Od)R!UX{x9pX@Jn0Ft)c*6aqkk$ zq;^p9*=T*Rcv*WRU`70v{nG;K<^Kj+@0n@{U3jG-vLVT(xw6ChvX z9|kn!*vOK;umx3iqZCLKd z5_8h0)>E*avCf#MhHa1jghEw)=ptN5d3@AQa}C`g2iCM`u^0}JCV1S4Yt`x8d1a*% zi(Lmru%UeeBL0neVrHNHRae>fZS(45Al94UC{fqaGnRoBwX|Dvj;gMERQK6#QV|P6 zkjQ)yv7_a$f)a&;iU!_;>#5*@>tqCf8iYx{Rax-Ur5y$4fs|WgE+SC2$dFA5S&c zPEwM9b7p$tTj%kV9HT3CRGm9$=8`K2CtsN^d}%fMHAgZ<>M-}Mv5=Z&GCtaHWtV?t zLKBOGtfx`APU&Y%OHsoKbE+SwOjT&=c;bE zeyDdS2l)*ywIJFoF>Te#%>gAArhy5zqAqqpe=zPqpR+iM~wm=ZDj zch9jWPlPz|@G$&%(e7Zw`V?O<cO2XB{=I^CK%TvfLBA<6T{vSSVfaY? zE4x~GE{O!7p$vyI9Os0C1H^8y`?U?^H@ut~J$Z{>S)DxwPXc(Kb2>Gh1Jev5&ecLn z$G(*Rm8lU)C~2m)#L_<_o0&@_7# znw}+M4KEW`Y$)D`ECD1XbJdod^iEZODhCBfY~DNq{@p6lzv9k>R@k2|ZAUvSx~-1* ztnRjDopf{r!l-uHf(VZX*9=M2mZ#amTVtO@ZPA&$l+f*Y61vKroi%$7Gj3&^394R< zWXu56j}nrcM`VCHqAZG1AL01<$@6vuWdpksN@2=ez_8ut206N=D-jORuIrPUULrCr zT?}4l$zEUrQZ2Y1QY{=(J8S`j-hFJ-*LWJWOQz|v%y#CTD)l$^zy#guj%YT<7Wyjb z%OZkl(;Fn}JYtQI{RHBoo+;he-wetS>X)uiBYMVeMQ(xUfD%Tv{kb^iKsx(z6i)P~ zZC~pAqn+m2)4fF|!2ur~)JZ-Yj+{MNa&T|7(P4Lfh6ch1lsoV%ovcG9d-nP?4UKc+yZ#Ci{n_T&dr}D|d zEnF#>pWQv-t$Q^NmHAtn!2$Bzu)_cq^}Wnasv&LbYj_5VTAJu0jSr^sLSS{Ku~ZS0 z!+<^FlAy4x{97_I)p)jjWhaI(g-r%a@d>J_G-^geSOeOaur&&An@koQkyil_`RH?u z)OO%T?exfypI(Hm=oTrokU!j4tk@+qZ~tK8Wzi)rcqpvIiIV8xOFZTC^a^pUJwdf8 zmhwNb7R#ug5yF6ybok%>B-cMT!oMe+W_R5#3U1(^Tsev(FimU$LTGYBPz@BVHnhsHXyzNeO

h>WFa4Dn(QU1<#)~W)@Or`IKbAf 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%0Ag~9T7wyrIkjztnSh7`Kh{4oJJo)h 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%0A}QhEX7=T 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 S;>+sG`g#qtI_2PrEdK*#R;sN4 diff --git a/lib/twitter4j-core-3.0.3.jar b/lib/twitter4j-core-3.0.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..b9394f43c9e5948d36d0174fcfae0c36781043fb GIT binary patch literal 284059 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

=IbTvgRT?H2Z5l@RrW|emk$X1Q&GfIam1VQ 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 literal 0 HcmV?d00001 diff --git a/mobibot.iml b/mobibot.iml index ca3aa60..9ebf5ab 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -134,15 +134,6 @@ - - - - - - - - - @@ -170,6 +161,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/mobibot.ipr b/mobibot.ipr index 9c565c9..995cc86 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1,14 +1,8 @@ - - - - - - - + @@ -75,7 +69,11 @@ - + + + + + @@ -291,6 +289,7 @@ - + - - - + + + + + + + + + + + - + + + @@ -135,14 +149,38 @@ - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -150,7 +188,7 @@ - + @@ -211,6 +249,11 @@ + + + @@ -288,10 +331,9 @@ - @@ -583,13 +641,9 @@ - - + @@ -760,7 +788,7 @@ - + - - + + - - + + - + - - + + + @@ -910,12 +939,14 @@ + + @@ -957,19 +988,24 @@ @@ -1051,11 +1090,6 @@ - - - - - @@ -1076,16 +1110,6 @@ - - - - - - - - - - @@ -1098,41 +1122,71 @@ - + + + - + + + - + + + - + + + - + + + - + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/net/thauvin/erik/mobibot/Identica.java b/src/net/thauvin/erik/mobibot/Identica.java index 4141c1e..a24e065 100644 --- a/src/net/thauvin/erik/mobibot/Identica.java +++ b/src/net/thauvin/erik/mobibot/Identica.java @@ -36,9 +36,9 @@ */ package net.thauvin.erik.mobibot; +import org.json.XML; import twitter4j.internal.http.BASE64Encoder; -import twitter4j.internal.org.json.JSONObject; -import twitter4j.internal.org.json.XML; +import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStreamReader; diff --git a/src/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/net/thauvin/erik/mobibot/ReleaseInfo.java index f02a294..b0bfbc7 100644 --- a/src/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Fri Aug 24 02:59:30 PDT 2012 */ +/* Creation date Wed Jun 12 14:18:56 PDT 2013 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,21 +20,21 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1345802370733L). */ - private static final Date buildDate = new Date(1345802370733L); + /** buildDate (set during build process to 1371071936682L). */ + private static final Date buildDate = new Date(1371071936682L); /** - * Get buildDate (set during build process to Fri Aug 24 02:59:30 PDT 2012). + * Get buildDate (set during build process to Wed Jun 12 14:18:56 PDT 2013). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } /** - * Get buildNumber (set during build process to 4). + * Get buildNumber (set during build process to 10). * @return int buildNumber */ - public static int getBuildNumber() { return 4; } + public static int getBuildNumber() { return 10; } /** project (set during build process to "mobibot"). */ diff --git a/src/net/thauvin/erik/mobibot/Twitter.java b/src/net/thauvin/erik/mobibot/Twitter.java index ed8a907..29dcfdd 100644 --- a/src/net/thauvin/erik/mobibot/Twitter.java +++ b/src/net/thauvin/erik/mobibot/Twitter.java @@ -38,7 +38,7 @@ package net.thauvin.erik.mobibot; import twitter4j.Status; import twitter4j.TwitterFactory; -import twitter4j.http.AccessToken; +import twitter4j.conf.ConfigurationBuilder; /** * Inserts presence information into Twitter. @@ -112,17 +112,22 @@ public class Twitter implements Runnable { try { - final twitter4j.Twitter twitter = new TwitterFactory().getOAuthAuthorizedInstance(_consumerKey, - _consumerSecret, - new AccessToken( - _accessToken, - _accessTokenSecret)); + final ConfigurationBuilder cb = new ConfigurationBuilder(); + cb.setDebugEnabled(true).setOAuthConsumerKey(_consumerKey).setOAuthConsumerSecret(_consumerSecret) + .setOAuthAccessToken(_accessToken).setOAuthAccessTokenSecret(_accessTokenSecret); + final TwitterFactory tf = new TwitterFactory(cb.build()); + final twitter4j.Twitter twitter = tf.getInstance(); final Status status = twitter.updateStatus(_message + " (" + _sender + ')'); _bot.send(_sender, "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status .getId()); + + twitter.shutdown(); + + // @TODO Does it help? + twitter4j.internal.json.DataObjectFactoryUtil.clearThreadLocalMap(); } catch (Exception e) { diff --git a/src/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/net/thauvin/erik/mobibot/TwitterOAuth.java index d2f8c73..ba9267f 100644 --- a/src/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -2,8 +2,8 @@ package net.thauvin.erik.mobibot; import twitter4j.TwitterException; import twitter4j.TwitterFactory; -import twitter4j.http.AccessToken; -import twitter4j.http.RequestToken; +import twitter4j.auth.AccessToken; +import twitter4j.auth.RequestToken; import java.io.BufferedReader; import java.io.InputStreamReader; From 5858e12b1a001aa11ea0fc7bdf56cf8af8130317 Mon Sep 17 00:00:00 2001 From: erik Date: Sat, 19 Apr 2014 21:16:53 -0700 Subject: [PATCH 006/842] Moved to gradle build system. Using exp4j instead of MathEvaluator for calcuation now. --- .cvsignore | 9 - .gitignore | 23 +- ChangeLog.txt | 294 --- README.txt | 14 +- build.gradle | 90 + build.xml | 52 - buildnum.properties | 4 +- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 51106 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 ++ gradlew.bat | 90 + lib/MathEvaluator.jar | Bin 5762 -> 0 bytes lib/commons-cli-1.2.jar | Bin 41123 -> 0 bytes lib/commons-codec-1.8.jar | Bin 263865 -> 0 bytes lib/commons-httpclient-3.1.jar | Bin 305001 -> 0 bytes lib/commons-logging-1.1.1.jar | Bin 60841 -> 0 bytes lib/commons-net-1.4.1.jar | Bin 180792 -> 0 bytes lib/delicious-1.14.jar | Bin 23859 -> 0 bytes lib/jakarta-oro-2.0.8.jar | Bin 65261 -> 0 bytes lib/jdom-1.1.jar | Bin 153115 -> 0 bytes lib/json.jar | Bin 39824 -> 0 bytes lib/jsoup-1.6.3.jar | Bin 276135 -> 0 bytes lib/jweather-0.2.5.jar | Bin 44542 -> 0 bytes lib/log4j-1.2.16.jar | Bin 481534 -> 0 bytes lib/ostermillerutils_1_07_00.jar | Bin 525947 -> 0 bytes lib/pircbot.jar | Bin 76700 -> 0 bytes lib/rome-1.0.jar | Bin 219671 -> 0 bytes lib/rome-fetcher-1.0.jar | Bin 29876 -> 0 bytes lib/twitter4j-core-3.0.3.jar | Bin 284059 -> 0 bytes mobibot.fb | 21 - mobibot.iml | 201 +- mobibot.ipr | 262 ++- mobibot.iws | 1880 ++++++++++++++--- properties/mobibot.properties | 10 +- settings.gradle | 19 + .../erik/mobibot/CurrencyConverter.java | 33 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 88 +- .../thauvin/erik/mobibot/EntryComment.java | 47 +- .../net/thauvin/erik/mobibot/EntryLink.java | 246 ++- .../net/thauvin/erik/mobibot/FeedReader.java | 3 - .../thauvin/erik/mobibot/GoogleSearch.java | 5 +- .../net/thauvin/erik/mobibot/Mobibot.java | 1283 ++++++----- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 22 +- .../net/thauvin/erik/mobibot/StockQuote.java | 0 .../net/thauvin/erik/mobibot/SwingWorker.java | 2 + .../net/thauvin/erik/mobibot/Twitter.java | 8 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 3 +- .../net/thauvin/erik/mobibot/Weather.java | 7 +- src/net/thauvin/erik/mobibot/Identica.java | 141 -- website/index.html | 17 +- 50 files changed, 3124 insertions(+), 1920 deletions(-) delete mode 100644 .cvsignore delete mode 100644 ChangeLog.txt create mode 100644 build.gradle delete mode 100644 build.xml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat delete mode 100644 lib/MathEvaluator.jar delete mode 100644 lib/commons-cli-1.2.jar delete mode 100644 lib/commons-codec-1.8.jar delete mode 100644 lib/commons-httpclient-3.1.jar delete mode 100644 lib/commons-logging-1.1.1.jar delete mode 100644 lib/commons-net-1.4.1.jar delete mode 100644 lib/delicious-1.14.jar delete mode 100644 lib/jakarta-oro-2.0.8.jar delete mode 100644 lib/jdom-1.1.jar delete mode 100644 lib/json.jar delete mode 100644 lib/jsoup-1.6.3.jar delete mode 100644 lib/jweather-0.2.5.jar delete mode 100644 lib/log4j-1.2.16.jar delete mode 100644 lib/ostermillerutils_1_07_00.jar delete mode 100644 lib/pircbot.jar delete mode 100644 lib/rome-1.0.jar delete mode 100644 lib/rome-fetcher-1.0.jar delete mode 100644 lib/twitter4j-core-3.0.3.jar delete mode 100644 mobibot.fb create mode 100644 settings.gradle rename src/{ => main/java}/net/thauvin/erik/mobibot/CurrencyConverter.java (91%) rename src/{ => main/java}/net/thauvin/erik/mobibot/DeliciousPoster.java (62%) rename src/{ => main/java}/net/thauvin/erik/mobibot/EntryComment.java (97%) rename src/{ => main/java}/net/thauvin/erik/mobibot/EntryLink.java (86%) rename src/{ => main/java}/net/thauvin/erik/mobibot/FeedReader.java (99%) rename src/{ => main/java}/net/thauvin/erik/mobibot/GoogleSearch.java (97%) rename src/{ => main/java}/net/thauvin/erik/mobibot/Mobibot.java (94%) rename src/{ => main/java}/net/thauvin/erik/mobibot/ReleaseInfo.java (77%) rename src/{ => main/java}/net/thauvin/erik/mobibot/StockQuote.java (100%) rename src/{ => main/java}/net/thauvin/erik/mobibot/SwingWorker.java (99%) rename src/{ => main/java}/net/thauvin/erik/mobibot/Twitter.java (96%) rename src/{ => main/java}/net/thauvin/erik/mobibot/TwitterOAuth.java (98%) rename src/{ => main/java}/net/thauvin/erik/mobibot/Weather.java (96%) delete mode 100644 src/net/thauvin/erik/mobibot/Identica.java diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index 53371c3..0000000 --- a/.cvsignore +++ /dev/null @@ -1,9 +0,0 @@ -DevSuite -build -dist -log4j.properties -mobibot.properties -fetcher.properties -*.ser -logs -.git diff --git a/.gitignore b/.gitignore index 3b737ed..4f84b93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,20 @@ -/build/ -/dist/ -CVS -/logs/ -/mobibot.properties +.classpath +.gradle +.idea +.nb-gradle +.project +.settings +/bin +/build +/deploy +/dist /fetcher.properties +/gen /log4j.properties +/logs +/mobibot.properties +/out +/proguard-project.txt +/project.properties +/test-output +CVS diff --git a/ChangeLog.txt b/ChangeLog.txt deleted file mode 100644 index 744bba7..0000000 --- a/ChangeLog.txt +++ /dev/null @@ -1,294 +0,0 @@ -2005-11-08 14:58 erik - - * buildnum.properties, mobibot.iml, mobibot.ipr, mobibot.iws, - lib/delicious-1.6.jar, lib/delicious-1.7.jar, - lib/delicious-1.9.jar, src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java: Added ability to - set the port. Added NickServ registartion. Updated to delicious - 1.9 API. Update URL to mobitopia.org. - -2005-08-08 21:53 erik - - * lib/delicious-1.7.jar: Updated to delicious 1.7 - -2005-05-11 02:05 erik - - * buildnum.properties, mobibot.iml, mobibot.ipr, mobibot.iws, - lib/commons-httpclient-3.0-rc1.jar, - lib/commons-httpclient-3.0-rc2.jar, lib/delicious-1.5.jar, - lib/delicious-1.6.jar, - src/net/thauvin/erik/mobibot/ReleaseInfo.java: Updated to - commons-httpclient 3.0rc2 Updated to delicious 1.6 - -2005-05-10 22:47 erik - - * mobibot.iml, mobibot.iws, lib/commons-net-1.2.2.jar, - lib/commons-net-1.4.0.jar: Updated to commons-net 1.4.0 - -2005-05-05 12:47 erik - - * properties/mobibot.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/DeliciousPoster.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/Mobibot.java, buildnum.properties, - mobibot.fb, mobibot.ipr, mobibot.iws, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - src/net/thauvin/erik/mobibot/StockQuote.java: Updated locations. - -2005-03-06 13:04 erik - - * mobibot.iws, lib/delicious-1.4.jar: Update to delcious-java 1.5. - -2005-03-06 13:04 erik - - * lib/delicious-1.5.jar, buildnum.properties, mobibot.iml, - mobibot.iws, src/net/thauvin/erik/mobibot/ReleaseInfo.java: - Update to delicious-java 1.5. - -2005-03-06 08:30 erik - - * ChangeLog.txt: Updated ChangeLog. - -2005-03-06 08:28 erik - - * buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/DeliciousPoster.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - src/net/thauvin/erik/mobibot/SwingWorker.java: Added threading - while posting to del.icio.us. - -2005-03-05 13:52 erik - - * ChangeLog.txt, mobibot.iws, licenses/delicious-java License.txt, - website/index.html: Added delicious-java license. Updated - ChangeLog. - -2005-03-05 13:40 erik - - * lib/commons-codec-1.3.jar, lib/commons-httpclient-2.0.1.jar, - lib/commons-httpclient-3.0-rc1.jar, lib/delicious-1.4.jar, - properties/mobibot.properties, build.properties, - buildnum.properties, mobibot.iml, mobibot.ipr, mobibot.iws, - src/net/thauvin/erik/mobibot/DeliciousPoster.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - website/index.html: Added support for del.icio.us - -2004-11-16 07:46 erik - - * buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - website/index.html: Added the ability to ignore nicknames. - -2004-10-30 13:37 erik - - * buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - website/index.html: Added the ability to ignore links from - specified nicknames. - -2004-10-04 07:22 erik - - * build.properties, buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java: Added - action(channel, action) method. Added input validation to - action/send methods. - -2004-09-28 02:15 erik - - * lib/MathEvaluator.jar: Fixed a problem with the MathEvaluator - library where "atan(), asin(), acos()" never worked right. - -2004-09-27 18:36 erik - - * .cvsignore, build.properties, build.xml, buildnum.properties, - mobibot.iml, mobibot.ipr, mobibot.iws, - ant/jreleaseinfo-1.2.0.jar, lib/EXML.jar, lib/fetchrss.jar, - lib/jdom-1.0.jar, lib/jdom.jar, lib/pircbot.jar, - lib/rome-0.4.jar, lib/rome-fetcher-0.4.jar, lib/rsslibj.jar, - licenses/EXML-license.txt, licenses/ROME License.txt, - licenses/RSSJLib License.txt, licenses/fetchrss License.txt, - properties/fetcher.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/FeedReader.java, - src/net/thauvin/erik/mobibot/GoogleSearch.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - src/net/thauvin/erik/mobibot/StockQuote.java, - src/net/thauvin/erik/mobibot/Weather.java, website/index.html: - Rome is now used to create and read the various feed. - -2004-08-03 01:07 erik - - * lib/commons-httpclient-2.0-final.jar, build.xml, mobibot.iml, - mobibot.ipr, mobibot.iws, lib/commons-httpclient-2.0.1.jar, - src/net/thauvin/erik/mobibot/Mobibot.java: Commons HTTPClinet - 2.0.1 update. Added automated backup for the data file. - -2004-07-07 07:10 erik - - * lib/commons-logging.jar: Commons Logging 1.0.4 update. - -2004-07-07 07:10 erik - - * lib/pircbot.jar: PircBot 1.4.0 update. - -2004-07-07 07:10 erik - - * lib/: commons-net-1.2.0.jar, commons-net-1.2.2.jar: Commons Net - 1.2.2 update. - -2004-07-05 19:03 erik - - * lib/: jweather-0.2.4.jar, jweather-0.2.5.jar: jweather 0.2.5 - upgrade - -2004-05-03 10:53 erik - - * lib/: commons-net-1.1.0.jar, commons-net-1.2.0.jar, - jweather-0.2.3.jar, jweather-0.2.4.jar: commons-net 1.1.0 and - jweather 0.2.4 - -2004-03-10 09:05 erik - - * .cvsignore: Ignore all serial files. - -2004-03-10 09:04 erik - - * src/net/thauvin/erik/mobibot/Mobibot.java: Removed angled - brackets around URLs as it was breaking Trillian. Added pong - command. - -2004-03-10 09:03 erik - - * src/net/thauvin/erik/mobibot/Weather.java: Added invalid station - ID message. - -2004-03-10 09:03 erik - - * src/net/thauvin/erik/mobibot/: FeedReader.java, - GoogleSearch.java: Removed angled brackets around URLs as it was - breaking Trillian. - -2004-03-02 05:53 erik - - * mobibot.iws, src/net/thauvin/erik/mobibot/Mobibot.java: Now uses - setAutoNickChange() - -2004-03-02 05:52 erik - - * lib/pircbot.jar: PircBot 1.3.0 - -2004-02-25 17:21 erik - - * ChangeLog.txt: Initial import. - -2004-02-25 16:27 erik - - * mobibot.iws, src/net/thauvin/erik/mobibot/Mobibot.java: Added - random ping response. - -2004-02-25 04:12 erik - - * src/net/thauvin/erik/mobibot/: CurrencyConverter.java, - GoogleSearch.java, Mobibot.java, Weather.java: Added -serial - command line argument. Added ability to search the current URL - posts. Added ping command. Added more efficient arguments - parsing in the public and private commands. Added ability for - the originator to modify a post's URL. Removed the various - URL-based attributes from the constructor. Fixed the nick - command. - -2004-02-24 05:09 erik - - * README.txt: The properties file can now be specified from the - command line. - -2004-02-24 04:58 erik - - * website/index.html: Added reference to Commons CLI. - -2004-02-24 04:56 erik - - * mobibot.iml, mobibot.iws, lib/commons-cli-1.0.jar, - src/net/thauvin/erik/mobibot/Mobibot.java: The properties file - can now be specified from the command line. - -2004-02-24 04:55 erik - - * src/net/thauvin/erik/mobibot/CurrencyConverter.java: Added the - ability to list the current rates. - -2004-02-18 03:40 erik - - * src/net/thauvin/erik/mobibot/: Mobibot.java, Weather.java: The - weather command help is now returned when a station id is not - specified. - -2004-02-17 06:10 erik - - * README.txt: Added (very) minimal instructions. - -2004-02-17 03:22 erik - - * website/index.html: Added wiki reference. - -2004-02-16 20:04 erik - - * .cvsignore, build.properties, build.xml, mobibot.fb, mobibot.iml, - mobibot.ipr, mobibot.iws, lib/EXML.jar, - lib/commons-httpclient-2.0-final.jar, lib/commons-logging.jar, - lib/commons-net-1.1.0.jar, lib/fetchrss.jar, lib/google.jar, - lib/MathEvaluator.jar, lib/googleapi.jar, - lib/jakarta-oro-2.0.8.jar, lib/jdom.jar, lib/jweather-0.2.3.jar, - lib/log4j-1.2.8.jar, lib/pircbot.jar, lib/rsslibj.jar, - licenses/Commons License.txt, licenses/EXML-license.txt, - licenses/Google License.txt, licenses/GoogleTagLib License.txt, - licenses/JDOM License.txt, licenses/JWeather License.txt, - licenses/License.txt, licenses/Log4j License.txt, - licenses/PircBot License.html, licenses/RSSJLib License.txt, - licenses/fetchrss License.txt, properties/log4j.properties, - properties/mobibot.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/EntryComment.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/FeedReader.java, - src/net/thauvin/erik/mobibot/GoogleSearch.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/StockQuote.java, - src/net/thauvin/erik/mobibot/Weather.java, website/index.html, - website/simple.css: Initial import. - -2004-02-16 20:04 erik - - * .cvsignore, build.properties, build.xml, mobibot.fb, mobibot.iml, - mobibot.ipr, mobibot.iws, lib/EXML.jar, - lib/commons-httpclient-2.0-final.jar, lib/commons-logging.jar, - lib/commons-net-1.1.0.jar, lib/fetchrss.jar, lib/google.jar, - lib/MathEvaluator.jar, lib/googleapi.jar, - lib/jakarta-oro-2.0.8.jar, lib/jdom.jar, lib/jweather-0.2.3.jar, - lib/log4j-1.2.8.jar, lib/pircbot.jar, lib/rsslibj.jar, - licenses/Commons License.txt, licenses/EXML-license.txt, - licenses/Google License.txt, licenses/GoogleTagLib License.txt, - licenses/JDOM License.txt, licenses/JWeather License.txt, - licenses/License.txt, licenses/Log4j License.txt, - licenses/PircBot License.html, licenses/RSSJLib License.txt, - licenses/fetchrss License.txt, properties/log4j.properties, - properties/mobibot.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/EntryComment.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/FeedReader.java, - src/net/thauvin/erik/mobibot/GoogleSearch.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/StockQuote.java, - src/net/thauvin/erik/mobibot/Weather.java, website/index.html, - website/simple.css: Initial revision - diff --git a/README.txt b/README.txt index 804b473..dca5b6a 100644 --- a/README.txt +++ b/README.txt @@ -1,16 +1,8 @@ Some very basic instructions: - ant jar - - mkdir run - - cp dist/mobibot.jar run - cp -R lib run - cp properties/*.properties run - - cd run - - mkdir logs + ./gradlew deploy + + cd deploy { configure the properties } vi *.properties diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..2c96161 --- /dev/null +++ b/build.gradle @@ -0,0 +1,90 @@ +apply plugin: 'java' +apply plugin: 'idea' + +version = '0.5' +ext.packageName = 'net.thauvin.erik.mobibot' +ext.mainClassName = packageName + '.Mobibot' +ext.deployDir = 'deploy' + +repositories { + mavenCentral() +} + +dependencies { + compile 'log4j:log4j:1.2.17@jar' + + compile 'pircbot:pircbot:1.5.0' + + compile 'commons-codec:commons-codec:1.9' + compile 'commons-logging:commons-logging:1.1.3' + compile 'commons-net:commons-net:1.4.1' + compile 'commons-cli:commons-cli:1.2' + compile 'commons-httpclient:commons-httpclient:3.1' + + compile 'oro:oro:2.0.8' + + compile 'org.jdom:jdom:1.1.3' + compile 'org.jsoup:jsoup:1.7.3' + compile 'rome:rome:1.0@jar' + compile 'rome:rome-fetcher:1.0@jar' + compile 'org.json:json:20140107' + compile 'org.ostermiller:utils:1.07.00' + + compile 'net.sourceforge.jweather:jweather:0.3.0@jar' + compile 'de.congrace:exp4j:0.3.11' + + compile 'org.twitter4j:twitter4j-core:4.0.1' + compile 'net.sf.delicious-java:delicious:1.14' + + //compile fileTree(dir: 'lib', include: '*.jar') + //compile files('../path/to/example.jar') +} + +task wrapper(type: Wrapper) { + gradleVersion = gradle.gradleVersion +} + +compileJava { + dependsOn wrapper + doFirst { + ant.taskdef(name: 'jreleaseinfo', + classname: 'ch.oscg.jreleaseinfo.anttask.JReleaseInfoAntTask', + classpath: 'ant/jreleaseinfo-1.3.0.jar') + ant.jreleaseinfo(targetDir: file('src/main/java'), + className: 'ReleaseInfo', + packageName: packageName, + project: rootProject.name, + version: version, + buildnumfile: file('buildnum.properties')) + } +} + +jar { + doFirst { + manifest { + attributes("Manifest-Version": "1.0", + "Main-Class": mainClassName, + "Class-Path": '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) + } + } + + version = null +} + +task deploy(dependsOn: build) { + description = "Copies all needed files to the ${deployDir} directory." + copy { + into deployDir + '/lib' + from configurations.runtime + } + copy { + from 'properties' + into deployDir + include('*.properties') + } + copy { + from jar + into deployDir + } + file(deployDir + '/logs').mkdirs(); +} \ No newline at end of file diff --git a/build.xml b/build.xml deleted file mode 100644 index d3386f6..0000000 --- a/build.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/buildnum.properties b/buildnum.properties index 096cb20..d4904de 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Wed Jun 12 14:18:56 PDT 2013 -build.num.last=10 +#Sat Apr 19 21:14:33 PDT 2014 +build.num.last=40 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..3c7abdf12790879c06b07176de29647f77aa4129 GIT binary patch literal 51106 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

CflOl1i4(-2^1KXo)&I5QlgjRKFQgM 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 z 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%0DvTus6fLI&f~`9YxjD=W5pR41LBYQt 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..78e97c5 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Apr 19 12:47:30 PDT 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/MathEvaluator.jar b/lib/MathEvaluator.jar deleted file mode 100644 index 9011d7609bfb4d1f714823ed3bffa04d6c6137a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5762 zcma)=cQ{;I+s21P3!;r^ArgIbCQ9@WhEYc^QKBT6!614G5~2*zyJ*oSdKW_U-a-UJ z^j=1nAo=9qL&K(Ygxr9?o9>g4hJ?lN{0l)VH0Q3<3z3r=){a898{^W90OEvYsR3j{0pZp;q{PoM>@omtf# zt*rSh;TA3~>3a6w4Ehv)6GpcBy$#4XI8078j=1n`I3i|@{2ek?j9nMYW=o&1Zkt+N zRTBr{t~9Dga>a$$C!EbVCK8O#EIYGoPhiC-?31oBFIK)Xa1oJi?9v0 z9PIRZ;xwt5y;*QnrUYCo(V6jmUWT$o9NM6oh?K9{dLE5dH!64^4D?nXQo2UjzPQG) zz5h1!v*Y1fTLPfGG;SOZT-=$a+XFV2e08hGaO+HPV2m_8-|JAYiCGqMVE&BfaGB$t zNf8M(+@3p;f7gz>%%0lV`Ko#ZgPwZrK3zo2~+e30bFN?2cHUKlBvty z;ah7LpT8YiI_$5P&r1-`HwHqg6K*d*&$X`1e_E*!`Z!vs;|tAVY`1tZCcclR;<(** zKZrdcl6|DRM?3V~C4;iOnw8seE)T5=+_CK25OWBPg6}%6iixMVv125^0PTz7*PT7J zEDa4z@TC1-K^niA4OY<3OQ<#((0@$4-`J51p}F?$rQTYvZPlZ7_Tn)qw^JER%d@~m zv7Ah@i);6grGaNo26Cd2oOQ2n4>$pD!G?oO@vexy$&3i0P9ljC@}Z3){9M=GWFUhp zQi|ErwDvH_KeLy#PKbyu(BitjZc_!m$>@vy^E2hCv8?dnjM@~+PA^Ri- zbCY+a@4RlbS#&QU&=aHSlp>FnXjI!_UwfVH&n*#?AHun;P$gAl9(Pfpsg-E~kxP*4 zDhuoqJ7tmVw@Bi7bm$1fUZ2&&dY{Vy){e8ai2QY&sQk0XIoQq(KbUD!43`0CzGqc> z1>2jG_COKZ42O#;(OMJA*>S z5YSrZ%lMoSlK2)mzxa+(wTJ2)5;iMPn^}7IZ=FGlIXXcfoUIGC=+U`G=+7cuqt!HV z#`pMijqKm9&)*e!r8zmOiuM5wNl);=HvEJh#-b}CXSejGx;IwOxGc}N47@y)cvq&^jh6wwDLrw|#%NkXA;ljO#u(wA z&DdpL;BTHWpfSPiYnKW#pg9 zg`Jh$U&%K&fqYvbPpSUOl-mqR+eFO9^6ko{9K<$yt2Oq-fj%*pEIO=`6Z6BE16g0A zIM4*a0AxDEEBNHnp)p$OL7*OE?xGEQN!{_?;n34pfKdb3>iIotLUkL`QosRpm6_)U-Tj?dLIZs&Xg_-~uv^7cV>@>4+W7 zj5aA+kaDf!%3YW!L%d2iEmyf>C@CCLKr=aM98}h%NIuqrm*!S`V5T4=!N0%EvpuJM zMR&_Uv8k#6KjEfWB_^n4`$Wl+z-yr$qTPT8VKPqvxgE3B#5{dR8#O$zYq$Ct{eA4n z7OdDFxADbt7dxxmEm_w;Z+4pRG&!3YY5X4Q9rr~1%RQ>4A)N9`#;=6C)?+g(xa0|&sL>IQL5{u-Tbh`6TyavYJm*f^R1qA#t_A-VqcK9g*|t|OCv&U&nq@r1j1ZEAAL&vz>{>bquwXTC6M zfMI+Cmf`MEf(`GVyS#|(@^_cIaeA~ILq_4r*c0GEttQGGvM5ZrC;Xs-V+SS(O$xXX zbKNq&0v)!Z3}+b;vYoEd+mojBP8%_$A7}{4s?C3A7RTDeSRTqZ<{tzsWfmY)P77E& zh|Dk?i>#YEQvbxS-}g!9pj0R4q#9lGLHD3C#od$TuA##LmwqhUT=}rOJj!~+SUTM0 zZi01G|P1pL7~5NVbb5HlL%Hip&U z14k#-VXvHXE~Xm=hO&x%kfLmN*=C2xW z%a@FZ-cp?roGF=Ic)#s7L~A8YZ$#2>4!Q0-eSnaD1i@x7#%a*?qH08kLur!+b58rYlv}(J;t~p{60A8T|}Kaa<&!Bz{(mXvdoi9I#T8P zXevq2<5O?hgg)$M`5f&=IO%k6uwz8@is@uNO^oz{Dmc*wE!knLFwkgsfo8OpQ@0b? zKWhK*VyQ}(bfWsa!4f}!TEHs5KE>S^Jj)I{YHUN)dfeIhO=Y|0%7T zEST*jRX$(1b?eO;5STjOXn3}zDo2oY+an@L@232x{e9ZKM9q$}z;Osmm8zI+=}Rdk zS0;OkbQWl+-TluU?E6pQ{jd5oD4N7$BQ@42EmG`o-&K{Jlm7j$3I|-(O(FyUx~TvF z&j0DJ{*HviKHAIFKfbWrI*bxOpN3tNCwcjby{b@B!HB%PL+jHGMI2t$NracJ{6|(+ zt~X)c%`+>Q)Oxk!EmH@)+r^m?;*UcH#aA(yj|1zN71NyySBX_WvzTf2x()tjpH<9{ zA87~ayU3yX6B+jnl4o8Wn=cp~v2!So*f3G=y&H|X9n+16KCujrkM|i>-^W&`Ci^8a z)Ouvi&zNp?dP`nyP7FDz-}B?QAzr!yzmQ{!#4d&yycHy~+W% z7Uvg9`~#~s_OV<{(%T*C5oO18HZpxvs~)G4WTL`nc+|Dtdk=g<<)ezk@)qjCBy`fS z3#=8OPMrvJopuKozB;-te0c^mSax0!R>{+xy}*=Uri=wUx1j* zzmiguRh;LAg&6ojJKA`fix(&ozG&@n_U#s(W`uPaT)bzHnX~fmMt`Sx{LRvINCV?x z5>Ux9VhI{HT0{zu-xMCT6)u*YNdapsvPZ+_lp*nR-=q-H?XqIYVb^KPvlKv|sF(zV zeCOThMHs=4g5b5RtJ4Z2i+YDs>vhOGOAB|&*tBi#F{-swuOZG*L5gBF?cQza$vWU! z=#7jZMUb6qm)MAkg+NkhXKO0T)$CpLk}W|5r>8!_G4O0+=g#{{6|~PoFw=lZNehL! ztNNVeC-|Gp*0uFXw#O@eJS$Qw}xEjf0iM~euj!o#0N+l(rdsP?`b2HKS- zPd<1jZ>O7CBZhC7^ysAfk+1obw~0_(2C?-}cbeYtxhG8*Z#fngtG|C;31x8Tv0C>U_7`490Ew-H$qwhDKuIH2_O^gwv>U>;aabaHUs zBX6i*fyFgt^N!%=eLJxT2Fz(;@auUZJO=TKW+%Sy=RDtcEPg~vSC+)K)S!yL*QkCA z%hCZ`UQarT%V`!b%g&mQnNk$>E16e5$m2aFQvcT1chB+s#|56t(|-?cwm&71kq-9DZ@xM`()mHSejA!uLj8MJOTEak6 z>j5DC2?wfboazQ8^w3W1ap9G%1AxXBtpMjX0dM@EZks%OZi)tqjh2GHKqZYN1uOAr z5; zntb81|^;55vq|N4WIS=qr3||(}NinC@^|06NYoUwzDe`A-1TZ-Zonqz zjnAYI9+unvYqtgLif8+_wfYT?Nf=S#ON$BExtuf0t|Wadu8&ZI z(G?O$4V3UwE64FoXxT+flNMQ`IaSOIT9nZ3eL7mMZKyqDX?rSeK8jAas zj+JY5@e~@0-M$q$zfzL5hriO7wMS^ac$NZx#h!U2@+A0~$mh&t+=fy70m@-Th9xS^ zaMBdsO!T#$P25QnSrA@D$BG_XrU(OANtRKhz({r)K5T-6sFk~myAh{ZGW@ajEl;#^8@jX2rn;Uir_7;U(f+4syV@+|TP@^26U{|DGWJ zEdDui12`_{KV*N&6aTvKzh!>E$bVV5_VV`E1n~DY|2+fzQ!)9Q;;*><-`4w?3H~~M zz9_*ZwErut|7r2>8R73HxdHe7lf~b8|IftmpVt4}Jp5_xbD87*Yj=OQ{`*$qpZ5Qx h*gx%I#Q%%^uS}~6#l!!pB)mK%FLyZtKeJ=N{{hMvHuC@g diff --git a/lib/commons-cli-1.2.jar b/lib/commons-cli-1.2.jar deleted file mode 100644 index ce4b9fffe40c41669797cd806ac989471b2acd84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41123 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%0D2u^7bcx|ezvBT@`6 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 CNzF?D diff --git a/lib/commons-codec-1.8.jar b/lib/commons-codec-1.8.jar deleted file mode 100644 index 32f84c92b7bda2904ae0ff464351e2016438774d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263865 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

u3ZpN-*>ImFkg8&rLFZ$# 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 z89�im|^{m4=ewpQB0#K8Jfv(oQ**=j)(H*-dD=Y+%qktl9y7aiwlItx8JoEu#NvrIrR zyPR8PUM*VFO|~9se$0i85or@!!Byo2@@yajD+PoH%LQZv&bUew$dS94i>hFzaFo72 zkca`f;Q6~9{E5a0yr^wpZHKWgAxSAJ z+0dX-Em^V8Ta)vV?B6>5t<#}RMZx6)X+VQmJ^sV?Zb)-?58Fp~XoA3N zE$I=E*QMorUKS6|D;LviU6_0aI+pSwfO1j1C`(|83%gczhxL2mrEJn&CeYeErql=b zCxD$w8$}d>^?^RD$WuVzDy&cj#z!Q8*B0C z3l7XXz_-`NRtRJ%b*F5y`IkkqSRhmA_cC#^c6vTbGCL1O+<6y61bPvAaYD+GRlI?i zsg$PV3SE*f#LG?SN3o2tJQ#$x(PSXxx)ti&-;I#{ACU?SR z-Zi{^IYJFtiGHB{`~!9|*Whjj`HOc$lMR|?!ryi^r}c|}2@AfHCx!?5cOS;@*s@NX$FmYGWA%xKU<5}De<7_59M}@E?DhEz@?K@=h1t0`oyU3jsk{A?eBfI!w$Wy z5}n?}7+|GujcyiMJ4Hs2dETGU$c|E9XKZ6mefZ9&$Od}sjpTRBuqw>tjTgmPJQ<8? zw0TWS?v?Gg*tcVY@j_8o`I>J%FpTQYzn$oV=(T|JJB9G!*V*ZOGbDqhA6r+~W+azU zwOetUux@A77hey!Nes=}(Z`1l@bjXC@_kZ3`PefYpr<{{P+FOib;b!^F%SR9*E~`T zk#KLkFsb>|aimR9i&s3_UiJzTjzABSz*}(g-sFk_f@^2Msq@X7T4bCg*aDLVOTkNp zgb5!@WSJy#)Z-v+J7BtHI_;ll<8H)Kg@)-ViW&f0gC5{L<-Z&vC1h%8V(KJo@ASh| z)Xvx*u%}_>ZhsYRXXpYw?-8j*4X*r}-Eo$Khhw2#tn(5YkJ)>E)O(Fir)_VHqQD*vc z^m9+kcRgUtzVC_;yX`^tB(45qki`smqCo5WOs{bff9j8PS-JE4A*QC!3k$rD3He*98Xe>%AcrI%r~Q>8j`JlIFeCa)ls9(=I9OXKM71lu-Nlo@&F*LH?2rl=kn|Z1(fC2y2?v?Dd*TQi3OEDbuFR4jPrI zt)wQfJHjDz#;}<|0a0X8QZJIjOvCP_zf2$YQ3uAv+89YQm0Dlj`-{KvWZ8DIsozEz z&6#qT%wDz{ayj0&v+ueiG-(=hQ3gJBK4$cO<;Vo?>ERvJZn*)vDs{pdtkRLxY# z=qv8C!t(eXX)75mm@zhOj$vM+E!wSaYQgkNUq{1`vY8v-c3d&ayl49t;%bL9oK(>_ z3eECAZgMQ}9P$L&d<@`;8u(w8fgF@&0Zg%M?R*T`$vWQ>VsS z@wYY7R4#va2a^8N(89~xzOf(c&h^_K|A;6?j~Lu>FhV$vTcpDetjbG@ij!NUUXb<( z{xdE1gC;x3)Hoy$AODGN9?R4|**A$C{_tZ%lpLfe=p33>PZi!%HFtcJnM86PfcCrb z4z{Lym@>tAIQEd@@nhe8#oiAv7c%Jf_%{@)<3RycJD?MR54dfy|5qu`=B9S~hJUB3 z8`^pN<$_LDQ~Ya8*7v!F1Jc6*nhgJ`)ea;{%QD#16?q+wi3G$VGeu(}Urfohq3us` zPoBKbIl(#}nPyT#wJze|0p9DTn;XFlR_93{Oe?Z3@n#c$IOD5)V4rUf zzj?}Co62qOw1Y#i3!ZF$Ljv1ohAGtwcEzJQ5$dx0EhMeDmUOc~J=MB0gl($LbNsNL zVOzs*2;HV%1{-Xwsui3@S6AOEaVC0U$Ps0ES|Q2j1}Wk?EZ)p`GW&HMyl|q56*>HG z={m7vL*`hpV;$U}`cCkDBgKRXIF@1eZ=T!oVdIK(J@1lj?W&40fT@C?xQ?ak(4}SQ zB4N;fgdN6IL9SyBR zSmB!#ps1HnuT{yyg6A%-JrsNY*#s0~`9kO&$A|hTIWOaqVMcK z4B1yWClpYguGWW}giS_7MayV@SQZHnxVorCOhEr^=2R}OFWN9za-Lx(+k?u>80bXJ zffJW$SVL>k!C7gtY;%J6ZtsDbaYW=-+EsqIG|jtthd98wFN7|L8Ex+D_hs(2Z6qh! zdL)+SM!AdpqP^mB{ANw&LLIG7&p}hZ$5iy&c>CyuPGa)2u#cAeK=f)Ta^m*l=qqnh z+r4NdxV@E>LC%8x{83`QUXPT-n7|kMbB^#LR)I;$glj z`9(sUGiej8DkL$2hv>N~=_@3jShe zkvQ&uv*6 zXMAv5vZ^({9$%u$%Kn6IEYc01`J&*HxxuN5$UQ$4py)&!xU!8SM=2loOw^Lh*k7pO z5p#pRa^*8oLha+Qnkm9g8Z*%0!nMvGmek%N%RWDfLbK7maZEj$zDwGgQ@wKr{FuIt zG6pqU*fbX%YlO^t3~cj;jq7w+QEe>O_#$IXhAR|@E{6xr?g#EK1rR;=Lsz~ecn90Z z3`aZJO-DWFk=IGA<6ykwh9{2iIatS4B!8_#oE=*&@%TKUrGojlHIZSjspDGEMtiDz zv74#{w$yfh06UA>XkPrYIQ(m}M&j3;E;yh${R*}|IWqeivdvo zy8yY)rJ*LtAVW8f+z5_X>B5HzoBskz*f*Yw$ z16o@`bzbL3U413biC&&dG7C<~n1*C~hod9P6-Tl&zKdHjqp~-v5nN5sfO5ADALC+z zb-=l0?Hf48ncWar($g$wmf*fu%rJ`T-cp7bbn?E;Y7f@I$Srifyq-cAX7W$InF#@R ztAnlEvQBpWf!gc$XiO^mM!JE_{+o1f+4;+na5lHDQt)h&7GJmdgpm>$Na02&N8R*OO*6kgx`x0x%Ug7)!mK`!ULHn9-^ zu>pa^F5SyAbZ0)hd5x=HuFm)@SOJd627|UpECy{H;9H=0|C6dIfF$cIt=-V)i zmEY{&d98byTNy|isbYwJ#K|HJ>cpOqIH|7+ zQ9$CQuGdXRHO!(a9z%`}f@X<2Kuzid;F})Uq@+#o6m++WfbvQrnp-qHfD#JP?sORH zXG^v+G4}s1G)1iYF2nXHu)0`S>oA7VN=(U%k0W1Y%#gxmW=aet=gh?BD%5%3F(^}v z)fzlRnw{|)9(wvNl|N0M+Bit3Q8t}7v0{3m(uux@xrG#C{`z`Afw1(z*XPN?;R)r2 z%DdoZpbPF1!9DBF-R~h*rli;4Vbh4WD`%<=KX2R$?wFcR?x(8)d}Xx)=Pc&W*X&-b z$>uj_MTZ`VqFd;_mmq1aYVU zm(DRHKTO3`GO`am0~j3H(=>V%j?yM&&W>4&Ku7S&*_ad2V5ogGQYoxhv8&KKQ*>9A z&7+6ihGX`;WlAmh_p9(te@RXcZu%Cl=5T7ew?KS0{ICFJUjs+{5d1^%{rJ~Ofk%OP zML{l;itBp*I>htAc}^7Zqul&n^dgQ6Q74BUjuJI=N1@IlqzzU(c#a}NB>@6pSAnBX zIw|{_g#cHc;iMCe=n-@1ZpvuUz5@cOfdwT8noFET*i8<)i9UNa&CJM-;XNe5DByYMb6GEA*EF9xhN8xW(>x%dj}dutmN>8iXYX){mt0qOiQ&!)XT zqs%V+2A)SU?$wNBF#jJX^s2yNY-=TFESo#go_B zC1>{j<_ePS($g|Wz;Tbz@8ryeJ735EHp$8#8xe=L>P-{biiK^hPbDMpMLZa|tJGsuWMB7sZdC{F~1B{?b~B!YEH^YGKMAjwJpiE-Xz6hAOa%B;!YiJC=!B%C1#RC zoM}WF5l<bM)%nkahs>J5w-fb?Qy*v+ZqLB2a zW?*Z0P(NzCbPcJFUF;%y&db=rK8gpm!Cd4@&?|TRlMeYyXP#ywG&OEs`~ly1I{mnru4S` z%_y}pzhEuC6YBVaOKK@MhZQK*%751n(hv|R+W@8(KR`eHmq!!-Nk9CvE!#9f-X4&E zJ9PbpO7ff_LjM^Kr9w5b!7}laukYF!?`_jfPV1Fk>y|4;LHj9-AD7oWc+w1@h`R(_n*xz zzhE4C@bMo74>9tW zKF?&Zp@xu)l$;FB&={e+%*Ppp@3DzCr=D|gTqBmBXJbyosW_!EriW+!MmVga;3IU= z>*iR2hpjG6AnY~z4Ckz$(zbJ>TPt7y@MmqbQrH5WcjP?BCe#7lbT@%MlY69G3FX+H z5A+BM4}M|^M`SG2FL*>U^A{uvd2Rst^LVGJ;eUYdTQ?5r>5vbKM^|0alqET}7gTGg zgT_C^Dh~6VEwr8L)klC~`dAhd2)-x1Z>PdSvu7d{-WPvQ3wp~QXgGSa^E&$9B3S~{ z=_>zcbn<_Br2C%>$J5l<;_tfiKLQN9nRls2M{2 zL_mh)F9oI~YG`DuB_PZ0|5Y>huI$pPmwm9!>Vdz)c=4B)X@b%Z2MJ0AXx$Ce{FIYd*fUVy@I*bbX?EOv_5v-W-^jR`-1SgPn` zV1q^vKVUx1D)|h^N|=Al$8nAUWF;gXMtx^jz?&Ff3Lav!ii3(##x9v*0-ta^w}P<4 z@9C}li^nEP;-!z_U?(-eCPgIIaziJz?@U2x@A&ljlF+B^%q1#`eyz-rvvZrqGo#uQ2ux?)|G~FvdTx zrNjECjzCU3PhrG_KBspzfB1Z-JL~)c`PWCT|I?Z=adG~CHS>R2%|turbRGaMxO4x> z^wmG@GyjAJa8df3c?5tBiK4|vUK|b+7m)C8?a!A=;!+5vmdJG9gKc)3x@xzJ=g-m9 zM^+D`RCKj7pH5HTX@aQPO@XI`BL^hgq4am6LVQIo%`FR&Y?Mlo_a`JUq>r#G8=$aK zhUc=NN)-rmQd+WJ(oA%B3WLU54FVUjGXKP^3)Zh@n>$8~Ng^RNX(ztYOXTGw^dbgn zn#aP$k|Kp_TPB7`xS-@B#s+lM5~!Z(ez6u&z?7$yPKA%B2d;9E-A?rmIk(!XFSn_x zem5>`B^X5WHpg8#yu64G%xJ~!iG5pmzL{JKb_Xw`RRI^DeR1xcXHq>n!=c|OC4e_#Nl1&)zV`H^zS$cC6p*zoBJEM^j13DNsGW1p1Yg6wR(+N^ zIft3Ed>z3u@bQ64?u4&yIyXnYr;sIKw#}|TGZ^6_GM0}Y~Zl?_j z>We3Y@@aR0CuWZZ!im7PyRzGM$Jf`5UEeOBA!G^9TaON7j$DgwwcY>K2}Zx*a1T1p zeAwrvY*Fsp0x7$^%-SJ*3yXq7>Yk1Ao_~z9fhALwNJLz@_~JaCRCqh#{*zUj!JdH= z^=LDM>DSUBHdqb@=#7sH!PamN!RyZb^KJ8rzutg#!Ki)HacIUFpP zjo7S`|LP89C8%PTBA6alloROK{O1 zB%U|m%p%&j)JQp3qD>}xcK~rFe5-7pF3sG0fUx8_CEI;i00dxVptxu>{xk1+vbpiD)($4 zv9a})TF%i<_Q854I`>^j+1`EWdqs3O0GulQLdN4ep1Nc%&-%swvcT$7=(<3;RRWfPLmifYJI@nvEtzv9Eq|AQ?|YGhaFcNHEa@u2&Ux{4(>k$y zO0;R}v?i2*w`-nJl`4>sKAE>N?G8bl=#!%oa=3LhFB{N9^<~3N{W2M>p{yEyIEshv{?fn^I|4E!7`Uby966UQxmX~l% z1M+*@-v_AZ56B?<<@pKTuEo>8P5beD%1ylh_G4avm+8OLaQugf*j9Nx29WCO186uZ z66EG9Jl6S0VJH+-hy9MLp0%REG71niD(PnFKn4_5BBF4hz^J*|bXMCGf6NXEPd&(C zgjp`w2}*D+F~o=TpyG;%k_OZObAI{`1AO8V=#3;9q@17xWpH82Az^EZjXjHz=l~>X zYEnv)H7ke&rOU&)I}R`H?kF@gma;6OSxq{hCb5qZv=z~eg9vqk>0~iE7(Y7x7YKbUoCq2z_8%i?9`p<)2dQ* zmHez$;2696$Voyc2nKdkK0d1t_pDTb%A%?o7|Dk~*LXE&EkQ?$$OWsQ-yZch$A`_I z7O;(VS3l<4-?)CQxbTw5-N}r4Z>e(l4R%*29+rqTM@xAC22<=l8D)%OpRWa|lUxYB z0QBcIxmnpUSpw5V`= z`O=u>(T6nMWr|QXWb0TN-de4EeSUpB^KBL&($|h){Q`ASob}@fz3@q8}bkBw}C@&JoUS{U!=BdTNQXO1iuqt=!PA{b=SyVG%L#MU*?d#rDPz1B& z!j+jz&e|Fl-zxa>4T($BafUYF{9Z26+!EkgoJdrX4?4D52%o6F1cuKK9TSyVqYv7m zZgvRWEVGzYw=NijP=xC`9n}?y)R6oMk@}P_JIiCMoGw`dig$&V8F&>tdqH@V{nrek zwtm_p?&+a@jL95|n<71#%@*C6uxt#ie&N4_p+|`^pmP~3DnJ)>72WT=R%as#E}_>6 zUv8ok3TN#s2sU3k+QQhzDsl?5pKr?&H*mqj7ikefD1UVW%r305xTXh^gQeR zle)CaeqR)Jm38Shv<$sm{ikeXrXz*glLym)k@2BBlo3eHU-?iizd+CAtoje_-T$Cf zZ)}#)SnL}*v<3`I`*)cG@}bVPmD+og8+vfFMl8QEn%A`I$;$ProHHrp;1s!eVqd?1 zS7eFVSCdU>8D5^7GJKe9D_*_Yr`2xPbB>&L)an7R&$@kuE;rW<=4$w@7R}UWDYfwM z|FQQ@;d$>{yKrn6~k=w`=Zef7d(bKG@e@ z?>x$LlC$w2zcKD{Lu1&C!$!xpmlhD$atDZOahd-IYJ)V4O`-v{wM#R`3UZUYzlHdL zqjPA)F!OMXE?@u|AMr#fo;)N`&NFl#^+kNH1fI}znA`27id`>_#NB`c{-Ifl4UpI3 z;KGBUtkAdU#8Zls$J#S%p^=DhEhc;TRb7vgp4NH41aR|tM#-mu;*F(S%`+ldxWvn5 zNqP93PnS*zHILMP0RP8)s9$pn|H>o&iy$A6beTzy)P1QGd7mh(^_!w)MMa)CcCm04 zzl+{irB-Jx@#;RyuSkRn)5!DiIU|g~LLu17_=TrO=Y9Nd^`xesfN<9|ZAO=Ts!w1}(`;(pi16Sva?-a2cfgWCzW^ z>|(B`C3)#i*frGId%^Ak@j-=XbPMNTe~q@hZ&`YMWlJsXNI^A2)w^o-8;(J-CM+wg zXvvCiAB+OjPU6VH|A^qi*zcc3H~#?+|F@ZkzwF-sCS3oM$;*G^eE;v@e48(SFdF)` zaQ@r*bH5ahxs$$?x&A+1r@!Ih|4?}Ne<_=3X4C;tz)TbJXG9Rcl+Ays?7bCrVgS3P z*L01#h_^3lP+7bg4 z1|)tibQf8PC_MD3_!6cd3pfq7`WwQ0cT(D#gunf{NJ_hiTDl`qgGJ=<;1^<#s|+Wr zl$zHMs^x~4t>~vNJp)`S z&=Y*uS!;lql+7$7wx&|Qt?9)cx#yrKKP!p^c=Y$ZKX*oP%aK}Z{;DykZMnL*1&4m_ z_KHA5s)gH3*1#}s?JpuJ@?`X0tUJTXV5GH^oOLeJ=b+YH0-FzE!;Mr^DuR$47T*cZXJ8VK; z7JZ}@p1;21UjzIzt>5O{y-mGa!kukhv?l|NVtQg3ZKtYeW3D(Zv$r@X`$f%On&Naj z_+d(K$?n{B>r2DOcX?gl22JJF&UT+t&pTL@FfXBJx@XBt3ucc4vaxlvZ4`x-CzB)h zW;zI-8MVIX#;|^NHz`s|&gz}@TLGq+W0FrM9JtL;5B}uS0Mx)lPt9M>=XXXpe)U21 zM8$LUVo$hGGGniuYWNs7*Ihe0brgi8vng({*T%<&Ggjf|9R*AD+GamlfNrY{-hXoA5n^)7`rA3elhgS?+6QTCsx#R zO`8Ag2P_J{)+8gb+07K&D1P8?HSP6@>3RSLe>+Wb0iPNQLNFp$UFGHjn=@9D&~lo9 z8ddD9UCMq3fF8gJpa-xYM+pf~Mj=GY3T=1Th=P4#AH;XQLgtTV?=yWS_Ffm1c_%31 zV`Fe1CrHhoM~YZr&zEOHp`t>vP!2lhH9HO17Cx&K;vP0(ipk*iM&e}Ij){UOg(i5p z&hMJ8hb4LAPuexr*?GbLW78~<192v2IXyjndZX5E?NGypj-#5~uy`KL7Ygv4Rov8xA#94pwZ1+o=th1tmE#NKx8TOdZ z(>plYIC4%&ohwqo&)kS!&N)UxmKndCcFt`4Ic94`g0M0RJp5(Dd&}F^^v50)Z$Is@ zwpq4soL)4ZfBew5u>HOTX?yk0k|YzkX!HpA2Vlpae|yU*0B<>IazDkoT5xTr3Rp7L z6f9k2Hn4Ce*Y__TxSKTFz8oQ7&QkebS<(y88O0XlrIPym3f1FtX(t`{1^8!i0M?RJ z0&$8PH-+>(II&IJk_2^`8fjJMB%1fSP8Fx+iaA^w&em|87Z40VkaKk^-4nc8Wp$I+ z?WJtrA;=9d&%gw6N|_17Z{lzi@9+pA3{G_oaaOA)^$gKd1$YRxfhV?h~E!!Q2 z)(%+JH9O?KCj^_t3inp{CF*usBK7la)7Er3zxjz$gio|d=e}0?S>$r-4$mzMTD2dB zHl4=|x%H&0!Ws<#CU4z#q-R2uZB+7WT^5X~>S=he%ohSDE#51FZ)W??SG7Lc>!R;J zeCsXPf_y4)?dE;`oINdX;-}5yYHm1@F2j2Ao`>T-e~9SE+&a+$+%ASyQbZxN<8U5^ z=m>e$X=}Ufz9b7)R7BC4thkYKDZ9gr6{XgVYkOxC(@jry~>cKUCK$PuE=!f*J86lem=!Mv5jAu}6;DPxGat5wBJn}w9Lefd;hGnv=uc*VjUq13l=Z)1{yX6T z6GSWX3a~&M0fySYvIqSl>-}e@@=uhm?mgzu1Du2vIt$2HtT3<0KSPyki+H_E`x;9{ z#LZIDE^Wy=Z?_S$OZm-f!76%Lp%gyAycw0(7tBCGvWzOKE26L}xGaMZC8V~pse)0A zC89MO`QtydYlIGK9>*I^cD|}8ZK%r^2wUTP1%*uDB45#;H8AX{XtO4YQ z$USvl$Q-@oB3!dX?@ z9t<47DplkB>NZYY^YiL!pP&cq@jw|Q5E82ghV&l?+sIbl_Gs3edB!oKb{c&U3OV*} z(S@h$VeC!ns<1Y|N+@OKO|c6OTaCLw9dUaCG-6I{Eh0qkyXm&q1Ht${6Ha-x#sJ)o zIAWLBeXFEtD5l9yF!-{#9chNGyU?n+j4b6g*#}BQq-W{fYw2%ieR*CVt;ac@M*L2|AYl zPEu6x`|ysrvwH>e=lpB#ADfV$eAtdYUTSUKethft{)gzxU&ptWH+1@Ez@@R`tPOzH=>?$8$g7Ydv#1c@-uaQV zibUBNoe8^h58%iKae24~K3;Z&^f{}H$FrPvrLC+)*| zgF&t!Nt(;WsC&m&-wRhKV+64F*y&LzB+p4jKq%Vh@nAD3Ln}$D)Y-T)*O$NhmOHwv z=^`c9aF?)Ou@b>^9bL|SRB=(lKu4DC4|P4kt$Q$j(@@fW!_)W8*3LWPeUHr$vLx5H zWV+ImuH30QmHRI)QE2745o1$0nIosd`46&oJy_&^yxPqI>^{d^(K)Ma7nW%71GQ)c z2SuzZ%yL*f4%HgjUYslUH7?~umK;@EUCf!+Z&0&!Rf1_XFFS{s@2GF=MtK8)iD?dd= z-h|A>nHSx8Hd)nvV~(;a!bSZ2#+QN_juAo8|3D(edcEh*)=Pm97bq@NDa0ML+_%_n zD~T2y_`VZ*K+BbNa3P1}ad>3s1A8uzT4A~9Dh`1?ePQ^M$5&4T`w`L&;>*C@=(4FY zK|4YsBdB-zIpD8%GWmkn40H4mAN)z6k**$uwncbQF$BEIuhqcT9YoiW3Qb9{tER=7 zJf0xWr%H3eE~kok*th}yhP97>APN#3xZzO%-pmsSARzufbKv?%;r=dl{V(;2vC^9C z8voD0C;;;dNI>x}PBICOWWnOA@(LIjdJ^mPs)j+$#)gda5*f?0@Nhn`vg#oc!V>dQGVG|GofLBtz+MEsas7;Yy`3#phQus(6iAA=b z^6_XE60KsjVVUASZH#8I7OY57Agh)}G?a-%R;_u(5c_&M`m-0^y$o(ZbcTR--}_yp z@ugWzf6fxTp0Fp(t?ke+9DM#%G-pv*{1FdP6tt2eI__x_)vN|H8Y6zUZZ`bkO$Vvd zr?R<#)bCze3`OhLHBGI}$PpElC^uSnFzsgc;3_K`1(iV@_C3*Ez8 zb!|MQs=(UO>SZWd8RKxRWQshqVZX2KDQ&^S|q?=9r@r0LTpimDJldtF3?4rjCB z4-Al`)A*+y*?M1He-e%1ykQ@S+Ztf0%s)hb*1v^%Hq7pEIr$WyWqgRfCE4Sc%pz); z#nM+_#$I?m4ymGtXAaV^vElpa>ZH5gJ5q@kb9pJwgoLiW9cQvY>YAc0m`dwr@G>*( zJ>R!|G-=Hn=W(Cot`K0SRY7w2Z&R0qM7$^>ip{4ba6Bkw2WxQPQ^h zHxalrtzy~Y-Q-~{xCO>{nriyO*6&4^$ZuU6tZUwLthBF{{#c}NUA)-mWi&OOvRIwS zD#|R%RLS=Gu&S}Dalg-qFd$`(sm9m&u)&jKVH9BHwMbxMur#CCr{|U0!C_^l6+z1+Ya$JKQQBKa2V8$PA1mdAD4!cwr65lMm@5&K`3jSglDY&P7qxl!-%@MO((P9T7UqjF<> zS5h?*ku7;&_#Iof{>2-Vvfp>Eogevpa@4Lj%6ddUc_wD z)+rrxC?7ZRsLcaKok_X|0{t-P=Et)^zEMlW6IA}e>mPb`jI_hE3}DC*0Sp;`p-TR% zNjv|XGw9I(e6Z?e{8cLWdHfdgMG`7aaK+3<$#ObIJE2VfNh_v!uh(innz&+Ol3MSJ zwxi6`?Wq7RHxz8i01kkmdkE4ZcLAc3)|x{Uy;9Uf8^Fa{X4Iqwkinfx;y))(85K>c zG-naVntcB`8`&)nnL2SX5*W)sjDS4iSRY19fq1|Fo5(^Jy30}MnJkuBl1~V&!ZkN$ zHNd7we6`er2nV*>kqGvy7C{q*3x+baLPl2UNVLgLX6=eb#EE-H^}b_Kdf#x;9#JSV zH)_o3jq}Tp!UlUJU*=QG){giWbUuG&44eS91-H@KW|AZ)6=%xUiL1{mJ@|FuTj3LAOnsIX&QpEQ3wr=qA1^(HI(^s>%elhyD_|I|dcYSitL6+T_U37&MN0 zYtwqv;gq~>6Rwqk!&HZkt&t!y02?R>2*uhEDN|7EZ*9XtN##mcRPJ}d{QPn`&H}@I znGmb7vOgH;5CECI0xwefMPCTDI`JF#k6Fl1Zai21K-sA2gJ2i>*oZww38V}wCfT-7 zk5A*>ZE~8Gjt`DEJfAikI~Uhn=drv*?h7K#s3py6h$>pk~8uOEcxPikjq9yJMH0`?X z*%jn|=mSpWYWliAZzW{mt{vRhgpj$?0t#a9FA1S>U+)XD2=sqh7(C+%lJpf6!S^`z z=?}_tQizp5#TmU4VS=_-w@{0vOlx_Em!tx;hU?Uw??O$V#;)+3fx)*EU3qqE)Uh($ ze~mW?p^Gi1&TGpDy^ys~d9-x*s)p6tF$;=?n|s^pJ8T*K+ri%4a*k{y=yz&3MBPzt zn{JG3uWqt&E-0Ji<)aEwnRh4JjHhYo%6HS3DUJE7L_R%%$goE@C#SU|$%W;q?8C>-d7?h@9+d9bw zQU1;Nv*#VsfR;zM$a$I-N_M=R>sVe=4F~b>jUntjeK927t3bbLvlqkusm-?Sa7rM| zAT|Yfg<*bPNvGt}Ax^w!<9r`6mv1>buFV5W16Eud+}|qy_UWS_T9qon=)SWWJxaQ3 z=U47dOQt^(HvbQ2mS03Df47zY*tVj38V)xDXe$hWw(_Uw;J?o-|EuccAHsqEUrg|) zaNz$86YL@Mw|ckh%lU}KPdIhuu3!=aX{H<_Pu;_e{9GtF!k?#vPqybHC?sEBjjl9~ zpI98BkGxx(RIS=hs_W@oeh!i>(iH|F?O^P$%czN(n5HD6=0Wo zAoY@#chO4L{u*`B)0fl^=?-oU5{b_?^Qe#q4?ua~o7EK6TJ7&s0&dv1{@a+K2*7FV z1i!1K(I@J&!f*<;xKurrJ5gO%rknq5;^9d`RBJ*w>jm=|BEf6N|mk^l{P%MC;S=`X8KcG&*6^+LSYO?VM zBd$t>4oG&yOAmE!3@NBU)&>Y1S$<^&Nr*_WVzazPy%_z>8`Ipgs?OdGQ5Q_`QxeNc z0RC0hW8to*{RaqUw#^-QB>ar0HvgWdpUzLB@;T02ac@uqF65DYr~k6j%zBL&x!&|R z>x>_yv5VJNr1ohAX1D>-n0U5DmDw|Tj~(rKt7rXqTBVv|flH36k31Yxk50fFb(MlS z>Q)7iE8DF__Rlnic6cpii`zA)n5n<9T`U264+LQEk++ACDBfn6inpRAw3T>s{*gYQ z<;6Z|o^8dnOt5X9!%Oa7FW0szkcq#e70+7(%)xZn_b~PQ-jfVqyL2#1#byvUUDoC@ zp<7KVcU2=%J>zD@Ma%&LEyr~Mdk=VVUUI)%-ph~fKnl&Og#r+kF2J7)RU)-5+a#6s z_SYj*hYIo6KPc9I?J$2~w*8|#;_oK&AKPSpt&~4S#r%C}`GX$fZ}IehDhTH#W^=R% zc#M8JYyO3q$kRGzbK z;OsKt^I8zw-WI6qT*o9bwZb7r^5|j>goh%^ycywTzCYSt&u~c3r(E9=_dO*Ox8uREux0_+(SJAkFi%Zx1Bu~hQklao6-)6}A*Mtj`c3by_5hE`}?0}lc6KN!-(B)LQf|M7=QbLHdMyCp!i@c*@H z`8T0}lN#90)y+;3GY*pvOMcv)n&n&l zu*A;}uyFrwo`vfZyu&Qxmr4q`%Di((@0F*WaQ0{&H>p!oE@Vkr#aFB; zM$(LmD)(Ep#4})rYq3Dqo<3rm@D+b5v&I>FegiomLwi(!usxn}jv~@h~Kucl7;%_i1q3q>|7q*kd_g9s=2{NPNJq;Y+gCHqF`zs-#ED9 zYN_J1^;3b2krY{eZo&Z)PGr4FII;3#@=ruZLn*FA6|G5|ii({|^Vb|jFQgEO4@{dW z1R9Q0UlS&o43);3rnhNJpc!WgO;;*&P(_HGm{3O9z;{$oZ3zo^rW1Ci7$|$336*ja zeusK6zpeKE3ma?S4r^;`@H>y1F@)9`dCITi^VZT*QDp|Yr}cV)BMW7fQGT!Lc*28< zsEy<@q=bq~aG|1Nduv_}RpVRzwbiG<=8E`GX+a|}1DcaY%TV~GKK z;fwAa&xLYFnYzw@hszE|L>w5i;ttn)0%DsVOS!h<&LV6N0_f^LjwF0Y;jc5r3bBzg zr|M-+J0X`z-@$i1N%dbr3s8|TOs>b0+S3qWgh^YLADhfiRIW~AbW};zGcbZ_Hfh(O zaMnzLM!WamW5CB$@s~{Go2L!CV``QUKWCD4qjjsnG(+dto0b96UoJFs0nG_(v|v3! zw2JcY`7}*c)tz<~TqpbjkDkv46JALf9Yq0$L1~x?QgB#H~kI+y`9%zQ=*R2?Cab0fqZ#&Db&T;Uv?M{3+t^|q%(O#`rdLxl2NC*@s|7-N*2Dn%$E+Fpe@86A53S&-o1h~ z46jXbf%(|8EAU5Ht3e1w%0)tIFV90KO7keXs*&RB84Dp-wIi#Vn(KG!XL_YK=m+~r zLtWdOI@)}Cdlw_^u1hy!RoA^gaQS#R99L_-oo64dMh`8><6U{yb-kw-Kl-aa^Q^Je zn@+5D` zi_KxX;GXoItHm&ist$C;>!Pafw|m-mUe;+U35zhF#8Ydqhp(UUK5EtZ!=U_zxqo->$^~)P&=_dQSdsIs4cqKfgM(u~z56Opji**Tq_i)%yt$AVbUo zi8nCuz?Q%b<>qUuQvkip--yH&xGRAj6_qpw+q8U80y00G_vYO;OozR^A8p?f%PDl0SLg zwdd_{Muh;L_a~jm3&(ir`No8X{j58xHV)}*%B9ZbPwGaFOBE*!V3Matr98&kI{cjR zD&ygU_YS-Npe&Ke+2b|^*}dZ%_|3{Y13+1#15lRyWHk9zt+p*SjfRp1HtTQJuA2f| zz8#QGj$3`~y#~jR`N{Mi$+UqKzeV+$zA2$4G#2H4yx`^{OiRBVG{L{HPOeL{8m5LRBAr^l>Osn zxE*y8>m*F{j?*ds&g{56R0O9lE<43k4ouWZ-91y74M$U36INib6Tg3@Z9{`VjKzR| zhnDDyoG)IRGoXO$%iwIz_m@BL#(e|vaL525c594(vgUqIy!}UxT@|j5?cVUUPLF{A zSlI^`IT3<+@}aoP1>JM5c~`$8T_8D@vKTlKf-CxRCpFG;75|5f2$( zh?4}_9~Dd;`xj@D_Zut>nM^6SZA=YJ4IR?YRT!QTJfXqd$XPgX=OTgQhLCRQj=!l4Rl^1b{cZzM>TsU}UV;41xRDFkN)Ci1~_Cz6P<5d*By zNQp*uDjO&=_i1!Uxlt3$>@iogx20baL^1OoCClii#~Vva1WdGXcSCuAc_|J`TtE3& zA|Q=aDIe+o|@(=!R)G@o|;;@UODOLir8GCjFeSB`WFA1@A`Csyr;($ zJ5Ym(Jq5H7Ig{;0lC?IdSO6jl6FuY)@NQrZEF;xN4$KTXi8qCY`0Q_bE1vrioPu4D z)t))V0bAa{RhOc4!-8Sah6&y67!Se36lr!P-57isNjjLB14ns>(WMsZ2CopmE;Yun zjq%ahXHYry>y8146h%z1E7}IkVTHy5-b^zv>Nan*AJ|Pu0wHWL(10&Q(qTL~dlIBZ z0UQnf$rSYrH7p9Um@k2^EzX59Et;S3q`U)o0pj;iYVj&5A*)G)4|nWQdrnO$t!a)z zkW;j~>C)B2<_odpiuy!jXtb3q)mPPXUIuc&>nEiwfk>T#_xi|?oRKrL;kZ3MYbLj+~kDjKJ2^1z1k8;gsTTRLno1=-gX-?r2XJQDT-Gl6^=Sk8$Msx2X=eX_YRRl zHzYS4av&a&T#kB^+fNPnGGjNin4=gKx zJ`@P>xZhHuaqLP!6`4aIgr1aW^?#h9!IW&>U>J%72}jsBl@7;aDnpl_>17Ih;g7kt zXx_)tAwUbZYpyg=GUZ*D1*^M487(fHN2&s#QI{5Vnv<@ns43EFT z^}Qdd*B~dhGJ%<&ZxD?r;d{t*!4U~vmSC1~Yv|Yq%foRCZm7JaUN8rdp+-U>Z8oKK zZtDEWl#@e_EwAqHf|{4UDWC@R;m`MWWK{Uc4y)0Xhh}nJHtYF#u!Hl1;LsJ)#Ry_d%nCh zc3K%!rua=DW5?)ui43dhRupSWkK&|m!}x+rZR)W3*^MUDPHCgI-5=bLr(O$_;p0@$ zejtJxE0ipz3!eQLnkR72)Agc)Tbxf|^qC4$99Z-t|Uj$c@%O;wz8zB@3{HDuuD_cDx z>$rsD3Fk~UngujOdp6DpA}JCUKl?a)e%{u&ujWZoYWqeC?jMjzWbio;VO`;jB%sNe z;YX_rX5vtav~SeE5^IYW-RmtNfvw-A(}6Sw!IN9DkV{`gvQzL`G{4^s?Nimt<^^;3 z1a*SZa*NlZ8Lrf|AZ;}MG2PiLKUI0jq}nO^L8g~=yIuG!(ark3=5?gKxI0olC8AK} zqmY^AW+0y>4V#Q>hyxQ=?DIZFu^bEjMJhPZ#%Y(EI@h&gG2?aQ1pp{C&bc_8CfOR zI_uk+MrZS31F()%KTYUsg@9Xo!Sai9$vzZ)R?R_Kxk)8Ur$FROaaY3_w zafJ3^ZR>vtwlKuAG6p0@+g5wUlgrRZc+5}Yks5pLtIb0u!d)zu{agZ)k zXc4Yo&*d8LmlbJLvC_}79}Fbj3ydbelOdo@cjzCHeQfXGP)MpF&Zg|Ps4EFd&oQZe zztOl|K%Y&%QZG>Tw1^+ee7)V_sX?{N%$9FM+WefZMeSCK;M|WZi{EdoG#^U(RU}Z^ z#UCDeLobEDBt?rIoYDa%r5$uR#`?wwi}E#bpHp`rRahA7LGP29wP}A~e}Zn(F_nd; zuJHTlBqnKX=5_V<8bNitC}JWVDsch{z|m%uOXLlDOtz6fWoE`?xe#)mg;F z_Iop4eMHOb>H`cd=Sqy=_2ML>jj!n$>)7|-FZyHWSI${CitFpGb31pwYE^i=Q%Li4 z@Uy_xrDJLr*tbkAzBUzJk{GR!Cr`D&fiI;z^DWTVOHf$vTC``0(*1y?dog{vBU`X@ ze({XIsYG1*-RhiN$I)kprk23S;F+BHB<>Vd>-Zt7oL6JW;s);P?CoSr#wF{P)u*0U z$sh~msAAO;(^8z)4X9@N=?-lDs(P1Y-_s=-x*D>MB@|Do(FLYijJQSW^2bK=F5)>4 zk=H}7mGfSN4WZ65)d;aWdX1IZ!(inDe8PJjCnBz26#0f?iOjorCkg6TcS!a!B{qvVph4V$i zVw;oJQN6^TXQcqA*%Jj@(E>CnEjZBSJSrF_OOX;8Q*~eA`32_Spa?wqb|~|svOEjb z7&HjTgesqd4WC)8U}U;{$Viu1tn)~Up+2WU{&~yu84>>u_9gnu5Hj_OL@@_qwP@Z~ zbE%o}E2j>vO4p+O726b+5tQXRHd>nqFLqqMFRR`8;pzHXKvE@fHbgfG4G-;zUhcTs zSf-y%xrIsIf&`ix5%ziTv1*DG@59V>zU0F~g-BWN>see3m-|+%iS6y`QGJY!g$k!? z5{9&d^t?5<$b)>Uq#j3>{03FbHFJzXL0*vq`GuK2giXDgeFrbi$wo$DGImYzQ8_+A z<0ij7)8f7TZEE_+csqi`>K%SCiN?+=PiHOK!ulO?cxiD=IwPA3r`Ucab-ZYq0;7el;aQ@CiAa#<3NQt{j?r9v^iuKWrqDXT23o@$EP>M2+d zD~+PnTY&^jjMe8(r*rX5FB#0SM#-ve%1E|kVElH}eKC(}@T^lsnL5H*Md=D-VeJ?_ z#uC8}8NX24WXe^bP3$Jvvinu|gr=sBhI|n0N`JSy3QT#A$HYxEPOL(0!QCA_olk&ZV6ms zJjuHZUA%f6H#xeGv!8qC%t7bcRO}^Y1UFRZy^JM{?b`Lc^@038W`^KK%7&mxtoRMJ`RrU*hf z{)F%BI-&+nX_+w<^*b<6%3U*o1@m|PvHSNz$)%^S#WlrDFQ5DBn>90zNZy&X6n-T! zy0t}hXU?qcTlTVsB2pt+9NQ@BcvyXja1d0jS31#0ehnU^mkwvn$F{+UjAk03=)RQdJ_ihB zwFO7!u<@MlWVzJMP-W#MMHD3~Q6bkhXkamvn|`gqqyrQ$iYQ3zgVrY_$d;+qo|}8n zS{znOhsRyRW$M8-#G|qM0%Rw8C96i`&CcVj`l*1B2d+z}58qNu zuGacuEWRDTbNCWOb1jvd@uA(QHgMgY6<_jcu(upXplL52mH{~p37^@I z3+|73+T&8+D6Sh#cOKwG4{@yg2#z(lnRq^Pd%IkYon2fAXlA#ejn_TQ6pUm)@yfjN z3M?e;G*+s%Ls_PjD*DJ62rBEFe_9pj$w}ZS<3TMp!mq1M{N%wQmEbmMBIt}b=`r6y zBX_YlY7%R+eq1dDcjoD<-FPaaZSGL9>GJvqFG&2Pk7tkoq(=o1;qj-)ng7_l|E25q zP8heD=SK^Ea1Sq_$)zRAqtQoS6%|$vQ!Y_Hw=sx0H$TI6}MEAzguZA6m#%VCYlb^a> zyXeAs>f561zl|0cs4iZvqTQEdG*H)X8m9n;A}(q8S!ET>j$-n_^Hspr64DDp;Y!^i zyw-DAxim<5RB<#A&o4%C@HW*(hFA}eV9*3f0+g6%81yVdngmu5oOn;Um`EyJ(G zJ!^%gY<99Ee_Duv&MFZPQ-FD!_3JU6T83KVLb}P8x3up3Pf zv^r8Pr&rpnh8ID4+^=>CxQ_U};alB#u5Adk zYz=fEGzkb%raqzUlRH=bVWU)~;>*{bj|+J=K7|NX7_p(>k2QtufZrR! zSkyY{&q`@AuTNA2?;GeUQw!A46v7p%^4Czyvlm>Q<377ga;;|KTUQ@VFho!b+8&v; za?tkHYS@G=a7ksQyA;@kx!FQbBW1sF9=R`IBs|LZ4Sc$S`qWtaf*83?4x5dARhjA# zbZk0a94vEPnDWlM&Q}R&2j#knc$Q=n-fm|#h0p!b#jQI}20rFpf`;B76EzP; zpGL<4U)Kx3#Q0C~Lw^6de$|>gE3etCiT<>$feYjn_oGJlw#r!$wh6*P(W8Q24*{Vz z7pWGEJ9EHZ0Vdexl$kpWehKqbc)t|(1iMe*Y~sFB^aTbj)Y0VdY(2S)3(^iUdMFZ- zAtwMvz#Z=iIb&xW8q;EuB&2{I`cOKI-q&S|<13IL9T6ORAc|_PP0OiN@;aDN*c!m+~R9 z-#eeNC7Q-cR5Z1+KJ4HXsGZ^Y%L|}5N~Pyax)su^Ojx3S_XlUPQBlqDdu68;=`S-l zo^~QdqO)Stg8`*f{lQ(7|G}f{&SS%v^T38;Lsv!r66=}M0U`&(TxsF8_sBkSW07< zL89Vo4)c{f=X6K4L%E1QLy$fCQJQ8DP6~ECunlkh3ONOsR&ww_a*%lm3pFz#@`I}2 zNl^I1X?}nQBxD!97Y`P^?9&Kj)r-bkGkDdm0)wUb*C-Lv^v}&JU$P0}aU=H8+Zsk| zNp8p2(lMj1h@bM2s{$Gx;Cn_ocO$n9y+=iW!ykY-U*mKRQdXY*-@tz0O94EqOJ6dA zbzBm9KIdo@&!`nyC+L_G)XTgLs;}XHtZtR&5v_?na2*%(OuW~5TUh*!j?Gb$B`VVx8V2ahqc9l4O#>yjHZrI{ATX)iB1P;`j$^%f`a#vCpP@ z#qK-=p&{##9L%B4WTSWXQy$$@F+)wTzTs?uzo<_H4|8%6{?k2cLeKhxpbg)}YPpTt zViOJc+lWzq=}7*LHsf*Q>;huZaQp|Q?geLSoJks^(?C|T;?eKUMUjY0CAKyU;Mg$y95@nD%9CDjuIVW*F$PxbXC5o9tTf zuH1>urIcFw?g~%v)tUZO_(}*~4UBJ5-nLVN;x+5BBpBKFsW5o%iE~@1h2uwAaCq)_ z_7l1Jd7Qa}iBHu#X>aLi>Q#5^dLdU4IF^qQenGEG=`&hij^*CU^_dI4Y_PRhm)$Zy z*oOtY{lOP~ z5D|8?2eomseO2@@@%;UV9x=<>s!|W>6^o=(0VXN}NKqcXqbw9-0_LMhhlMEA=1r+d z7={`JP&8jMkF+)CAw$_~4n`8p-53MgM%hXFnL}zlNYDHO&jhhb6LtIWiqzwws3sYa zP(a=1Lk}tE!(u5HX%{sMVPVVTwKIY!o=BLAB=TpgG3SpLRqUrMRcuC%r8`-6p>zun zRNNOoNh(4n?%DP{bz#;H&vR%6C}T(taLv8Y9R>}l*41>5f+a9qN*`L}5-e#Uvoyfn zYVbgpy0gKj4h1+hztL9GW+OtVsN896ID9;Ds?D&oi|&pt*hwh1=5(fHvVm)NR)UGb zpI+>Rv7$C%vSKwvx31k(?DDoMm@{jUj_I{pl4IOpX{tW=7+mRqKE_Shh6MPfB8~{4 ziHeappvz8^r6{$+(9!u{GvkTUxA`WFJu+toCos%G`Cqei!i|)O_0BR?4qU>>pz3A# zzwAqNt#9b8Hu19sEu(Q`I3n+vG16*$CQ@n9mTIQ}UuvUoR11J7BS{F;UVaix`*f5q z2s(Yc7li0@yZ$}NY~o ziER3j?r#HPuFImS-yXl%M-kvj_S;%hcGNi+g~(actdKaLT|45=ITV;e%clhd^y^5GpMtCVibRfA_gF5EVEYOo>H)W$4I%?NaG-4Yd6u`{e@B zMeS$ykrZHn{R^MZzZ@2T5+gyg!;W1L0fDRIh(dOZeqG9FAsZ(Wf)1BxaB>xm&t`jS9)(@h^!zIIu* zAz$UkrraQ*i3kX^69-Ag}zXvhh(5ah68H=f_xj%C?D_((Q&a7`CeKcB0+6e{C^a>F_j#=d!3Y`}Dd$EtoX80nv9OlwX)C-xS6RV)0i+q$qUa$>w z1E_s(cN(L&A0IDi1}F_4hfKR_ZR*tzTo*=E2ETUkWiC8Rd9TQ=OuA!dra0l$)l7;% zhA%5jZF%=@th)@HAGKWC{^)!i4_bhi$Y_nAF8Pu11RB!x)@#5Z>3I>#neffY`nldm zlcvtN6PdTiGbYu>9}0XwmGQe%oKiT843=fF_vK(JWw3Z`OP3X5SH$zMF{u@$-))`d zClO16Ss|lB>!(mWa%u##hB*DSLw;{e0ckwCm*Qr2;ZerMj0bD7{96{1;Z!&Wf99O| zkl9)W{s)nk*hvsCvp@qxvoG2_`Gg zNFhUnjs;BVb|B{V16|J4Hl_ABSke$$CWIPcrA6Ef%IQQ?KcV)CXi>jJm0ovv4V|y} zZv;nq^MJ^Bvx7)(=4dzL7${=4P;@HO55NY(h&>zUY#$b5$54opnzw{YTK+%I-YLqm zc1s$r%1Ya|ZQHhOv(mP0+qP}nwlgd3O8xuoGrqsO&t0F3wfEYWYmf2F6*FSahy@hr zBS?$< z8h5OBo>Y^~X+$oqh+u|ae*-BbYJ{`L)fby{1oBGpz8i~Lk#AAz`Qa?g(l0Uxmu1@! z@0LO$v*qJ!4%dYfx%EV_-318j*ge;}+P&QRI6F3OT(e!GyRh9FwtBa(+qq~J)aW;V z{_wSJp+oaSX5s|9u^PmUOHViqFPFP+PnjY8T}*FdC2dTK|(F`u z^`yq*QD_1s!AYvFYIq^qcXHPeqMTX$JFBhJ%4AK^drIMXu>``9J#!@KJQmd6No{OdSPq8x z%&ZTq7p+rt>j5WW++Xf;#yDe9;#|?DTkAwbYkh})t--K4Xwj#@qF4&nAuxQ|j(T%N z2iFrtlI<+Fy7Cl`KcO;^ReA0n1pN?EVmkS}5y+h%-o68&NxMxGV<_p9M!6_+(v_n_ z^=(9U%DCqqY)Tzs1bEhYYeTu0T_&0U?v|^hOS+SHsfLJ(+rtB9A{X$=(+tc%C*i&E z#_Zy6I$b+oF0C%wmEyKU{XdQhkD*2yP2>}WBSA-heZEsKAgyG%;V{su;~xe2ZW8c# zzhU4m;;0jpBMqN(Rr}lw9^FB>U9zn*$Co)0X5}F<+a&XNL@v07^aCH`9K*};%~9VD z;SfuQlPMyZE*BBAtds2k-k!e zdttvLAK^))V-UjOlp_{y>(+Cz_;Lm&!>|$?jT6PlNn(^adjDp#2vbV(s8K}Jkg%e# z`r4GEqHToC?Fs5@Vf0x&=9_52ZWdMya^8n(P2fG_243YYrCs}+NmEs`+oj8+cQbl$ z>F-PyVwwNO{}tX)RE!$p@kLLGJ5Y2Qb2VKYOZ1i%^M@HzNoLk#j7WqED_tXPGyWSt z$}tMpqi{FP!GpExHx}&cSfRV&2sNcx1#tx?1z&-vq==F2+T|Y8_M|y~bV3H%;;)Z6 zCT`n5;u<-!M8mmJU?9wUt1u#Ww#Zv|z)+SpuCBC9L0kr}TEmiF3I~ypI*Jnsw!|qn zVO~t*Q8jxgI&wjpJ@ds)B78r=9Ixn4XS|ss2XOpL4t@XkurM{0n0GY5opA#&1^OTG zegAD56Rk3Bi^cX&*dKHl?onTJ1ny>{^<<2um}HkE^r-w#%T!GT&3LQ?D-D0#lCxro zojkK{b+eRrt9Cw65UY^gDX)ikp(j0%TS!}D;v^IXb{Ja<{kS{u?_iJpmM!qMh4zT{ zL`?xZc-`zD83TT*Ku!M9tN{(enjA!@?LvANOh9r`@L2g-Jiow$$robD7UzfIQ5jL- zr7i>O{sPwZNgSjs3@WEFQPTjnx zq{|a;D-I03cM2$<-D@8jqE$zW;Y)9SKFBBI&HCgi^9;4NEe*O5apm{`{_0r^x=RiTHm!g(Y}}O?%KARb>(@L z$Sl-y{@Kl|IsE#xa|>-D;uT}8)KiQ^EnCj+T~yCP-YO~E zpE4?Eu`#+-rfNU5v-x8Q0GtBhMF|2Q1lS?vD8{$V|9af}K)MEQZuYxzVusMS2S<*- zIxyqNjF{{6>wCfV#*Czv;>$Bt5#=xn;o!?icto_ZZ(Y!;AfJ`Q*)SoD2S?ShBp-1j z#GQ#5AnPPar$sk8)u2L*B#|a#oku{K#J(2@e&V0K?0c}|?!ekc+>EhhMaBD~*DW#V zbNKWO%F#K$BY}dqqePsRL#YSZs@T*{ZkNwf4oF-3>-Ks?I@}PVMEVWyXbG!u&^Zca zY1}JLSXr~`Yfw>JMVOTSu0P78)(3SJ4yGqL$jWR-Ce^ z2P(4CnpyMISaC6Jxrhx@H#X!_o3%*sE4;|3>7O^hYIiPQ9@jUl&b_jZ1suqnY;Zw~; zqg=l@9lOJbFB%-=mffn7^ee`e4U;Jj44%j80`a0HS~6TDPIvA-fW^K-dY{Hc2@K$E zVNT9gGn@ef8PX#_d_*_qW9Dah|5rd3Y%SWBfi#`+KWqb6SN^!Kk(MWcfWNB^&Dn%7fdU zReBDapOwu`MxmMS)r{8mRZmd=j%E$Erii8RGHR(*0>(Glrn)9{xlmBWEZM;68cQ@+ z;}Sk0#ZDyaIVPP zmsGT2v_*g#N4tq~ILDyiZz;kNg3#6{ju}rRg>7m|tO4pvs5d9_%V}Fibsgffc8ALg znu3ebJneMrC2)0+jmPa55yNTv2^2k|^hE#cI`;RFgT=G7eOt<=b$?VG#^dL!#}Dv! zpmh5gC`XWEWO zt3t!<7YD86`HD8nArLQqKIG%m$=orOT&FI{3mBi^Uz&DnKN&6bE8Qnqd@~-l^efU< zN%f#orrj$WNI{p^d7E1u{}fVUzKXgfTWe=`Q8R#TX%{%K{k*FI8PnxksYaNq*|!=$lD0yx)tg!~ps>PtK7^WB- zhQ%3$R$-pXY^c$~_6m`ZsP*_9?A_{Q$3kcBo@Oaivl5tS2G%0(M`ZYBpt9k*Cays& zqODetH=r$JOO1P+jC-i>4K3E2hD%6Z#-C2?#V8i%a&->hU5bLUiOvgOvz%Oa++1hW zbA(&Cl*Us2-|tuAIuy=zSCLL8)6SdBWzp%Bw500n1_AyoEo3F5joQQ1!_`B!k>IU` zoF^HB1p%r21vMJa;Iut4D`#>ZZsm*OvW2dCsmte_72RHFYqqX$?K1(A8#i5^F4Rj5 zl~~_qo=wjO1243w+m*27OFw>-XxO~m)Y>nBf> z4GbT_{IcOTQC`@s(EQYP?|&^0(EECG~cc_mry0#l*cWEuZL{AH#FOE`DDL;y=zqm<;jAol!~ zCG7gPG)8V2{}{E$I_7kS%3Ia;tMxIy(?%VY%aaBZG?wBk)4af(O|y3E_8R5&-wbY` zXlTeR0QJTc(BJ+C6x9E3n3b$5V+W9Uy5H3RkgAFw0I9YUGuF!I_yK>7KSI$DlA5yQ zaT2K-X|6S)g*Az-TD=9jQG1+DS?{y#52Eh{Nj6fWwb1~ZlJx>MkQ(Hm(9?U2?RPMz3?b}**KQgHO#p%Nk!3%>BCg>3v_XEp? z_2dZyF3F;TrN^8=@T3m1v&7KlFc45s|#Jv?*t=EpqEJpnRW@KP61%7VEaBD2IK$2SVmT$h%iNw zcQx0*1@SnbHABJ^%f3bwhs*zHswiqoy5WeuD$Z*jejP5JR(PpR6qotn>KO%^yRz;9 zm~hX1(eW#DIQZ**)8+vlk{*A>iKOIh_t^-tqjUBDFkmcG#;kSX_;s`cWx2p{@__N$ zy>NDu`g31A_O7|Dg^FEGPrJ14YFze@kZx_C?OZ-`Vd*rj!56vRRmg6UJTzO#BJnnPe*c>Q z)r8=ESOq9w`+)NGf3myy7d`jCt=zm-rR)y>VdwxdHEx0kCK`{eJn*&c0wzq7?Vl?` ztfC(#^UO5iONvQ2Sbv##p68V8L?Uq=ZtO0gp9&0bKq03vW-c>RnSEFS)P578X`^t1 zR`%DhOFOjm<)-Uy&@f@wIF}om7OVbdOxMW;O4#yK#cuMK#*$S<)zly=Tp*;YrU60< zd5EIY%KA0M?s_t~`Sv)CLTxfZkRnnYw9~p!GZv>a4%D(iTqmqV9YfM?N+BK`RRufr zyI-0*nZNgKM!dcuuQejmkmSLUk%^4B@sc&ApyjfQaK=-Im7aS@hHz#Ntw~6?hvz#) zIy+8!ZVR*&txo7F>Wbe2%A}uX@x!9uF*4s!TO&uZ1a(Vj<*mug z?U$)SywmpX#Ky;yTi3SjY&z7NHWq^|LW~`K?f##OOHS(rk6J}t@B zI#lWbeWjLS$PUW(3nO%`z;o3D=!lXJ;}JTg+-6qMp=&7powNXb#=vuGzZxIm_0ndVmO5OJ}B6Nehelh z$>DY$v~(|p@MjA;#XkgVPk;z26&MEu&SBXivq;X!kU~{h`Tra-CQVLns>zQK(mpch z#ssN?#_v0&`Y@Z!Gvg+9H%P4=K)+Jn+q{^BZwRy8w_FJpU^tKtPQHGaMbPy>i5BY2?qL-RhWR_Mst`7z3> z#Q?MqCde%%e)9Z1XF@pcxR1V`vjiXgu9PR~!s8M8jz4SplNS@e#9qYm{73oxT!7PO z`S@MHrw%>(>bP2|O#MYVnr@ktQm*U`ywvbOaOvpi6dvHYAGcEXy#E{-SfuHPGf9cv zRBgQhf#H$?YgB#a>tR>Jg4T)!Ina8G;y`+A_q;i`&v6(L^)SW>+2d$3BMR_uc zW-=c@X*Dnpf9mk--(k8wB_ozos|qtoXewvdr)EU8gMR+QBMQl8rEP1^dWxAAb$lJ9+Jgaw4)$j~%k zdeM|x)FQS19`}tVH#o!d?Pb#V^E($*hgBc=1DmGP(DeLB+n6tv_SeInAZ$C`<)k^O zc>nMm-D5d~t8Iw6GaH?Q8Pq$Ac}Sh*|C`Ips?l9J!Pypi+KYD%a}-Pu*<~%ZT!I?a zW>R~n1g&TAda(^`mbch8vsQp4S0)1Leau-oFKIFm>x??9X_X{=?T=@G^mHYBxbXrP zKbV3`d(opLx3WBn32Y18va&Ma!9_1|3e`4WtZlW*UFAB*D{eukP`0ZO#bJyF{}k8k~>YWWi{lMV+)fEJQEJ3ei47eu&5Kmt~BP}o9N z!;mBzb+BAOOVtE8#d3yBJEPziyuIzBtLNu$BPdbA!?>^l$bpMudLv2Wkpe%(b0c&E z$`s-N$wufP;lU!r0{00gLgNVMhji5QH9ty{)nWA`9LdE}*@CO!l!6JXO2gbxOHRAO zl{+tZ8}>eKD4jVZ@KMTJ%3S%3;(>wIFl z#|?Wbu{1~jXx4%xU)X^~ALx&4#b;!6-`S&=ViKw`j#I4UJD=Yh>3Aio#YVM`+)T%$ zi_Bh4rj&0#GKf)!Z8B^fX2_(v0D6`3=UG9t+K_1?+4u)A@Ok} z1?6VG4jUM_u42Ejg%S9Bl(x~xy|MhQs zn8MoX1KI#FT@N6p|DQ}4{{;p9mkDEZQn&4oD zka;$JCTObOwtbmUPgB16XoFgWqkx&|g1DXCoIz)&1way4S9k-$*IokI16$KVD?T{> z1}bL9#rFDMrcsSDN z+#008A)YIS&rB#aBv7=LIYF7^IJyV{oCZaQZiN2|U=y`~tVd|!A%AG2qhx;Wx{+lE3-vy2YKlwZryu(L3WB>^y9Qbn49-vrdnJ8F^LT3{UK7 zu5-ny)zjK-rmZsV{ZRh4;brH;yo<}r>y^*BZn-BCzfNmMbX-e#{Lj4u*{1k=rFbW% zn?lj{I_tgA^5)92t+&vKiJ6THofm3VT17`LzzR(DzBAknHPvKK{pr zJc5s_I)aa*TlDL#y&wDQN$8>P`CTeOLX!l2q#-zItscnipvFt3|BF+R-L<)_*@j+u zK4SV|)XELHuqM0a+yeG7a+_1zR<7bpc-&vllJQ!c>T^qM6mcvutc@C?@S+$}HBL$k zM=_3OqUfdGFtYKPCN%5!n)&=)&a1tVd6@Qg;O$I5qzGnXh?%=j_7`oYjz9e*(jr5}`wZ$JtjBw3)C0Bh)z-tz zq2ZJ!Q&sYav>(40z=E^`6R?v)v@K*+2uZ3@Cl?4)Sy22*G9I#|tU`VeoI1a)dm(7Y z8jvu;*3-)%y3_{Al*h>g`%9j~RMjCz8VOWufC>`Evk5ZGrNsnG*V)xC5=zjNNjlgl zDnOS{1&~6JiP4mlYq<_{;HkKy7c$-ThyDB*;9NSsyoE~c$V0#RefiWo2;1n`@~fOW zDa2Lsp*0K*wYZ?>pSdvvz}%Qe9$suHp```JNTUa$e5EZqePno&man2!q+Owcii(h` z6fR|UR18u~$=M|}wy&+R(n|MK8^hU(6$P_L*Va!edDWp5^8bHqEJ?wlNsIIsedv=? z`gMlpY7D0Rv+a++BgAe#-~wm;$#|gqei7Wjk(+OyK)4^BT)r*fBLq`7dmg-<5UBlL zA?83|e-M0t8#90YeuRF%Z53PuCRd8f<*rHf^pX;TcvJ+>3=lIUA;gv;C6Qt$ww!D( z3RV1_4JG8sLQY@8m_8Oc9XKDBVIMEvCCRvdP20dT&=Xo^ zon|(o@+PR}K)3oX%$k~wOc7U%eKZ_e^cr4}_-j&RGFz$W)*XZFxwu`*GFw8j65@}m z{1Hiye6_x0LJTLZtK^w+kBWlZG^#lvC!no#2}DisG)EdQQm1a zwq|o}sSmO3i`&AACO6Q0hx48V9^cjKr;-{Q86;yUqj0+r2jO{~@DgVwJPIsInC>iU zK29NAZ7~OEyz^MgWxo>P24|CqvNQbh)y&<>GqYK0XABA6&5l-J)%xhotLWFi`DRGK z>ON-zup>PDe}o_Z+i)~kwcp`ilW6o=xPPLooB^X~4MZeaoQfmL3E`YP{D{N?gb<)V zXd(xdNVC0!$N;ISq^%?0%Q`;@`8O`aU0(Ko25tGS5Y3Lk=+$Z0lgtO4!4L%o z(C!f-P<59k#K)6AkSuE-9a+ak%|HZ6BgQik5|0NNubB`y(7bgm`4zVi0ZcU2M;xkY zAtcb5t^m`VUOdE9IgAZQ6R7=Y~dF4-}@%uS4zGr3J9{k8BUY5Nb~{O5p-Y0j{R zN}OW3%w5L+@Q$rX)p62O&FHHQM)QF=Zp*nN!yM;8Gmr+j3hD$6EULqlJyQBo}vZ$o6(1EqamvOVC48ao?_}4 zYj#flQx0;ZLLaK<07_l6hxR-pzPZhaaWqhoKHWa}y@G!aakGf~a08qC$xVm7Z1?i% z{b=|%cUN@n^iAH&)vXI=tX|ywtqOO{s1RdzoLb<~-)R`&Zt*K~VL~?_zi-P?qO~^N zCUAxAzVbD&R}IQ*^Xv$f>=kOB)+C+G2B8U9^3IGa?BZ#Dpd$t|(4%Y!jwt<;QXE6k zqil>gUf8}bch4Wj+h>otyb(+Y=k~$BJ26l}|kwF$T*e`o=HxEzFGvh=kK% zd~@kiE_L!BdpusB!CdAcDp|HP#ty7!(NvT?OHeuwIsLodgkvrou!or{XbT&nGzQ~v zLbfuH|91T;O5rW$qPSAZjvDryUo11vAIK*#jsKk#&L-o9_cTZ2v9VaGSxI$C0rMeE zzPFTB2J-!NTHo_qg)p#b%zB&QN|B?)A@y&i)_7fl$yJk-0f}Ssw>`8dKNg2Y)$)%^ z9l<8%eBxB{L1BpS1roumU$}*FH@QOSbB<2R6m}e(J1}xsus&37Ei0NR9!9CX3d;7EbP^L2C_&{mps&h49yQ3!MuUfmRg+Le(p9h zKCg&=&CP9`SNne|YWD1PRq{ibF!yxe%=QdSc&apUXZi*wJk)vae}m$xQd^PP09B+_ za-Q#7E2|Nag#{1GU+N=J{v?2fh0aQ@t6JFqIdw_NYRlurMdP>3>(+*eY*${KZ@vmmnbGo zLS?&bhUfB?__;uuwECtG(sjBSek9@M0OdNof!FtSE3sXmuyVL_KwEkGhKJmKGRZH5 z#%g@sN>qf>jeT0K*KWJ_V)X4U8~=Xzd%S1KZX!T zHGfL_D*8)BFrbz1+7r(x%zBJPTnVV8|c$nRiyjhw|gkm%m^g` z#3?yIXZ{a})Bo)goUPJt3m{Ir->E7AQt1} z!jYSkBS82M5PKqt(uL?GChSB8r7Tv9!$$!Cv3vj!D!AF{``T}mE8yf z=$4qf#HR6xbk(I%9ws8C=`rqfO?W{s?6>2}f=$k5wMp_4da&pt(avk$wKTQ7_;A%! zSPW^8;}_1`s(*X=cgzel+FdlAuW+T?8_8FPDw;33)&Er0*F@0LZ@{Q53#^fFVgl*WQsab+Xt9 zPg}e9=o0>dY3W*6gMXo?=nhnfNKSOYm9H(K3#rfH^_lOnK�?h~ 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 H%0v2pHnR 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 tc` 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 te}nOVCD`j|-apix?}<1@fDFJGl diff --git a/lib/commons-logging-1.1.1.jar b/lib/commons-logging-1.1.1.jar deleted file mode 100644 index 8758a96b70cfba9466bacca19c0d99b87cf53734..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60841 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

    {@^= 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�K6;V=*aS?57vP%s-9y`tWD7Yhw-3N^9ART3Y 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 diff --git a/lib/json.jar b/lib/json.jar deleted file mode 100644 index fe51b5014d05dd9a22b3ba3fbe42ab6e0e384fc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39824 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

    TL{0eT(}8Cu9%46FN{} zK4Vb(#wl5HhQGR^?Adu;_LDa#4<&QGXJJF6^%Qy(_8I_15MZ+kyMmZ^_0ym36|#wI zw}$X}0bxgszPmu}dqLBe%;gOcOT5B$rLz8}FN+d3WP%)UT7xOZFp17LYu#wAlV9>T z*82q%PRapiQ-qjA0G_9*mf)({|#rL=9Lp9@V zAg7o&3)?rjiAXe-JkY9fcSAIHEhQ`nSV6rT8tWD?K@;{$L$Rl z`A!qAIdr%FjG+Xx?eQtS69+&RTN0e~l-U+^LeS`_vOsuGDdS&oX5YTRvP~5cbM=fY zj_oYJbL1`5?ITtcWzT>-PeP*6Ed(MK3ZtB?jLY~MGt2+A>*TQ#1s$< z+=f~YYkR39SsG4!dmlpVUUmT(%8>bnS`EFqi$GUTh(FfqiWC5(W@tDz!$_WP9_hA79$nMkz110y}EcTh}pKq z@orO)XZLz!54;mL9KnwT2qBns&Pt5fj`7JGU$&_s%}_u{wne>-7hqw-0T2tyVP7E? zL^IRTu7}R}X8R*Bp;XJqHpeLxhgeuu@Zh*|N$VCo;{<~{5@m2D$Dwc0jn-{%A~xbl zu6J{S4eDQT%p&P&F?SSXN&~k;nPO%>6|(OB97ytC9*`8uv?kbx)ciUM6T1t0<`UP* zavIE&3_=}zGHCa$4lKxMtF)tw+D6(YLCK0;>c+H;cEE1 zFl^Wk!Jg|&*HE6f*(qgxnsYZGLVWVI{YEj0FFfPj0fXh?(k}$zmlc+%K^R9^o;`}8 zef-5YYTJ{5ar;Xo@gQiLGoImcNZ{QJO0w|GCj#F^YLKYwo_K95rgXa^ni>JAz$8Lp zGp4I9P?A?<;gLc<$5`GP5nk-Q?X$5&@rwxV7FjHQ29JclPv@5EIFZ9-ydf8VgpxRu zAsBK#o6ZSuy~fSOJzMzQFmnZ9Lwwe$m$?JVfZBp6?Y@L4wo?DoMNvwYYWl_mc*A6s z1}l7Q+=>?zK&#+#RBMGm){fC`me?atM5liEiC9P{pWxOf9GuX+4=ju(pC3lCJQC3o z#j)HcoxcOj5ZPvt+)_!-kCtzVC@fv~g{376`mrM`hlg2?CbQj)VMjnQ&mPN_HesHZ$T z>USu>#(ahB{?Kpi#xt3WNyk%cC{pq$&>mnB@i6J>g^W2x=F{htO%N|MP}(2VYUYpL z@tAB07vfyj1&QNFNLh!w=TyKFW~vs4(UQDbv8p%em8M*Y>KUW%pIY{E*W-BVJtK72W9+-SI<4FPK#Pp#5G=_qU=cQ#g6sE>IlK4i~WBOuE@?-q0SEJQTsCsup%n6Y^q#QE+k_} zlAY`bIXpDMGrJEw%O?OMGnqByd!BN^TYmoA98y?|VOdG1E56kJQi#YDS3bWEQ+9pm zNrXpM1Qiu+E4?dTud*N-A04Qvp~d`RLPP)_YtCzHd zj0exC2IN%>$vD9exu`Q?bc7g|Re)i($w`Af;i0c_vEh`06G2eCODI_j8r)2r^K;M{G1 z(olyHKzsqwF?%i+bs zQ9-f@B92+

    6@hk31B+MHe~g19zp6uYKGs_4$99SX})*f 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

    )_foH--bysfZ=epcg- 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 %t`X9uhCfG{g^!!^l1gnoeI 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 diff --git a/lib/jweather-0.2.5.jar b/lib/jweather-0.2.5.jar deleted file mode 100644 index 67baaafdab267be4066658fe993ebe9779ff14f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44542 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

    3A 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 j|8LPhwIhFt>|Ix$*LVtgOz1Ox&{+RneW-AT; diff --git a/lib/log4j-1.2.16.jar b/lib/log4j-1.2.16.jar deleted file mode 100644 index 3f9d847618bcec9c81f36ad6ee76a0f53816987f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 481534 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
    JN5nX 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
    Tj1!v+$xtx*dI+GHLe_`0!x-QO(~;6cfZ-l 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%0AKWfE$qTJhrf;L-7QRzS1a7;?kZ7Am zZwF;KE#l9Z^9^q@>bM8R4;K6XsvqV?)-gomX-Z=mfT?oY6m2kHNy_l zKhr9Qh0@B0#qJou5!B-U#M8@`x}y@P-9mxqDBqS@G9N5J<0~Nzm??Xs$4b4OpnB_y zP+cP&z}&wMS$ae1!5>iFA5|nASQ&Wh^s)87m}2fE;OwNR`cXvPNb>B!9K6pr6Nsz( zVh+bDBl}SgeF^u;p?yf_G9Hm87E>W!Ic^$9#QL!8bd<7RrEB98i)ZmQe`# z^vd5bEp8O6`DwV>)tKnf)V5m9vYR&+!g8(66TN!Wmb)!JLrBj=zyyWBPN1tV*Vg#g ztvJzewOMT8&vRu3QOYlvteMKWn)jStGd`EJqMSMIi(1C+Y2Nd+P%vPo## zK+%A}bh&>Gr2uxdVi|-PBDpDShRke&8?Y9dew$?2!gARP@<(&I_9SnbN*tkK(GmHS z!8ohSP7JMm`Yy1baLJJD2^HJog=bpKI9^5t=AfKRrPz3Afi<(C;yiy{iO-H7Z2w?* z3MZjyTu(UVYBq~;Z!}E5!QZiAqP1Bw9po9oGZA*8fCy!z>5#m?b)L#v0HHeO6ZM@s zxnW_#%4xHub&3(3hR$KiVFIUK(DZ6}XL49sr&DSWzkrSZ=+CN(fZ~Ye8LKcgS~%c? z#!^Km>|nC;T8`5YhxY(g6^)?$g<$F@Mf+}2&gIJXmnorm@(#qyvBS3&o&6mWYK7UT z(f{UNFkx_%+k8E{S7RuH$zp;|_AJ~uVYTBp?jT*K%@SUL_=rl#L#Bz0`o3bPjWZK{ zbyJ=*SkhRhPgS4sXb~g8`k8&3mBNa`)Br0Rbf!SKRom!rAv<`_>@Aor`m7uoAf0hw z{@_JAHSI5O;ss}QAO*_@vYZ9Iz4~rrSG5s^%Z8nM`$A2Fk4Y%zg0%ENbOH$~4wfY4 zOLBMfV)^_ZgK_|A+n^;TY*r-xt(VA}JWmNNt-BXGy+1@!YL*@wjE zhsJ>3t=jnpO9t!H(s9^P)iRG5UtAM6Pi$gfkHrfYywwY2cK%X=)BxXIV{egGo9UwA zn=4iFya&;#C?}wPKSWr%<;`VWg56ZK1y3#&@=16(2^$%rVf`3NGc_&@vo-jyzG}S- zV@OK;xJhNmg@X{SEQOUF_Ghpk)k|!MnSErS84D|NUKnFG#*ww4(=-)vVK}rx*$)vE z1BGPJ=GFuAR@e;-!_Y(-GH<`aGh{LzwMYh$(u`8{#nyY{s_-@jKO2@Y zR0rwJQSw)?Uck^SPH$*6LB1n!5P(3G4$_xC{UY+I^mhx1fp$fs#r!s}%i?0d=w(x9 zfXDh9S|@3QVnTG)QNlzAm-kmgwZNr&{IFBaVo&kuDEx{Blqi^F>cr1&NaWfI6mQTgJIHg)0m7$caaAwZ^xHVZHQvz7y5@@%a1{*D>=W zEXg*51y~x-ciw!v!2T~U-*Kv`Kg;a_`nr`bE?KFzUD!XP%JW42vOHn_5(V#Qj*Q*L z6%LNb8-hQKPE(U1FUOo8zA4yjI$m=0l7(06)r)3fj`o2H6@<&T@=O(&IA`L4W{%1q zOOeh*umKNxkZ(um13d&|{xDSz+-?cKK?H0B$j|#*V-dq|MY)u?flC{^TdSFuHnuh9 zHx@P4H!!SJv^Hsq&=c>ts^SWGw3vZi;SAqv@`6@Ym%90Jc;djl%NrW(Vc2~~8_u{e z2*pckAF!-QrAHdsw7BC;ZKYHq=?KNQku8#t$oJ3In&syLSgww4O(X8yNn?2smpg#i zJCIr}2AymGQZ|L&n1#^ST$uPJc7tj>pF(+EQ4AF4N5DMXQl|)tT8!k^CC-vkYoXgN zId(>fFAtia*{5!(DOzwv@zpFBXQSvksmjTLzB-{5x1wL0u`o{c1F0U&^xn|3;T{h( zzfZd-^r4TgdDUen_D2r6Y+-0W;*3kdYAr#^^ZTpXCM?JWvxcyb6R2c_%;{;4x44sk z3^jS8R8Km9n768m4$fCeE~OXB##Y59Ybb{?*VtEcg*B7H|HY4MaEpgD>)Au=#Vw^H z@?H#U|KJG}|G}eh1D@e~!rg*SGza#rEjE9CDuZ^+k;milC<`CE3Y3mR;|?=$;uF|{ z*zZ5|!m>2-4M6oD8U0YBE2)u_l0d5G5t}S2k(#hIg@cIr^FC*I@00Oh?Z|$trYN1! zWLvRRsN|Go4*9(dTqpLOI*FcGN-!(GKiIi8u8Q=d6UK;AUGNI5i3>{vQa*{q^vc=` zZCYGm*9BzSmKZz!IVfYaD|>XFCD7Sx#ktWC&KmjzO$&R-nG|=_QE!oK26K<(0q=Lr zVUVK^l^~+z+-4Cmp^N358&3-dq|=mqgs{sO4#=aDkP|&{M_i-}AC3d4r#;DQtc<$l z*ED~GHq;u36M}IF!;&E~F_(xZk6~9B<-~Zcl3JEZ7c2ZY|3q&b#@jB6plx#B7Nm); zadN&kq6?-+tELw@zL(ujA^mjx8v~U}EA^?+kh(LhyWXV6c2u)twtbo0fa;g)9Sh;N ztKZUoz240zg+6&K@1#9I2QTcKuizUiZ*}b8l+=DswB-47C=m8%Hv$b_L38|Lm zVgWMik7O>5I7EjZL?m5r_s#f^6kn4hgtOsqnGznx|DH4d`}vppzdQdb%c>~+M_%wB zzU0^>TWMTDq|rawLphy#m(D(C!D7XFBwjE=`Whn0Frh6_i2;nOlTMo4uYi+-!FDAJ zMYJyLR%kzIce{j3y-zo zj8GskE~iO#$4rAlq|4qt#ValVw%FitO_}7S>L=ohd$w%hg^)e-Q57bQU4+I*%7-is zwmmCdr|(gk6*(7B z6}AQ6;Ven_J%b%V1P+DH64Q-zK8CWf=DdBBWs%x7CK|2mX}Q{OKOw zI-y0oWZV7Pv30}41Yt>CQU-C7W)v_1bB;+yPb_jw@{9q~{W*kF@?veDv?@f%5-nJ^bsZ zC>0yp0B!lXCx1J4+RTYwk_fL!D2RIum_&$+3_4OoSOBHp!6JQ{Ompk1#v4dcyQQ>N z%eGYwyj5c!2eBri33|k<^+3^kL%UXeS?{ISruwBPbA8;51$ONIl<>Ci&&})aT}*zT zC&#fMoJMxso1x|o&HVu}o1_L!Kq`UFfEps#y$!UwXMe0MK@is{sJkKVVBVzc566|0o4FJZ*!vg%&A^GE~lphtKI#cTDJfIcKZz)@xw7Y z1eo(hqT;CE=E`)Ae`jgdLC~)4aZ8i{ZXbv-#8d9LQ-VV;Mv-8|1MAc<$ooe}yD~tR z0B=Z|w*iPT^^WCtTjzZ;+&wkt&CA}gM`%d?OFv8AA2|P4heyq@w;bG`wsG zt+;(e`|Jc7q52Pdt{y$OeN(%*9q%y7uEmh{fb7oo$!UH&yZsY2J3C`RUv@~lfp0h; zZw}}eYrAezNW{HdyYA>wefPudFMOv%zOSD2S2ziee8{#&y&LyK!7%@T(cQbfV2=o5 zf`c7HJA#?A6XYBGec>dT&xR0Ef`|lu%MYqof>ehkP)D2OJ7!M5WY>odNVh#y0w0w8 zgA+^WzT?124XRzYL)I<6lM!>zMdz4(PiTDu?%e5VAkM)pA8`OCp94DKv+kE3f*+wj z(LN%d1O2ScFrnui`Bx*ZKgqs4-y7=}clIu0MzDTUx4zpUt;};Z!54Zj#r`Eq@BWAr z;#QRTuls2{A6WlsxSs0)_dDINuh@{j)H`>S-voj~s$lk?ZhsK`b;k&AsxM>2gZF#M zTTq~gOl%KV=5bXakxD91pyA!-A*j>V+H|3ZkhdBGJxo$@itDDfpP9*VUJ$899BO}J zY*vbSD-BlQsDoOKt+lJgF7Vq3kDcsGnQ9lsk7Oh9Zld(`NVTwfKpVqU0{in;glz(P zTs-7tpkHgCIm4GS048?AT(!EfFU#+wzXke~FqJ+rZ0ax7g^d$noqY zZ40)`A%%u)v2AG=+fyw!CoHrz%WR=SEt~Akz+}Wv4Y+pdhl0txR~HPpu=M3p)K%<= zRN1QehFtx;nQC0fAlj(s8UC~-0C>oVLpk1x1qpDI<2?MC!kD~C7!AqwRy(__ZH>u< z7M4U<`wonork;*ILGSkv{B<-@4dLevh1Nrv#7}7|R%#JPQ4*o3wazV}A(sfmnrRCx z=3J;yQdudj20_S&Ez-I$rd=VSFV; zOTU3x7j#9S$%+Qv40u9kBvfLN04Vqj$mQE6*Cs*t1pppb(W1YEzR2Mn<-s?U^5U#z z2{no>lR-w(WPN4mQHCJpwUxfU6fusvT^HHz{J#Gs-_e)|@hv;*f#qFVw<%CH5Va?~{9%La6Rg{Z|{Lm@}f(Rkrn8 zp@JC$3gyv=D2e4WrJY>p-aw?>%L^yS6`JK9m>gf8n0f?`O6!f3#g!I%$QKDTg-v?*kY!nlFa@ykOh;8a(CVA=5vrOS&Dg6{_`)9|xOVr8l z%Rxo*f|pQ903SWEsX(-2tCzx-r&mFpIDQ4|N&1?&UYtcIgrT)64LHQ;>JMu+X@gS* zrMGC?`Etpz8Y3C<3|7q{K*sPHxZFh6lOjQVsXCJ<^JK^U_Wg&QVh_Q*MXW z4*g&-6WJiAfdt^^*c$-{(VJWd#(oNO0)i^2R^`)$zRmj`Z)iW-L1ijr^|qiQ zd_Ne8GFxOO-sx#)7GAJ$Qx7bH#C@pOq%GVq@hgS(ga@*{pL&OD1TLI{s(ZEBT_OmZh7{$%MF>o4>%arA=QRt2IUeqmv{a((C;+LY$j? zioei4JrNQ_bg9ijV;(gM#Tem{T#+bRXrH`2?gav4I-fCGvPc4BSc7iR`mT#i%7i(y z0$I6(n!GT&(kP3r?9m~NOB923A6@fpsFLL?SKT~v{m3aS^+RRaiA@@+Vt>a#pQP$( zNvbG@KJw^Cz3{VX)NZfA)L(8=+%#o2zbR+BiORe)qPDdugH(|HAAawb%%F-ILacD^G1oaLLmB6T#4B7J0MQ-z`Aauko|QnY7N24el> zfeQx!yyj^4@dDeZ5P3XKy?6XT211Vv1D7D9F{n6N(2CF3%eboI7gwPYP(@_I)Zx`# zlLeCW8bO+rQ6^nlG1(yQ-4irf8;vUDgv#$xNoRK5VB|W5v^S7%x}-N2dc5%iI>i#` zsQ{x_qU?G}Q6kM6q*xyx)a6l*(jvO7VW|*h*03R!N~%Z<&6cQU^2VrRkyMqPjQIKz zDza$jq0S_W^kJ(vVxgWiARjBDezWti$vqsSo7LPa+Kfs(3WU|&}cf>>?7oip> zu-U3W#Y`C!s}a9uB6*{-t!UkLp=yGq*upZQs{6$)n$D(qM^|~>o(`q?RL$kM6jd)O z3E#Ub^)ITsJY(hKoT(UF_T${Q904#>I@B|O?4LKi8Mi0}S061IO$(p6bb$+;Nm_6< zGPLh=t@TlAQ>oz-nN1(mNJi~)u%JYub6NLqokVHSmQ0O60 z%&2T^6P@H0RJCnu*Lu5)NVHhV*5dBCqR?Z{1=)Az==gc>{ga|y(> znIXxZU{=D#8x1qd;%ntDRf$dW`fNyY6DVI&z>SzX8PPTI>zsd3suy0Bilg?zyVuxo zfQ(I?n6V2977RJ%o|NOs!X0Pc6rRsrU2gmWjaE5l_T8?|ul+^c*dQ0#&_!dbAKFw= zQsAw?WES~C`B@~MuBI7{XQnWH*RYxd(L$Dtq&;Hjbms1O(8xvUB$ZR2LkbFTht9Q&;3Z?LF3m{3vaud^KI{qAypV&Gq&R{{BB z8w2bUK$Dn}*0*(Si%*Z~)>gADX|1YStL5y-yo|M)pPKbt-KQvZF8G%%i-C?_m5JfC z)H_^g#sfsbJSN(2u^NnUnSkRdUr83MCD2Svaf&XqyljpA+>5a!qpZtnrGM1x>>QT~ zp!9Ou+8P>VYBLjdn~7>O%cE`bHY^z&TU0XTpbU9Zo?2DDPNcULgk-?aub5Joegscg z0+dpc$2>)hD@)g)xt21-ec{ybZDVSrN}2&aR%weQ(cNz8zn&~bI9QmjH5*BcBMWz1 z#(mrrzRWt)!CH|B5m5qVgFmvP!fLMHW~%f3k|gVhzHMuSn}uc=wpgju8RXuCnI|W0 zV93GSST=*4j@J;G^qjBb&9`|cdwaE4Gv z_i9%qDQwA^r@N>l4jz#*l}1@)B!wt_$nZgw-$ne|vE%|-OlC5M;;rssJ&g;HznnfG z07l~jZ+=ClUGhdTBMfCB+lIOu;N|&)MioNdkYgiwS{8T)*#VQ|z+S5ZTY4uN)E#GF zPjq&<5BJ70uq*S!E~I&n!XnVO0m3eD+97+y5M9w9qGJ#I4Z)B%+KUn3&R*A!rSrp( z)CaLwev9YrIesPM(jT{LP1+=l5Y(QXRXZ<)TawEbs^o%9WlrRnIrejcOyxS<2R`hh zg)7~Qebj)*zeUvk*|4H^A-qLTyvNJ8XGbSry%aI_`TgU=$NBq-#&_(}_#;og4eW>& z#Zv~_b5i9Y!s;Q6b^Jj+(S$SU{wX>2mMpqA$>zgs;V$FmHLGj4T9X$L&sJSN5xSAS zH`;~1*JA=Ir{#&bDQ+nE3YEhQ2fG{A1yhFYv4OoaRZivB1 z_nwsk!1&sUqTpaRoMTPZaHO3sw6tsLACm7#>U;j2!hvjB0811v2rX;~hVX#1IwOr6 z$Z_1O<>`2}$xTw-K2o>EUuGwDkP8+rXRlw{=ToPw$>s0-6ks?m-6E@DaK$4z0Y4G~ zhE~RW0cXnGFt>Q3o9+{!olVr_mp6MSk)a&Sk}Xq_9$69!Taqifq6DtGLuJ{>PoDn` z@-p~$z`k2X?hnMZPcqSNQD(d46T(-VPAqzJPE1Pz$P8>tyy{b4Aqj9_cs^h@qU=26 z`xI>jZ2xZotH0!gC&C0ftJ!i7Ts_u|H{k$NoSM^4oOb^`()vtR>{89~3GV@$KVe7Z zuej0NEtkeKN=0)%Z;NNeHKMU{GNA^?sWS|eS0I1#;!j+~4|$w#*zO}X@;m;ifIt0o zZ-hAzg#9{i30qI379|<0K_t_6TC#)Iq3ZkUS)V-+^|&rS?LwBIAXCk$kN7}Ny?@06 zD1bE~GwaL?lTMXhS~c72>1$qUyEaAx7#o1}GmzID=Z+epKsXEFP)>p{9}A#h4RO5o z>F7x8%KB;OLXqS`ndIbQFOh?*mEWgl_l|i1W7dmRc;b7gE)E0yR3_Mz1AV(R8jO*^ z4jf#Ka8eC;We1j)XXTkf-pO#{;(shj`B&os9C1^D!qtnJsu2ShY;va<9dSa8n&ZUq z#o10GvmMRV#JHzHkk$nlmo3e{A-lq!$D(hoWnEs$QC=+D#PG8}?&<_*?Sk{NtVW@p zeCdpGOx_P83+G~CB_Yj)*cmt?6Bi;XnIkKiBLp9ccX5W6ER6wEiLVLNrkL_Z%2_74 zX8gZ%Ekm4Rr%9&io^nsiuF*#1Ap3se&NoFzxH!wOW1X%!38H+c&+G-OZtpssJ3WrS)=9AWl@ zbR%Wmq0YtmoekMK@Iw#<@G%9zkX86o59kJN?$dNb)eR$U1yoUFyAJ&^Q(7Ucm`Ex> zrJ;)XPAq8$npxS}mfW|3{fbn>x6~Yy1-_oW!%Q*)KCbGsBDTZ0Dj;&sAiLPp7j=P- z-n1k`K8`)&-*+2rsySwjXsiNdGXCJFs?AFTYc+VyCE^b2Pi(c3#~43=*UIF9K~@?Y z#%EvdGAWXbfFCF>Mk+rS2{uM^b5WLC;|BTFy%=WLFraxsHk96Q$A}*~33ZWN1A1Ey zx1436=HY%RmK!o@`3-X`cX8)fTBjtA!p{ene~uN@>NCXhvN+4Z)r`>xYcqN#F76LH-=R+-JY&i`6e|IuLsUvKPhN31jg?r#!sYh|?$K>5X&sfLu8Q z){*!g_Mqh0)9nDcU750BJW>YAF2z>DFg^EqgETFsqXA|th#3;i$$z-}Oi9d12oSx~ zpO9vtIXboYp`&ucVFhtoGT)iv__F|C4yNCg8r5C3pw(;wB@1SG|V;K?XWm_ z-Zs8^A?gwx@La{mFva2EdF3eEdf*lA(32fl7YtE{mjbVIiq-K@Rdyd6OY+e|w)Iyx z5zk8cJJ#VhzSBLSrN^U8x97%m%Kgd4;+je6Jt*MS5CVsg$zrNm)hJms5@1<9k#z?7qVrGq$J?FBqH=chpi^lNIqnZ(zM)>pQ^67^6Ol4>~)71#QPUvSaw*y zDC?CTX~V5#hw0X_MtL*bgor3k?lDE~G1quOGgLI?os86U`+=4SFXK!<>rZ;7I+W%T ziBs;~iANaiJjzYjz8xtETx6#i=o-JMTJ$CDT7z7A;?kFpsdkj6OsI7y5TUXS6qa2n ziFkVA#Vcq*T4F;Ik!gW3m*52KmyQthRR#ci2Jk91L|x_!MY8qqGd7@=5|km|kfnSB z2C5dMYeY?;*@hGL@&wnGbGD6rMW?#)rhQ#!?57KEb|;wGNmSK&881w`o3}k~SUta? zXU#&Dk05(i2js($FO%d}K1BwJGLvXZ$0%nS7qMO)>tK!Ex9VIR@BZiV$L!MxMoRN3Cx+SEn) zADrob07vTY#;B{vzgNqLK%F-rWJ zumX4cJ1aF?GKY~B;C%$5r5UO1^fWeq`P1sNr=2B@4*7ua#qxBz`~4(4lk@WR_vZ$B zi!tkOW5572J~qBG1y~csFgNO};l@yp13X^(Wh4Xt0}l*^n!!c@rMf;$^uy}n19Aox z@sc7(BMc>cPSRyepeAbmPtpMlb0IE7yb;pJOoU_Q=UW}vwYZ*+b(S1c=q zA2DIyP`yjevZI`^_XsrmR?bb2W ztmTcbUI1i1kBLZ0`g*3hWye@uzcv8Q9xB1Ue3&?P)v%lDQolqwlwd%9&fRy1K6V%w zLL!Wf%E5-;HH2x$gY~92^NFTjo7++}=^*K8NNKDwy55e8YiLbEdsgI3PpCzdZz)mn zO4;oGy-{fk<^CE0aKw!1_qE>r(3!jUhGpCn#Sol)NJ`LKXt;?u411`VYtM~=n{l&| z&uJRRV@6(>^azcK{m7$aDa=TUn$NoqX^V@-VAyYZ`h;}z=WzjX&EX}v0Nylaq+r`> zK$|N(7vNHM+3DI(SqH0i`I;93e?+lPZQaRfn|8WXg{?QFeARY|3I)Gf`in37kD%k% zYkPn~t_2}ZLadi9$b7UN0;loLlaa7Nhf)Z#MVjSfP~2C9aykYIib8A9Si#!wljHk0 z-l>1ZBI}iNuJIT9asL!e654zgLPi5sDnqDM9aF@3JUn7d%b<1;`q~C;dWety!q^!E z#{V;SFAw_%&{qt^KlZ(x5bZk}$6DV_o36^6&2b5dDksQ>G`aL%USz#E73rXpQj~o% z5;&mNE?KTj2NPdO&?^_dy2^FE0vFRWTZjCS_;;sAd#pf>$|0Pps;0YS`~_C*usn=j zjOeK>fB?vJu}wSfa%*Ozg8P|+Hsg@WM+$w zFFyEGK=nb+siKI0(1N8?T9mC*H}NPAkwx+q(3Vs=@L_r;ps;5mB79wH?wO3v6(@qElj~BKzeTuymqZmqr>0-55r_7kW)68?LSD6)k&)PJ zeVi1|+?H}ML&YN~dH0MX>{x@WQQHN#Vs{>aE2lpgm0VOG`LoT^>9zM>(Umd(M!09>xc8T!JcU;6be8#VE7}T<;DyLq>iaZ0qkO z>+-9uxzxDkGi<9}7PL*JP<}{KJC%7?=o1h%nVG>T|B9d+>9fkcFc>v8B>ny>$==z+ z5wz9=PQ{kWpyfkXiXJxXy1f$^yGWaogW3tV=_8O8IRy3leb247ioUv?Vcsj4mD%rG zpQus04vGGruMWQ0O>1B~Vb1(lrT}kbO8uTA3s_CBfUPUyAB5%snpR#Vd)QKPtXHgX zUvwjA)WQ*p<{*TJ-ng}Sdz+MJ$8g8gF|-pij{f-v`Gb8Uuhch_J-X?0oGOAp(?!4B zslIS`#pHboq6Wq%*SNyvq-YAv+K?4kjXbE;9SNYjmd@>d=w6pJmv&zR1WVAN z{EC+Za>`4mHscCuIP}G4@i=0V&*7sblAcO*cCb55XgG&&s|+TanK)G)U-4WRv>9n2 zd1T0}6mASW1MFN7lu*Op<0p}y_K=%g(cBN2n|^~;-%*3lQiPyuh_@juW*JmutD$!V zV_4?djyX>8L3;*mDACG|ZO~SNx5y{OukrHesZ$xz&9#Lc$Wk#@Q(uyyi=XQ%oAA|j zi}V;E%^FeKsGfpaYo8QEc{BX=TU*2Pv+-7<+%Nr0ORY8N9|io^6o~a-Oo9K7aI*fl z-`0PARsVA;~Sm}vCiQbe|? zY7MGGwochLlJ2X%Dsv=$`}Ph`*{n=Hjw6+zmZdNtJiQ~>eHSrb)EAxWWzfD{*Cx)h ze5ZM@+^<)^KW4YRD@1{!M3J3S??CQW!`GsSj5S=OlZ#$w=Qv5<~0825^I)3VYCoD1sZq?4*h9Bu<7R z?ZEFBEq&yH=nilhHXC{Y9DO!XEkRVosB%V@$INuyNPsMsgnA{zCq6O>g@yPUq9yY7Ja{j-LhUeYNP4gT&CJ53L9 zTOm2+vn5~N;2v^B(d;*60k??i)hIAHHM%7fI_ek_jx;}_cbuVF+&e02(Bk6K zrKeGpE)%O3&0T8GJg>BJVrZceN;=w+ip+qD;ed)kwFe=c)~_jP^tu*I(C~(4tb@5X zvTD2szRlOQrnz#Jw~RC!!itUW%^0_%sN7gbVyt5Dye`-%!Y)vbS=OIP^QtEIk>ko`b*&>9cfpEW@?c5`s<+>(riH zHjo*1Cr6ZKDLu~N&!!d{aB{_!#XQP8D1txG^drK~q)6#-A8HKb*M5;~)r_qJ96gCl z-u4p~jJC`>d|Q!@$eQTI-P~MU)`L<_`=hxee~J^ULxjMN*RoMDCQU zy<$!jXO(E#b~jhN5*@w0nOy_-t4;fko#e+q;+T4j^7%Bg0%&$&r7@i}7r_$iw%O+1 zz}Mjlg8uNHdBl!YLJGk)NW>SW-gE3cGci!NHgYS}JR{H;rMfF{Ya7^{veU(%(=N@? z@nETICafcm3WkN8&f`}U5h@J-+;hC*p!qqP4@YBHeDT4{L0{nIf*?hfA}%X{aN3xe z$h+iequIYQFX{wjb=p9Wjf?L=H4nZN+jfGSDg2GkCIK^T{P`|HD&5A{1@7H&t@!yJ zIEFA{4jEPE#=wcb-fD8Y(uWDTL9ll|tH2u1=XpgBV@Vs5w_i#hZyp^4X@(5I&U@hy zw|W#wS1{ZfRqlR;RXAX^pxHx_`hsx7bbt(AYG2>#rs_>N&b$Mliy+h;8Mmi0%YS&o&> zIGIfq_6VfcJ1*l2!Sr-b-Y4!>q53===6N|(?v7FKNJ8h*srW{AL9d!kPUws03Hb3*LjR;mBhl=APp<$piH_Fs0(|2ebt7vw-YM*rhKPVsA91_gx(NdP;6 z)FuE-R8*0QLU2BT3RIxWAtCn^vfvWW_ zZ2XQ^YwgO;^`Gf0JC4aABfoXd-TM>oKkojIL;g43NTnJDW~v3SoMDa>c1iQoLcJtM z)k1Eto#Zjvj@dDGh{;>TM{=1v=tJAYTcnJ)(8VzN$;0)NXFD);@Z%O59j_B3y-a1s z{z$Ljjzlq^_W=oz!f#?&i2M66HA9@i9%K>rp@oN=LIkOt1h5D9OlJFKNWG-9ltLa# zdGeumEji!YNO{PXvuX$2KJqhO{2{lK6No}@g>?LcBk^~}fp^JBMJ(@?3Hi|PoeBC- z@1+UBB=_LNj4+ldDva8)9F^d? zj1d{31Gmao%}6@bUfkJOUSHqYXs)VmZm;bz9`Xu)x07nU zF{`z?vrf|%pp;=F(w@Jfe%Yd>nB4T^K(d*YwPU=+J4O~-xkj!P^%Cv}!{e1vKX$}oR5PNu z^hDOe*=>p{NoVi9LSGkdAn@unJ48>zu)|bc%37w;6W3~Nj+v(2 z%4nE&*^;wBEQvm$0JoEXRx5um=qH)yWYLbZ(3JZRo2Tb_sb(#$IJJ%;N(LXmhpyRM zx4SN~6oUd&b!+Q$$Ia^L+6DQWLZ< zjuDF&;SBVFEWG``6#QkQ;Gn1jn-5;P$19I-WsR(NEFK1R zdSochZyG0v`?j#h#JM}pfoFHYM?`E})P?9rHXW!g`)+vzTD(mCR2WNhr8sPQ1E_=i zf502oZ+{1E3YRCyio6z_T-p`RYTjUj-|9zr3fWXic;v8ueX)K$U>{|w@BPT4Q3Z}u z!IoBY%*anVzfE80DG+EeN8E07sgFFb>wIdlWW9AAoaeN9E1)Tlp4F_VB&t)fn49Hz zuDur#a=)_eLS{XXb<>~0pi0d6^u(+#O$t)LEV*GO^J)Qw1TKxSdy8)pwdZ;o|sQ% z#Z0}i64b$9lEIG*`|ZI}WqwQ5wOns+!pRNi0>g^1KXL7vZ%-jJTI=bDp5KnT!c#e= zSD#RI8H@9cQ|BL9Keo%g#<^``v|BSp2Aeh-+r4(El^cKX%9ojXxjP$orL55`EdG0jJ!d*|b>Ow5o zpJ3Qq)*4sQo;89gw~h5!e)?%||B&o6+R{Ip7`f*Si!;9FIpB@z48WT!7u&4e1@{M> zQ|}jTD{8dEj~82E4DSYF>a16>8RzsTpn>9b%*sx&neFqpeoCFoagR>tFM&mqaqVZ@ zz@Mm@PeA!o^o!Imfq1)X#7JvaYm%=xP1LB)%6ATRSavW6Qtr@8Xgk(pzfv=tto9`0%wJ~$Mo~)Et&@sYWgq!5) zbzhuyZhI64`z7B#5j)%)uc__)pg<6A$KM?0@9W?1Ia7RkvJ&(&98S64FybOFMPy3a zpI1myq^+50#9$<)!c?eAy`#Q-#{_zEAC_Qu#3~1*e=%d`NX~T24j4=a@kfN<*cKmF z$;}y3YP(xg|Qx}kNxf$$kXQ`Pf*7l zi5$*-k1d@5XGwxn6dT84Au;Y8?MJ;$8O||1Sa9nfOngp2s+X5l+_bT9#b%5n@P%p3 z8-`1rdnV>+y|L4alO5}nD&fj%W(Nx=X^-x&drOz4P)JOm60<)^`B+Il;7c}jp&r##(1R3~G` zSjle2O5%%y7Hfe~z8Nerm9C3taikz07M56zX;Q}M%dQ8+f_r}w!?3Rvt!S2-IQpT z){OEfNB78mLR8XulBc<+XUKwj`>Zxh)-Yyg#yFCV?X{iG0)=GIj8;EJv(;(r5uI^R z{tFz*Fy`AKAVj#UpuA&Cs4+9LKJJ*Z?F?2cXTO1LSR4>osaIn|AoxIE3ykcLvlnj9 z?3^ISlAoj>`Oc?A^iLqK`jn&58PVxjb&RaFezDDW+G+K>^OAk$2i-xF_cX~j>^|7` z9UNLR*Rw*9{QW=(chmHBtz_Nq$F;4r`C-2v_P+(oqRa)w(kA^I#@X|&N5m9~&2up> zhTlo%?=9pdkXHVYN#1)rS+;d{W9C_8#b7Y-@5>Vf@X@ZkO z8uNmadx*3#H2oPTKu>KAMj-73V$(lvRvxywT%W&$>N^wDL0<_#iuTo!miTiH-s!G9 z#Q{!&x|cq)hzCLBHJU#j8P;ZefOkqh?B^rwh51*s-ZI;u{*0h#S$8T~D3@{xgEy$9 zJ~|Ue=%F9Hs(?eNWW1WgW2?qStH3j;A3C7_#Hib6_bRU%ZM8zNQ%QD&{fTjN)D9rE z!pYkc?{~K1@vAgDuAXbPaK)$abZ5G42!3O##`8p47}RLcZRdwau+zq$zVVMtGdYy{w`@G4U{}lf{V20iQ-2e!96KKndDQ2;0R`mgI4iNxlN~QCTZiy2nv%wmF?oUQbJxj{KDHKke}qU-h-!Zvdha?E&p7> zz-d%#H7>Utn{XkRs-?YmbkXfz)eJAQuiUXPI*$H|7PxVCXfj+qqUtxVriEwg%cklQ zWAS6P2a(zbBL5lKDZ-WG6XZR|e=QvyR1!6U6(T`)z2Q%{{Xd@9Z(#qvFIy~Nj&BqL zv@=710ULA-v#G9gWR-msG5=2>qL4T87H3^@GEP)`hN#BD4*n03sr+@Ab+I?`@GkA?dtAd6k^^fL&f8G6f z??CXn|K-s)asKzenSbXm{;y@W|ETZ!SE3@}YV@DiKmSTsWbN%N|MLpy9|i@Lzm;Au zQ)H~zb`nrz2}mYjaLJNF1w>#9Fc5T&k_jP70#vzETV$Zgo##{d2`eN-TAL(M%UTUq z!&WxdrHv*Lb5e~M`n6iEtuNxw;@8%$oiJTzhvMh%H@TTI5(bS~sX4wgJD*qI*Q4C0 zC#io3{1N1q_H`L?=5zzVBPtA|aoVbgFT~<8-y zl?UAr?Ou!^dn+*XoenWX`)a~+3AQeV8LnQ85bcx)^%zg~+}O7Y!|JKdbM<`C^_>qd z?XDj87)-7DaNTKd`0Vf$p7aoV_Xm9Sw&Lx*?gB7(0fLAK)E@t$m@-A)>dU1Zi@uB<2rzpY7JnnW6on&8=DL-c#n z3OR5LbrqKIBF}5b^APxYDb|pe(d|glV(Q>WNUd_sy3p)K*6K#v&+jpQC&rTgI?T7$@662d>#V9PcG-xnws&9(G=BX3G97djYiY~nlbW5Nil>BX1RXc@M3 zaUc>F9AqlxdODt4js@Xo4?1uu*}lMjF5ltFS-oM)S$gOlpPgEH0QWf@wuE8cCmDQE z$)*gr%wwR?&9OS`3$+WglJa#WvRQ{iUP=qM{W44oOTUjYSQ!408LXz6yDxq!57R$T z4+PqjB?j-QVz0se~=v&{odw>*eN-&roT;1s-XX zpV#J^W5O6_=)O;)0$2E|F1esEVCIFTm*RJOmU52_nL?6-bE> z7yU&0Vv!Gpr1a9F)b_E$Plg$_J+HX@gA1rthK1!o;{<_eAiTf{U@lNsPzP`w&@tE? zf!|&*+n{$~aezaqLV010C^T#+QzcnXqnwc=bgUQtlx(Bz zSkvDaRc$YY-K`~E4920RR`k#nrq`BRO4&lP)@3~%R5j9peWB#dSDrmtvnTz2#(eSy zGhSA{2UsTzPNvPe94Qy!>kY?s#u?&i)j8kZu7nhAE6fdr5ht{#zNf3}XQ1@hPyB#d zofPxtXv|~tH_zyY$w#@D;)rtwQ&!#a;Et5=D;kR4)bm`k`Rt4vIUCU!Yth3L+OF&` z-!~-lH8q0yk_h@T3+{JV$6y)5Mvg>b8fDWi6~($UybJk#8>u$#--E>n+#umkk2ZY4 zO56i0d{ItqpE8C!CJC_*bT~&`7!#6oshD$A6|g z7G#}p3_X!^A6VKZHY3KXnIsM$a8Mq2+{SR%#-HsxQN54ox2SIo^1Gu!AG~uCe%=i} z@pV()9)iA;@~8Ce$M^&^KTv*Y|BiWlP#;h3W9?;)dCx%`*Q8F1FNt|{de@*i%8z*< zjCq7J=BHFm`J~Pte#e_QO`1vf*Npvx7VpHp*%#aC=AaVpM4IhIYHGVSwC*&qdB-gU zA6;Go9n^e79RX1h6||W|LIUvo*I&um<0gRd z`&^jEt4*d!Oe;8P?*#=_3|;jAx0HdeA`Ff!=jo^AXA*2lbFLdHb>dLKuVQKCaTi)osKIJs+c#;HM;jMq z;Sr~^PW;Ij!1}P9aAWyJL%onQ>Y%dGGl0QZph=z({hY=)uQyVeUOpLX-+KACEP>5P z*o?&zwq^}cHeIf1BQo2btJL&8bHS1smHmoTIhcfy*K!Dk+^~3E2^4MUd`V$07Z-y; z^0DnqBy7@Ty@`1)WvkwVs6`Q%1RzD6eXN6cPa=m^{@0?_m|Q7{hiNA~sXl2mjTf?z zIMc<0=fLLtav7m(wu#n=G0#xblGp12yra=FAnfp?TQ|Ep+0{2N!)8`td3AAM360v$ zs?1KlXma|}q1V!$^x>u%;2Y4)#W{tY@xukrnYVi5Set zrBu{$kt&|F7G@&cU}8xE7qzSiXQex^q{duJvhlhb+f~|KOU<$_qp~i^k!O;4Nmc3` zJPXaeTjxLbS@4bqIj#!??FEeEd??`viC3x9L`m|3tL0sSsaYvLV)ZLa|n})8A>6Vox+)ua+!q&540NA@#-y}*X99-AXw1y%q?PKm%MQm?ygio7n0>8L!NOae%G2?ZF{AYx zF;m*=oViRh0)PPAkSz zx0nV`j8URwCDBy}=&R)isjsMUQ(1H_7(%CM?lTg=Jhca4A6g^!hmV_S0NLRS<&=X+ z&6z5ZS-+?qOO6JS{rNkhY3N;2+};sY@5>^eFy9B-`)|*oCA_+_4Ya5Tv%APCFv%vgT^xM?)!n z)QNSrTG`1`G+=iCyKn^LO-hI9Mn8v=&|SHEb@n}JT&+jekkE`@y-prZPi`MWx&uKcw&?H|u29vGP3x{7FG1q;=5Ve6+Ius)nPMZSIg~fcK3}Eq}3{J&E zkXjfU@b|hoQuyk>j;pxBh)blD!kk)GkYaf?f-mYYRt`8e6@Ubc zbDV=vs(5;}J++@(63nLbUSDuUXaW2UZgy}F!Ni?7=Wx4a;r=kJH7bVcf8I9vz4ku> z#~zxYyQPN&wq)+?1mky-U`@JVTJMNo-KzfLav&By#&tpsZXK^x?jXO=Djk^PdRe1K zNW3GC?(vRGHIKAGqjKVOqFU9q}j4E85Gp? z{7?z6J@XXUl$?Hi#!k3(nesrCE-|~1f`6>{o>C>+s9NEBcN{1706Oz zJ1RcP@k>wnDuQLww8x$9!LsnD;qVrYL9HS9+(;+Hn#Y5_R2paoA=w;La%K+#XIRna zj`g;=H9D^Q$fs8HjYxX{d`*jIhUE!F&^sZ%M?~E-ouEf(yf*RqqUa0DZ68)An0@R> zALgMIBkFrtRSS&sD-7{oe>1$*F{)%wZYr#^*=bW6>%BRTY3etYG5I@&n?eq+43iNS z|5bLD z)g!gab~;RPU6qx1zD}loygC&RB~@3K6Yv~GNwW{kM3m)HlG*@F6BCuC^`!M0Pql?! zp_60ddt|Z|jY7qyvQ@ByTrUr7g2mWGCy$7hC1^rzq^0B4bI1XQr4+3Lz);508alcB zKDoUAnuh`1>%QsmC}FXLH{`y-+a!LVLvrm{Vth_cP{3MD!kRL^emLol$fqiKDR8LY z72#|eGyaQ1IT*sZ{)9Wk04(b7x)xHEv=lTF9onG{RD=6biX%8JxPqjhvDcTl3YQlg7tGZoKN6_i{S8Z(Pc zh&}&%>(_~D&7t&T;W_##dHC-$45a_>Syj&7#PmOhRBIcQ71Xb5BkvT`K7;LFS)}zr zw$W3KzcPzRhB@Gdf7?LFWu%z$>d{x3r`%OB7QtHx=cn}uucNFN&F=Cg8r?<#g*2g1 z&5<067gH&ribfLMzbYPyDpaz4UQb?otmuJOkj}I_&%9)NUvr*pGkYKPnok@5l-xAH z1KTJfsLUP)g__})3iHv3PFVN=8@{bp7-R;9!&lGx-u}HcWPvGyb!NXO4JPCQI9Wq6sbEDw|Dsj17zLx* z)7nObN?S)ONHDKhD;Cn)U>?ggh+*!r1#shd8x}U%O{mdYs9wyasoJM&X$9%bykRhc~!IsDd(U5Wp^wUXzmf9YvU~s zEPsL=feo;EW_D6-Qp777)~4tpFE_7iT%b+aK-fe$kd+QY{3gN%HPh%BAqV^Zw1cT| zm1dsOSe(zOxS-+=)LMkX=K9RyfsjnkO>ZiFV%&miwHiBzsTBR~`Rwol{z4W+ykI!& z!Uuhb#?@pGkH+Gj68}PyokkSQM`QlM`nQmm8Wz+m7~IlG4+?5kwr-rII5;83cJjh3&%d zutRMwNDu9a)>>IKSEo#W2uQBd1JYb2YX}qUMCAkcL{+D6g6b>08IoMdIz2@oAa zi=e;^lrRUZ+#&GKn|DsmK&TYmUh&@hVUEj_qT0=&LA+xy#Gr&-y zl#<8cuuL9$IzNOLQJ>VY(~fS=J| z2`Vk@@RJMn`8=Ygja&{iU$IV}jVFN?fupYm+URU*cw_E9(;T2U%^B+*fs&laUW}m> zmERd{_4rHN4Lz@>n%ddtat`GK97hLmOg5ZY%~qMBxpxIox4Lq%3>bCwK;Eu+V}VBx zo{t%Tz&8CLL2nUu)R}*=im*H5m|?UqA;>d9z^}12gg+Hx!8>X|q=Ul`DwIZAcV?4{@80_pC=ce%HJf>OinymK;WG`C-9_&U24{9R3Rzz53Rp4qvk<46jk`%R4+@$P{!9KX_ zLHJ~<{#ZBbdT_UE9e?|`xXCQbl#y8c={>ndFCmmax-lXiR5p5IU`hujZyw!+8Vn`Vj9u${2q$oTdQH9i}=He zehFq9_aZtwK}I@*s9jQ@nI7ObO7utKj-Re3Q9g<^QVH~G;Z%1FJH<8*k4DI5;k-t@ zr$t}k9AvS|^rr4QI}>lx`>2k9ds;`?F8S`{+`%XI*Jz_sT5pHxloipMWTT{2(+NFC zJ*l``4_@Q=o3W=uZ>V_N2~nNz(AU(d#<(^P(Ch;)3XPv<>o$+C-*{`Z+s3#^3$v2K zkt?ntz?A`$HtO!&;Lr%7AbX@cXl_H$1UwuSg>i};18U47ISu(>k&$=zeR`r=}Q9!JHrU#xTf~W&}o~7y@TTN~Tt*TXoO|%su#fTy|E3I3pw? zepU1f@BSreNAgSX9zMZ8_``S*kGwbP1D*4l?3=`tUP}bOf1js4b<{Uxv+}SDgE-uBcBa zltXvl9CGb!)5Cs&5U*`-d(qh(p}S^o`Q;s3AA5kY#}RuM^YgPd{WOzC=AcV=T@-cU z++5pP2(PnQ3W-B})YzOJ9alJI;(}vaVuNxwybsv9jE2gD*b#U9b%>H3dRoI7rPy#~ z21D==xaGk#`Z$Pfkiy$IYiFX&kqU+yx6kO!2Ci?^O}u^5UG?sfX*BS->dY)}xThbV z-FV7^#{O(T=suplpfMkW)2^P-5V{K)U+VPmztg4 zh!z12vl+n6x>>x({F^VT6P9kr*Z}i-$uYMJpXTVu4>p(CU>6t{e*9h7>CW)&F}#b% zY8L^{n!|`77!NmY>|GHQ^YQ#N2_;TCl>D%Gr|-=}Y2c*z)|e}2{4<$%+s^pJ4a^Br;ySiGQm6|Adyt zKIuK*T4TR4Ug^I|55Bqd*l$Dsko}^!zR0{_XIq2}#&Xy6uF41oL@zaY*zsU$(=oKz z`95TXHghktuq@w{_1Yu}I4<7*8ppzAS2K3io|Ee&+Z#-|tgv)3V%#8=XE5#bwox~j zmMAE?T595$+iF=U2$#S+b<>eTfdw%$-*ls8UvKXnJ4Z`9)lPoikH~OUt5|4sBeQXe zj#`u4h!J0bRe+|!D5qM&omp?1yLMA@oN;_fYH#C}TNr*VIFOslXiID-$f#2)Z%<}g zNo7)(OqUKiImV`8;Daya8WXvmPx+LrU3lJC{I_5_lmwDQ)e`fo$qD-9BFimvlx;Z4 z&AG$6^vW*LZw!b-k-C1!>ENEX6GMjbh_g8eNa>0q@>Z?6wbPACG$sfIHt2!`IXie6 zUP@YR{gB7f6FYHr8n~9Gb_cz=v%4)RIlp0Z6)yl1j0c%Qva~AAq=w?s-BF!N4Drq+ z>t8A=0arFErG?sd1N<)5$W=p^k2Gt#${`{{fm{PIUXmeM2276@Y)!md6-gDUFtecO z=tVU0j1tm@Vme!N%#U1m-BZ$CP}FdcTAmxTG^S{BEVaF3qXbJ(TSO>1U>*%0H!CS8_Jwj=K#5r?#Plz9||dv#ePuR@SwM2wSZ^k1&Q%qkyJ$8 z0-{+VO;*O(TU3l_C&Nf^ruQooV$helTGw0R~}OC+BBlp)F2 zA}ZG8!4cOM&(_trVf~|~w`Azfc1%3ejUt&MHiAKL2T+dMRFRcs8D(Ww24R#Z2U%@I zGffN2R3#DE=DlhT|*02Lg z(m|}I;`Nt{YI%WlcYLOL>3 z2J6i6ZLCu*e=)(bea?}GpOw|IgmM}ws<^N5$TWg7CDIw{=;8OuS&Oc{zwWgC2@;eUNO-bjvVugaa4!utYAaqIKjEM_(f`x85y5 znVbkWOMO4eNfbc%+~jjwH=0rY&M-TAGg_i?$rPRvG6m4M5?6xQ*iAMpkPR?bQ9!BrN=l|O|uUuC@RyugoLtQXBF zTgU9iaWEV;VRBnVBB%Se70+KIGiHv%Ri}bF@9Q)YX{_}3Lz%%e^;e2|G{Y;)jOy9z zp${we!dMZO)RCN|REx&HZP^PBAC0-KOdV<~c*(4kPaeGvi^i*tcEPAmlc1S_>y2g~ z0@qNwiF~mh(nKmzUj8wVXFg8(82rWAK036dpo$u++*b|L2}39BvSA+ElWXl6h|BwJAwdSTLj{ziCW1UsSx;z(>ZTF-Z4A z@v!a@vjZAUUH`e`=E-u9T+&3LrQxyXqDadSw@-L1)=sz5h<#5mrb`xQI~vdyEAw&5 zB3|#HBS!-KrRzo5z}|vc9-n`boXzofvTE~65exz2VU3Il^7{*El_k*~-x!&N+b}2LgrHm&@nVNeWlabbtO7F1tuw4s*X= zgBURfQtX`%BpCngM2JM8kNt%vE`|0&=z-uRaS`PAtN4>)-HVaR#Ejt|#IOkiTaY7W z^o@8~%uRRNtVG2urjAzH-@%d!7&6wAJ6W=w0+^Cl9dU(cjPWm4y>00ES|x;mBJ!G@?x-> z(`9~W%*Pd}I-@7?;bsUB>By@a7;Ss5y}D9tJcH7*DwGM_&tCKprmS04oPj^_FUa<9 zhjIlEHN25kg(7(94KD>x1i=~;0SNIhh%t+hwLk&<3V7x48@q5KL2wp09_m{|tOrEX zmNV*p?*`9)O+M>EYZkrx)kWonAVzS;()9TgSPdsMR?(vIp&qGWUqk%q>ygBk3OyP? z)5&HaCg|ZwqLikGUThcs#5&qjK?}HP7yuIsCM%t_4Xe$FQ7h4i1?M| z#qMu=8z5~blx_2Ga*r}+V{il~6m!JYy9JNz$U8v${tVOy>HLg~4>h4zi`1J@ofFh7 z+pc&*v`aqr(a6(ePq8Ze@!+GyvCymjo?<-gz>~f9rH<=*h-9-=Chf2hdC#rbgUL~j zV(0^5R81k$%u0tdA@v3w0T9yebFvU3tD3Khsng`-HK&`FE3TPZ3{#B<;mSKucdVd# z_XxAR{M!VGD9%tcrH3Yu$t+ubGb(gFu^|zLt_M^4-4PPHB;jdwG3r>asunh}-p5dr zAqckmvn}}po76E%XH5l-C)s@13@X`M`S(7*fjC?NAR7IaVEh)sY*%q|fKcp)p|Uaz z;DnMnHz9Ll{LCMVefP_5B&mo;EVA^DDM!(vhFF1}so?$2ae2jTdGsk-?~hL^a{=U1 zxZGhMU%pjrZf&wpP-MfRC$Y9}eZ>>#8bz;^CX>v{{1)wq;}FAWY@REISwk2?y=Ver zJnm2mjRJ{f!hQInpbCtz6xd~hVBWu3F*!-T@D-aBe1|MN{wOJ)5h)M6Cl6rlJECr{ z2#)SV>3S&}L*Ca2SKDQOUB5y{d9PKOxZbwtF`Xj2WExht?y%Y8Lug{<;g(zZ_i>_>rS~3`Hq;7Y zIPE~CdM)UhYHi=z3-4;zf#rN^J>TsUhoLYjsUO#$JJZfsgkbAE-Ieo~yJY&)aX(-QQ9GZGyP`Ts>DD=5A=oCKr z{UK2Ds;jjQ3H?J*sT4XD4(a_1C^P&0}iHT@4LJra9wQ1l9~M)dmq z(5O8s!=g~B)Lj~TP$576D!)|3?(PBzJBg(3_6s2?wrQ8qsdG@h8{x%zw~a{a5`Z7EF&?>TwhQgyLg`bER{nYu zrT35MrKzp;c(n?ty}NDJ>qxA9xR?8?$ZD0=Tba1{9nwo?O;U@^>ys4L3zxgNNWalk z;AD!6tj-Ku9v*CbC&1Ceg>XK{4B?M9RWUpc0HH?H(bm%?cA&$6>MH7LXz7t-@I(`R zt0&9n$|5hY4i8e-RKyC z>}+rnb&|0pzvL^k;v74ZZE0YcQ+k5B!nz`J83YN-Y}|rMf3j%F<#!9%xKMzDx27=< z7ECLB-}7p$2E}|2%PbNM*a3H4_f$q;Fy%@pmH_?;+N(1z#xUK^w2BULL`y1I2PiGB z7iQYzInBlFPa>5?ZP~)1yNbMQB(v~u8{Uwj-a}c11kH5vQa-qrYu2tU?BG*zf`vNJ z>Rm~=<7cFYrg2>*cM-j0b>Kj}LJ=tf((gTi)M*wXlvTvD2zAUVDz3+rYAqrS|i?F3%175oFD`>pyD62^GLwNnRt^`;pt3n+IxPLJg-hx!2FC3+- z@2~B6H?2-t@*NlqV+a=mtvGomlH)1At6XGE&CQusO@D7prYMm7w#1B^DS{LmuNYu( zX)5KeX;oWM*PAPF9|=lefWuglXF{M4wZcIF3S9|ny>JBYcX%)BKQBK6p%@XQRtd5! zA)-*_jy;DNIU4K$X8EOB$LriXY23C(2i8gdrM@*M37 zK4C?4b+xO~E(<>b>xQZAwVrnWJG;59w0g>c9{i2~N32S|)T@LpD{Y!P?7%@ooLYvP zW)=zFPFP_DTMpA*TdV)_9BvVS7y_we1!Z2uwLq;F&m@wBQaw_7W;4Zjvt6J6AsAcw&s<^^ORHw6eDL zt*-8=sCd)WeK2Os4YOB!!~T%H-Pcz6$H-eUf6#w{-v7_iOAL;)cwYOlWqc2d zi6irU9Pk38`pmeAUx80MMtKcn)F-&SVB|eyu56!?yHwgi>ZYAVP87HsBJKfcUxSuX z?MagJex(Dv%x_4f=cM6f!*6?RYClmBP0>=o=mroNQB<<#xl00dfb!nK^&KOziUD4vvs-pWN&U!ZK;7vD_H(O8Ad^Ty}O*fW5_IkAg z+~hBCh;v~AZjRm=(7PEV3PF?_`GIM4K@A?*LJ2H{{s6XxpYx95GcAhasoa8qsUA^R zO z*ZZyoR3Xz7H~QKpl-2T+YsFOEN-DMfgHqv>2~(tilH&CdIV$8x{L=1!BL1gQ41b1C z_2QH}KJtnPz&|=bItV|&ulz`Za0|p&ebo3H1XJF)b?L`P}8WG z8M=mKG1S!UJ2UMNFT+L(NVFZXq}LQ?-!Fh2EvQ?fGwQ;Rin0TJWq|e|-pazgUi`6l z>)QePkOlXF-nHl4)du$80QLvJwS{}_{L_2##~!?E1Na8`!x!8Kd>5Z%mm7HJ=+6sC z&j#=g=&K&M5B}~u?kzmJ4UPda_n13=c1{Z=e?I)(XH-)+{hT&uaZ?6Zs>p^&Pq`WUc@~p>kpmi#MNB#6ghwVP0?pMPrSQFl^j#TDXA6as`<9Lw5PV z&T@yop&^dZ^Z!z(b%zqXqBgzKGJFF^eIaIa1pr#lRKu3!wTEc6`K)Owd#y+a>L>gY zQGR-Sn}jms41tAMi1T%A* z(iW)CK8ixxMl+R2TE)Ef5*E@|ZyCWYlUd%O3rmpIRA8I&vlYsHw2-!*P;I>V)e^wo z$Xc>ctGTmQ$^uOJOT~YTA=QbFVn9`bUot6Z?Ek9W5TwWxBkdGJKAMYc=qnt5MMa+R zX2is;c|NLBmG;gd`NS^IzlYa+=Qlt0hQGi|jF8)be+JQwyx3*&i3NQ8ntPT<8J2fc z^%rZc|LC7_VedTko6ZBhED_F<9N~<@Gl%D$g5sT7dz5~jujL5)8l~zU6F%j|lkz_? ze1tq9GHvpM51Of5W(}tItc2)_bV`q?M{aKV8tjK0VCL&VM};+o2@myMR9TcxHS|i4 zu1?RNa^CzRqTY&P7HjvmEGF?OOuzM|NPD(Qh@nTTP^AoHrUE%pM3XNfxs9D=ahQAU63b7E}BL@2XV?>RkPfZ&T_ECSG$!8petN|i0UXnmz`5BH95;yUxO zN$mKfdv048+hM1;>GUj@7iC*!q-pJy!dy2peNf(xwHV3kGy#i+(gioO=Z^Wy3M$*fn7c%~^||-148DD{f6*&-sVnywdLvODp!UBu`+wPYqIkg z+ZjG7T_Zv#p(2j2AllsvC7$P&kNq*JKHada2hHMnHX*q~EI`~93B}8TI~Uk&?ep%_ z$kDag3nsSoGaI6LcJ@}!q^^9RpSCyK5nbjtWyGw}wY;-BmeHORHRwFY!uTTdBoltu zzDI9I)|NW@5Wl^W9sHRiOHvV%W8%XUnFY<89}i157Nz^jd_=neUWo9nOJsV` zRZsb_hF%tAw#Cu!KwTjDMWkPWmJZmi)!PY+oq`kX)&QWRjfjM(sMiT*vq|h&NxsK1?3e9BQfLQy-}qEWq=T5X5mI#DZ2Xtf{^^Ujg?adRzLl*b!!N;7=- z%dgG2X=6r6+&nBGI<*)MBV7R>UeL{_wu|}hmMp!(#;+_+SM=pAoZvkk_^r%^m^1yb zp8@%rf)%!32<*+2Q(L~l=a2k4RrMj5_YH9FkoaW&_~^aVvL01vM->Gp-^#MZ&2 zsNuH>QYEaSl2nLSfZ5{3nB>*@*DXWO!7FI^KSJ5VIECMQB}`xBQpcqf>A7=A=%s_% z66hEqGCdwC#nG)Yeq8L)FOpOde(+fByX={sbv%r{B_2(zCq^cwt)H4HOG3aBxi)x?!L3bJH8w&vEvwDVTUj?ZQ!TaxF8`3cO;e!) zv?yixs&QV62P=0L)gy5S-vnRVlh+N$As3((V$v{7)DkG^j8f2WBceZX{VBQWrVjCdfvU)M zpbQ5AIPjx!-1$d2P9GVsE@>)0I{8lR>n`zWzWn_p;Hn`)em>Hp(j$jj{IKFKdAqiq zxS6uosz~q;4~9Q8dxV&?glBsXR6PXQI7)YQJV%K9Jrsbre|Mymy;b|+k%dr;oEr|o z*@_6!S1aaE7R!uEbBYNLG@asq8-h(UJ{V|{$h5>(8~gEEkQVwC>&7<>p~$&(N_Ja9 z|Khf8ZL_1qy}ZQ0zzl_1aPd1bmcxlO#h_aZ<+HT!&VIopS;+{1z zkO%X0+r8$Uk%SnNpuCd9D$8TKLx3$^;TA&q_%8qA$1)0~++Ry~G>khvpd>`DNAj{# zX@z~$)p#6hL|ybI+%CvOrZi+O1THauDSng@Q|Laj0qUN1sh&Am7hI^$x6a$dKKKZ7B6aFvAIM$vwZ9KXw`2+*h9u~PY%MWkqKq_0fDZdOWbcQBr zBo9!6t%SNf|ItLGvIfU}3i$UYbiqS$0KO6lAlVYfC zHoIj{R#l}E5rOcOf}0`TP)9fbqq_N5VZ32}i%M_a;m2NYGt!C5qPIPrcAEGpRdCBB ztsfzj8tUwk&>EZcuv)$!7%2Pg?oJW9XU3V>6f(_yj!wxq;4ReA(&XN?7h|WV^j9Fs z%WSqm)PD>bdmr!FN*TuiDynNnbmn?a_~1PPID47Ap+z>xhz4M`U2)kZR!<~)-{yFU zF~h-Dj=;>M@){$Qn&%4#`gzpY2~LmQ?Ro5|M=MYB>L za;EdG8DiR1=fpn}6i0%qJ*I_H?!lO5yDSqWFA^`Rd>X$%i$NR0apm3 zTHE2MOzd6(*!b0$zanDZF$LVWRptfU83v=$-dC`vmk=`=+c>Maz zvdlrW8wM&S;_f~XSd*T>s=0!V8=4N$7f{7p3d)YUrQ`@?le0p}61By%y59HSCs#}r zCvd-O_PE~9efx$_c;Nii@FTt|x|3n4MhMl;i||)zu}|4$m>J8|V1U;fqtcLS6?fPt@fa&Fn733on%87qO-H_=7pBoK-S09gYby$x5n)CqLR8@lRz&5&u=8kqE zBa9*32B?QYyF~H2yF~eScS$nu4J}bS0)Dnay_60z4~jPq8`H`~tY|q&H*q=T34@pH?`ccXky&Qb@jVbNX*2`}yyK zdvBvxJlFN>!&f9yS3LW7UI)Lns@L3<=tg_K?!e4kmcndkd#Vm>nl`9+q0Db~=o4YIGtaTIh9-cu#vq z?QCOCNA>AMZ6U(cQOC>H7o=J>A>()WWdooZ0pRs;@OPn~NlhQR{Q<_(3W0ah-U`MYS~1{ljmlUGV<_3wTc=M4bNUVblJfdf5L~c=%t3iT^nD zQk7OlQAhc@o)s1@6(j;7vQn6XIoJ&^5W7PZjTuA)2qNN3X6!Msz-$@I!&~72L>tEW zN7=C1)-+H@RH~fLh@w6ME+3c^eO{)6fJ0FzBl%iq9=Lbf&Hugh`g=!_!R&>EbYobH z;@4!fnJK#KcKaCZ{T`O}sW_B={f`^r#1fx2eY z-y+1x@U+avFg|(A2hJpaY!0H6;RLiaqwicO_}Yk7ecJYRY&q$;gf23?2FCc{wv-Xrx-G^o_m?QVfp9C;kdsGFG%}?*nK>_B%DiEcyYE2iJn1Q7?FCAcgtdYOnr?u;HJ$!$h$N(VS3QZMZpMCat!}Sc zxS_ppj!l)7L+hT^O?fh7rXJwV09qyoGt2*HH#m$6IVUqsk02z%SGzYmb{@Y-ZsX<> z2O@5inND%IWimcvrc3W_E9$jZRzaYHmP#3t279It+tC6W1rlfVP_?-@)kH3|qVw|e;@o=@; zO$c1)>n)~{f_<9mPn||Vm71 zGa%+_3#P4Wa&o6JBx3KuukP~J!Au)^{&XB0tlLnxeoAaAHuK5nt6`C~(Gv)p`c6HD zAFLUnBs1Al)wphNnG|bj+sjv5sJCj3BVLN)r8;g0jG&+2E{@L;oKhB|Inf92cmwPl z?8y6W1>r6>M?Ji2aa#P<<^)`{9pe=P&95)ar*Yys+%R5=Ahr;o0x4x0g~7kV#9}@5 z)G;bS%cKZ;2VSrUda`dn%Yq#D9XM)@QHyXysU`G4^<3&FDh*jJE?uWKmq0avMjO#} zRV=n1#A_ozhJi@snWZ`*Fw497tXA{vEGk3z#;OFDz(Ekvem%l2^WeuU20eu3)T|kGjG>jbNPCB*lg?MPxVvmS z(_jMey0`@%iPwRyok9-z)3CL5FkU3qHmW6ghlq{*vhFHseL`I;fsfx0J(odu|EuQw z=bQp>>qioM`(Grn|7UEM>c9H{(=v6k|M6&>DZ4s2*gLuW@0Vt=I9S~lQ${wqU;G~Ud%t_#_5bZhlq&oI zdkw=k)G6M#LS1=vq^)^ye)Hm|sxL&ab_fc^2HPh?Ww(CK!QUBy|Iqo)U+oedkh^oL zjNJH03&-6*as%!a9?HhLM!M}(y}Uob`;ZujW8JLW=Y#d7x_&Vx!&A7+LwJl24I65W zLn(lYiv(<8{Tpf%m)NK@%8eZP*!0CLc8J_4rU3NW#iN(&ww* z!-VBuxUCA!jS4wo;i=t2$I_d1>*iW( z6F~(b)?w6LzivXa^waWPLdTm^uC9>0Jhd&NV=vQ;4fTPv9v@x207ceu>lAT|guWSy zi;8ZX-j@Ptqsd}Sri2RxRft~1l;($KR; zc~h>4{nRWYh344YoVtvo;WXSKd03V=Wo{)BWkU#!GEGG>Jr3PjCn~1gg)C*9#E1yw zTH)|dqGUI53J)jV%7P_3MV!_;-<(m#W`j|cL56fzc`Lc4WOcJRBlNuuTZvb?M83K2 zSGqHkB8K$^ zYT%NIiC_ppT;0|#)nuto)<~fQa)XIcRd1`NTLxzl@q&?w8uo^Y9Bc}MCX&4(^>2W@yFX6M$bBJ5czc?A$urpNMEA=Dn?|ei<9-_pKLaB%3bcne;O^Biwlzz)VXi}pNF`6|E6}WvQ8~A-- z7VulwfOsbB;AyP1X|#qf^!Jt2eLK)0&c>$r1#TB*jJtx!IFU@gp9wsjKjbD_+b&!T z8bxPX4Z%ppe{?zk_z#gE-aP~GjbQVr=mYrfp zFXim$9r5tmY@p-WvDGfOk}KI3wMEJH5GTx@mDaHR5K{zE4oefqu}zMpN@o{4qCkwj zRETmM?}^x0E~iB@H?ghWoMf~blW+)@FFO>zncTLoNB~u6TJ2nyb*vNJdG27ULc}nW zo0#f^EQ^+D*L7(E=oHXvjT&8-8>b;O$;N3E@NFQg>$tIzQ42u;dn~pKlVzRc)wYCkwoCoR;Pmul1kobB3uS&re zs8p|KAy}(P0I0b~ny23b+NW8UI}sHFg@hoLm6hf+5T0O+pN=wTL&pXU-VMP~-V~}M zBr*lMNA&?taV32F7#<3S6=PVR(g$euuXRrMg|_ff1OVH~+3 zYFZL+4s2{vZ|XQ8=T+GDTgn$b*ST;FxV8cB2U7LVD-*x?7X`Y8hLP{LKfZ_ z8(a|v1e3215>0FMBe?;Q>t!+(O$TsKMq=-c#625ow&}g%YRc2NfmmbiaQcfsOZOF3 zh0_TUi{QO+Xx>7^zEi#SyFSs+#5}+VZnW zi$8Q@VS$?^*A z*b`9+UCj*F5;umVFENBYI3?QBNpj>3;Kvr_;pK#x#P%>BSEn>}IIYq$4^U^H47Hn) z#lNNuexz?uX|PqzY9D$d9dPN7tJkA9=?geI%p$~E?u2dl-a3+E-Fw2d2zsSC-f9L} z!EWj$LqF<3>(VWpEcB$K-^1n?%ArD0Uo^+7Dd56d=7RcoJQr2EjuH$9%1=XAcl^q< zCbVZM4s8aUt}FV0mT1j1xB@^6g#xd)02piZ5(@cn?cG0&mnSQTemJ`Pqq64JpTW$F z7y3opJrf^wCmbpsotGeH^gbxLQhrzX zZo>Lshcq88;#morSA+;Xq7PIe63q}--UtP2F_c(EpkigzzYetf_&TX@T2hs=69p;k zg5*{GQ&>Fw`%RX6F!}mZK=Ym2dteV)wLgH}T{r4tbg@4E8FQe#j92)b-Fz0%d zqd(oCB{Y-YDb3zp;k_}Z16SQ}mxt)?_0PK#cwJF1T^K`M=PSHSsbh>#C6|5ZOo32+ z^<1?=-7Ai$h1IdtM9#6ev>}+Y*da-=98gmaVum5eAxSwD)h|?v?Qy%0j8M+UmfVDJ2P;1bt6OLPAZani;;GidzB#{pMeF!wzX^-nmiDJ|!@foMME3H@ zzBj!eVtU=;&`ZuO<)Ihr8)~siYWX4V{oFz){aifOcf11B-WrL8XD)UJc+=4Bo4yD0 zJVmIgQ{eZXS|SUuY3PAnq%Yxb#0RZe0p9Pp@YO2hZH$O@_U&8fhXynl zYpA@u(7eo60W3&^5VYe_259LT2??}>G?+yg%=xDEI5IZdFf;Ru^Sr;#1qbD_N1Tkv z8M;xd& zxGRrXGy15Gtf_R@3gl?c*K{-%2w1*iiBQq{%Ssvok}mKoyD3h=F?84a8sn$U&U4SSz{-@4%Aeg&uYt2 zlB+u#0VP&%mHsaHqhV)f$Mc1aT2z6pY{lQ3OJTJPh$?X%%>iqA4z>qUfLJ?f zy26D>vjlc?+#StPL_TCMfL;ZmLU;#Mq#A))P0pK8qD_!7oIWsw1E}y4R8h?!Cv7Re zNaH}}TLyi`F%Fsc#CrxNL5w&AV*#VfhvQ!uMx-{%B%8#p&1Dg2 zsTLVF2%E+t+>{HQyd7Z8)gszsu#(#WX4EM%0N0j>aJELIS;jk)W0}<|Od9~rO(Nb# z$ka={DL`vY_a?Z{`ol3!c+z!*$#8pu95vg5A}|Y=;ueyv(a*lYJLmBEay5i*;CHB- z^JAPjXrUzK4S}sm3A5u=ERYUa^YBL~Ia`YCj^TAu`;vMoV%dV$C4k-xfpFPFemo(< z7$?^lEwjpX0)L=k_C%$03XhT&zsc7gU~+gv1P|v8_68XFVU3?y!c!brr+1e4o|qEF zETVOzw$rWlvko6@$JKp0awl>h^%4IdJeMu$h>V2Y`15rzJEMQsPf+=8-MsmS@AW?% z2F!jC$?F{tX_?XK8=VmadUh@PM>>bPVdcN6OX3;hJ0YBjMHqzyovNNNI z+k6AIF!_>%UYX@xu#;N`3Bpb8Z6d4mld}`uc#~gv2cbbrGAvnUcgYWKR$@1h5N)-# zpb77ntF<=ib=7c3M2<%dVNs1A!Pq;TQ>qqG(H%=reV)&rhMYapSxx9+^c&dN2U=oT z1)h$a-_X%;9$Wpz6_|EmFD-I$XIXX)br{i&{e1UIKIT13O1ua2LXDQ7b3j69?ok#27lKunv zKD!4*A8FZM(eX|C*)!#rx7B+9<2$kN9bV20GxQz32cY}A*IrCdb(s12Do29+L6eMA zBp~uK6ygUFYi@FjQ{ZD(YQTFo%<97z@L#+7b{yj+$9J@Y9sYm4tJ^#MuXgqSF=FyP zhKT8RW~+^hlBu1Esnfp_E@Bj?RlDpwQb9NIU8Q-H5t zVRIuU%m-EEPcAnSo&&0U=n|n~J`jmCOjlN8dEAB5l6>WU4+g*}VN4nzN(V;({vv}4 zUh%&a9si9@Pz{51iQ<2%6!5Eh0xb$S?S@g!(&`E2(-kFtzl|gv!07!BFIyAmiW$k+ z=mPfmSkAaw<$rkog*8V-o`t10w)HF*LD!VNC%VFg1dS;eN1xz|it& z3drd1=kNLc{`)ulANjz(-|%1WCTr;MPfvA>y0`Z?$Aq6e(}W#aPy}?u4|Fsn!wsEQ zv0(@ZQbZIPG*T2Sij|3L6L*Kd8k!;A+Fh-51-gscvg&Q}32q@sTfc=(K0IPSB0p-E z-*7T#*sxKP$p4<^H1|9G=;^!I__%TZdVz{L8zSer4#OM}0l~!8)Zl6(cO^-PBB&z{ z)PZIjjNMO%!|kBJi=Puh87rcgtSBl91}}-n7S%jmT2dT{fWV79EcWq~8nfitjugMO zK#7~k87m1SFTPBwIU811nyWj$x0F0xL%L*l`mO!v(B0|97sP+QL9o$_EYKVjD=v#P zAU47HCnU4#-KEekx~A;s>4?A<)qGlO+02x{wmm8{+BGN8PH`OeG$v^?GW!xJIlHBX zyui>ZHDQz&L{66r+5h_poYb=qcu4!I$L$Xvoh1#rhu$38gq{r ztWwLHR;JBPHDk!o@F~+SZ>Kd%RGf6|tx^9l&b35!aB(O8W?JJKo~fh=7t@U$#RpG?)FcZ*5`Dn_Of zB%Ey4Hw|JZvn<)gb&M^yOA?mHFuKJQl3)MGSaxVuz0+Eq{=2gBEDZpW$9&%t2Q6Hm zQWw-N9bJ-JD!leSL}$&B*khnS%44D*KreM@259P{84sBE4E3zK#j>1a1FLQfc*nm9RePg^-=|D!(-v@(5>MzLv)UfmzqyqHStp0gEO!VwwrihkJSM# z%;M&qQ`3#lU;mXC_?!0{*xj%$fZr%B`(r{ti;h2;{#5{)e=zx*g2W6M{H{SjowqT{ z$$bXZi`coaRb|89+DVXr>Ac`-m>$1j+ffc8lefNsR~5$G5=Jv)y(qSj2FcTU#h9FoI}W|r{W8) zszGj-%F3YYiXmsm3jNlkP}ru4#1dyuXqlC4ZK!OJgEPHJ*#Yf38`Tn_`}^i0i^_Zv z%z1&0dyyV9~Pt7@~Jk7)N)wMPf`l#y%TK1g+t7g zwu0On1jH`z-TEZ@Bl3u>Hk5+?#YSFF!1h+g8GW zSv}Mrtj#EX4sAP>mM=G4xiiAsoKA>b#dKdhSieNF}Y7Q_AWysT#Wx)J3a>^ZnW? zg?7ozVduj!M*#T20@GfrpBM0>H2Vjmh_%}iUec6;tN zgD!OIVj0$+j{L?JXVHvLoA93(n7JzzKCU5U?h0FoH%1xX#VvOO_5BZTROI}kQ>4sc zm&D|{ya8BVeIr@mQfnawDN;+SKJkUKM|d#8_@Cau4pH!tw43gq&KB2-6V$|S&W`xn(nE5++`l56^ch5>>qTR@WC+P z42Aby0Y?-kq+c%MPj!M~a8j|n&>T)HIdLh{Tu-FDd};MB!`E9|mI!8AdS-(5De{{{`HSU8y)nut5uyE^=9+x)LR zQ*}ofSsmp|x6v@IKJs4nT>KplNunxzG7T+UK#~Xx3bpb#na&`S344QHNmKvJ5iMj)9-d-P2Bwnz+u0 zJ|ixA;!@&T`Hs_#wDv*-C>PpD^QGCWr1Oc5HYt?o&Xc;y9khwfrP9*i;QCD4}e~u~{114k{@Jz^&^Y)b^_0UrU%s;F^ zyfhidBL}F$RJGAo+9P1>GBx^X14=!bDcq%MMHjcm8?6!q_E_&&8-d1lXRM7Nr?`3< z9l@!AW)>`16r$@AwYP_}cB8inaFa`ozs=yoj0UA-8Ip_Fe^M zJxR8u%9q?YDRe3k*W@yG6^jCPTT#hK?2^maj#<_r^W@Lc;ovc_M_@x9Y`cZA_SUK_ zG=7s%$2gz#j>pPHmVnE_J8w~{rR~NTD z83QxPM|2N#GcyQI;t*BYXPE^79EN{9W=XW^`y$2i8_APL>}-Ok-T4B7&G4{p4;(#t z5diJ%vt-qKkVO>Qhxc)*N}wgBeU0u@ga9y*A1i2MBxPcxdAPkPJqGYPMT*ry=y zD4_bgj15hMhs3%r|Hw5Jf9P6Kdj#sWbV?-W9NZ!ODJINjQ2G1aj zX+{|hC6C~y{W*>4_hn1Fcbc>5v8x%X|yTh0#e z#`leP(-9CK+z}5MiVRf-6f#5Mj%;g6*J212Rb5r4DqWFrsy{o@+Gg)Cxj53(t+pvi zDZz}q$SB~7D9FA_!-#xJPHqI&&8||z>@7A@+eRIwyXw#l!M%Rc)4JM1cEB6T1@rzp%i4(SeP9YXvs_v>ajSwa(bEiVsZBT!c=IGlDRI;{#WaOA219@mYA#oh3V!< zyWRO8Sxow@kp`vh=A#lR+T7HZOuc03My>ePW@I3%fk&PI=LQwYy~QKb9<6Bl>NJyG zNthM^%`vu0(MrVT6-f5_tH#-~rlu`VOU_Wu5{LX&y}eszE$O;dcD8Lr`dgHvEMny$1Zw8znYOSy?m35&~64+v7TOG&JTQUM*+MmRpV059dkDB1*$#E7l8`n$FRc zQOH*L!|yjsrCKg-TTC|mN@5|zssZt6wia#?GGxCN3!_>MH$$6*<3nI)Kl*nJB++>O zGzt)6v%+zbqzJ2dlA9yxh)C+bfiNxi4_T`grssjR!QMvi4B^2Sy^z{I}lDu)Tz@GuDe%#$} zVDlN?h*ww5$-#n!cNhnYOcF<6fm3CgxgDe4Zg&#iY1cQ4+fo|;Gk<}A4FQWeAPHQn zn{q0(+4?S7v)DfX&T@{j;_yk4Nb#0p~%N_V*PV>;9@fR`mGKlmf3 z&~B9p=tk^87y|ruy;z=LW4|ScL#Q9lkNC7;#o-C4?%*_DpgU~d6d^dLLlXed}BO?HypcJp*H3HR!5rcX&kF_vAJxs{?|%W`{KE zaAJ~u!W}-R=mb0=FW=(01O~z}Uy?(0-YepdXddBlNPJ9#V@)A2-($|aKc4uW1Ll0^ z#ENl$@Pv*6ZYu)%iL+PT9Dja})6MRd0gK&|f0lMV*FGw11*k#kTEOd?1k*Xj*E$Az zF~l%VAGj7fB1W|bn?@U>LV976pX+87svk4)4WRmnW#k!y=_~zKZx02RI7Iw;hpglw zv%+r-*oQDwr!jalzW7;HywCRALEOba7Nnr-3a2RbDj`wb;s&7NPgMMxggJ|_+M^~w?JK8neQAmw(q7j|9f@kZ0PYH)!p9#5yQVxn}3ZX z=;GpJY2@l+>ijp2r=qRAB!KeSnpPZ41p%UT9`i)cAYk!PI2yWcJcf9y#nzkdKBm5Gh#Yo zLWa;Z=)J_l7>ZG3Dwu`1NJ}^iWugN5;tncn=8>Q{NeIOS~*lga$5_&FSr|(~7U22!oJoC;+-=1+sxx^NtO&Vzk=4<^p zrq!!wf^o&sq(hI!>rx8cCQ+`)I7cFA*Ec~MYOvs7MThG*vVQ~%_Jp#3Bo?5qJJbr2 zNh@_To*=lFw12wB7*^pVS;g;3(MK5jyS5jT{Mi8WM~o(YmE3D@<hb0zK4(#RS zd7fVg#Q8?39wFAsxYpXwI8n7v!1}5zS0IQEx2XvRlyfN8$d;T&<342P=&G}#tpNwX z{I@}oB@0|4+gvv>Zw5D*-wV!F%~Z|6Ne~KW43Y<{gwY1cZlO`_%RmN%2h4DDyB-amwD2Fv`YE zfS65IEOJDloLMX=c?!&pkS)8(tnr|OlL?Dys92&34{3uLM3*?kAdm`r`b~8TzF-Wq zZpkWsIr5OYjYbqf3S|wl9+nC1xo+RCJ8#-RT4J&D782tPE$sazWA-V&r@h~9!_#j$ z`}Z>T9YFrSW5f=IE-t1{cK=bx{*EbF|I?xVzX!YMKHrGf#dtE zVEQ}`@SOqAHaD*$I*yX#XFH0GeTnh9Hx=VsHgIKZdtY~)X1{UgP0;)Me&OIXA0Z>w zoWU>N%OTK8K(-SNM)@J$5Y40oAb>fl_9OK5k{P7mV9w$|9`Nd%PQ4KYwi6n#{Kz62 zY$qiO-U8x7Ks+ebatKp)|Kk)lMWtgPBcRsLA>3ZR-fD%JCS$DB5>CaK-IKyr(_M{{ zVk4O1%IxnXtz`jFFq{caRgwRD0>9S?!Si&Dm~yP<6nNRo;CS#@$}kPk=I!W9clbuw1=F zIaa-ERl+IOC<{ktzCjNWQ3o}Yxj~CxTGUpzwCga-^GNv*?ofd(Mt*tf=p<(-FKepT zWJfkKkN<21MCkPf+3 z$>o?ejCk9I0Md7=e=rdrl0YZvqf>KhFq0Q%tUavY5o)2mPY&Sr$q*75*#b&t8%w7BHlSKpyiV{Kcz%HY^88X8l?u51G7zx31Rc9&9(*W|8tGVYN zwrWI;cRjyE?k*}5+Ms7aUY_OltR!y3TZWai^^ZV4Xj*|DsddJ_@5i;vJ1aLM+l3yj z&R-)A@Eg|WaSHN1K?6v-UUZq1C+fDkpSu%e??)GWqny2VBkwQWP=8DzqCYTzq4)?o z!5X~5XoQ|}Z)fd61xtH+8zJRin7=UbhJG%~8G#lClv(t@_PQk$|Q3!uJFK->mr|uB*Srlyqp!q6ZXbi#Th-ZYM zZvdiy1eQC0HP;@Z?+D&^K<78cJA4+hWuhAv$$)KqLm86HE-cm=!jU_Id4F64&L)-0e{((#O5!;5Is;~@ArPXp!jmA` z$~AJor~J)R(W!eAZUJH%Xt5LEkzc3=x++4*DK7^6czR9?C5^kl^15x#M{bFK)mYY* zLVol|okQqGMBe*PMN1HWXWCd#3-KYT*IsrQ(buWf@pB@0z9ep;CH5#v$|=hLg))s9 zE(iI94@Q?`Lig`Pxy8FY;p8d=Q*aFDay+0ZLOr26=sTFJ_t>(%2U3t5p6*=xDuDOd z6XH|G3ZgSCv-V4gr;mSx2R*VhBuGI40NTI1r~J1<|GOS$`v0wm6%3u6|D}Xe5*OqK z8BjvM;MPs#5K%?&0RTfrhCPA~gplgO2?ydmG{u;uezqnLI9nPC6-L1C3uA6Ebqt7H ziOzjG%k@3bUyYq!a5GsP4u?hvLzi?%n_`C|iK9`0{A7jqTqLAe@lx*0w-XTF!zBp} z^XuXx8FwpUB&K-u7{C4*M@)8oP7$B+p?ADsAOWh*C2N)hnTOcym2j(zrEEXAO>j?N zW+#&r95LU6=yTchDfLv5&bUBE;icGaU^H69KwANb9SY&pz$G+ay~1uTqk$kDo<-D| znQ;dAn;;W9ZpZ1Q@jfUb73*jJAX>m1`?Ga#kTD9x?AOB*$jaWkKf4ws82b1Gw8|8K zqS{GrS|xD{VbKPMb|BA1C#}1-dDZ7P?o@lv;6@in^{b0CzPpyU?C~JI9AM@Bj@i*w#1YnS9UJr z-?qd*?|f5hx`Ec5yU_F8J;z(u^SS>s#oD0|_(u9{Fpc*m5(GS#Mx4C&*T5P{@&N;Q ztcSyLR2hlKT{HR^$1luYpB(zRR9?u7IN`nhsXHd;=TE%(!#8+WD~g-Ur^(6oQ+O<4ki|q z2)oNYE+z!h-{B9dHQMZ$+rS9}<8Xs7p>P!7DYM0eMPtB;bagqlX# zH4>u7Rbeg?R3ur_7Nw)GI#J{(!dr>abqPx+q>sszbZ|BrQ;uh7DRBq{Ub|-2W?W$Y z3M%P|YQ8pdc*15(tixG!aV1IHtTS_?cuS&!uVs|`qeNj1vL%tmSzwB}85%89s4VGx z%AF);v=T7e93H&8G3VIe$rx`08eTwFd){y$DrgCBrNsX={|tgY`s5Ztb?=%~N@lzj zT|Mf&y5;q@+-XGQUVHheldVOlMO&7hX6qT%dMGfxI5 zZbLbibLVe+qw$RL)e>k}ZorhXOW$S|h_H6~3Wew1$G*EYx65JFly zH!)Aw(wNKl`Wf`1M#=J0)#E1hTKHD9JM^XSp4j!U6iM0?>4?Ts%#sn72ZW%$;I3{u zUN`Jpq(vspq|oH=|2f0aHEHy1tgoK;`qauKdVh>XL~1NonSOj7X0gxZbA;1UYZM_A z^Ex*vmL$naNb0VO0%xAbIm$a?OCT`>!rH(qYl%A>PK*ar`=|Av6E($ z%^5BiB}+??i6a2Esm*{DX4cC+!yprw;L^gz$3B(TS}kF9;2N)o@Th1v1BbOvH%ocpn(FqYAmCs(d=~AP(s|FJFPltIU)gKWqq$F9nRR2IGWv<+UlPRx>-2 zCGOFT*)&xRWGc&zq=qeZ^upL$P4QBtToGgUi`JKSX7UmOMq|#ii8*UqTI76A%Hx(g z9kkf1Cj;+ehyr=rXbpja0#%~aYP5z#b#aqh{XlQ9+}1sV z2~`jGH;B|-TOMO!R2-<5838!fSp&)N;nYwo1xUjn9FO2tJDP$g2%UL_81i}? z@dhCI0@%6RXA;mG4$u=2-Fwc?eiY6Z9@ZmC8}bUYDN&IIVV-rdhNRlr;RyXj!L-8B zmfB%I__+F~xl*j{l)}8$P1OslOs=R%E%bn2R_e+%(!8rzwQkvGzhB;Y_4xU7!*s6R z;}KeTcj5g0Xr=8rs}w*SE3PTyQOp@@a(~PgOfz0xroJPE=?y2i^(BiFDA2V!&wHZy zKBdjiD<8OnQ)Qye|wdO%Ja|ADrES2%RoOR{`a`Y4R;QRvzpp1l9K2S6vYQ{STYTsf zHMx&_*caHt0zmEno5ZpXcf^%D^2QOecS!#N%r}UCr(XC4?2ptTk@R!O113%>K@~wA zq^p9)^s)#iHo+`H9M4;1;%-T=kZU+1tt!cc4=LmqQJr7dTzqhr&l@YBG&#JdM-I!= zd>=M3hqRiPNC(xrecDA4$!Vc3T+7LV5TXv_reA}DyQIb|_LV<+Zz#>K(9SqU8JfW% z3h^B7VKI@MpCdJ7S0KxMg!HZjTu;oe3T2H=L2z|E_c%A5;w7aqPM$$azE@h#N|~Gi zFhIGSQ7y-OrlWPYSsyIF0OdGGQXFAWM5|J=jJlPR=_BN3Z;G+!^=hWD3X}VnV~YUo z@z8ebCQv?No;yhUxk9GqEhoX2+hal7@{L5hLn-PH+=F4WZmbm9+qsyDbqxzt4Dk7ukH7y35VC9{+#BD z#CjGZ1Y|Ft9wY)}w}27(?)wwhoU={8*+j9ude&9fOO;yeHc7m`dgc{9q|_)SHCH%E zlMFi-gNZMXqa9UJ4Nk2=uh!@)nj0)SAei0mOC~4DD1`AfzLgt|#7-Z}g-2W2yt*aJ zI<-yau|Yt!VYb{D&Y*4`flj9f9>f)f7yFKAh`k{r)~=k$Ak>1B`3*gu7d0J_9f-R`Dz`MG@k}GN? zJiZt|^273}J-0d?TY|353l2NKWd?SoQFi$N!+PLt_Ac-2CFr${cA7Trd({8Sd2!=G z$+PwC9Q=XspRg{+f5c5ThR)7%hPMCC?U1Z3k1U8X_O+C1*QJXBO+-O4K5_-BmJg*w z(`K<)Oh|?heP^jvI+bYJx0!3@LsHv*%fx&(V`-1md6xg} zW9+mp+z7G@a+Wg^yuS9a)JF9KppS?wwwajSQT(Y4; zBM-Jw>>gJt8nx!=v(;emKnK#+dnYqL0r}DExMF?fTZ84hh=P8y0*xhsU|cD~3QI-0 zn{L3gS1Z;!?InKEJ9l6IIpr~+$mscDHq=0sYm59PDm|XRp5%5@s7D_AYb{=Z5XRYm zz;GVezHI^F{&?It9b?!OL(SN!T^n%`P7YHegYb~6Pg41)GR0K$StV2WX0R?IEjDxE zBOpdY?GGgvOu8bX$+D1eqM-qXHB8mluHn-hHWn0E?tVq6D=@Z)Slc>VeMDg>E%2No zQ+*6>Xi31kpGBx_lR8*WF5o@h&7PA|@RqIX^0Y#e`4@+*(FV~=%bk>Bv#AEd+j!$# z`XSFUkZ|u@3RBPuV|lwZN08u%?o62B^yLX$D< zsbz0x`gg0MM#b75Nf3n>xVibFRVBFBN3}-E^8B@2ez`c1gmsvO^6(-^Zc3nS<2rt) z;LhB#aOGtHe?N|C7c*W73SZ`UXnOl#*ZG>$-QEA=^A1_@_;!8}BXtx}e07jX%_V(l zs!}hT!*0|u%8&{rV5+AQLP-6Uo;?!gl5`H z9*=)N770H^`EvIZQw+bOwp89v_O;rB=9Ro`8?`oQ$(4j0Jb9Ihfo9Zi0+1g|X zpQRDAHyI*$5rMLifO0K~o}jN<;^GpWIf3J?hOv*(rV^kvdmh`B3!?F72)d5JD%;6< z)ni*fOAXM7rP@V?SI%*J(zPtvgEgu|LpP2=k)E`uX#7pEA&De#!Uf*h?EZ@@KV|bL z_r0wx3kvOlSj=@uoKOqp(6KiTIEl?x8Q+POha7B{G*w+2W2iP;86w27%oy%=>m$7D zt@NSp80mJLG2qZae+&*`tNA4ED!E9>K`Mb2qldbXI(TC?0tPw545l22Rys3wiTCf_vNr;o(P1Pg3{r z1bJPLDS@7{kD!3r)~L>T%j^-(z~!=sc7w*Hb_t{wkW8nrTB!y*mKAyT@CCnCu&b_6 z+XCGsQ|SXJG+1pYb;p@?ryUh@2IF6tn%+S$^`GCX9UP*VL0K#8d-W^*$JNjLzg_+R z^f{-fc{(GX;`*&}sp+A?42mTPheI?Yauo(6q(KQ8*&vJ1W@!X2ALm{#Q5FP9jZQ@1UX6{Rm@236*}5t}Y!{H9N=N)g?eCJHgzu80M@XHo zPmncQ^>G@dyLB6{d{sKjHjq}6EKg4Qf>kPyk-@RMJsAfB!W#iaZ=vFgJ(`f81C&?o zsT3Awd?@YW%fa#yYmHdMS0j|TQ^+zd=41xCUy5@nH2&Z!MM-v(}=*S(^&?vmXD- ztT{-u)hGq&0F#cF*x587Ql&WateMxIRgdblgrAPSh=s|qYLaK-jp|n2q9AD$Q$!yj zoUxV1sunGDO(yCMiZWL_Y}fl45!iTAUUvCPmawR&n8g^mQkX=NIy;W}8?rOU2>-iV zb%la`6l~OCE1xlWDv5JSg?w4|W*qbSXDMcnVP6k2Ym@gNwJ9}eP{#Oah1WhuQ@eG@ zNY6?HytlL-?B%t(-}Z z=&5r1laPSOU(n^tjyiNKzCKMF(yLXBGGxR8Z|-fHBvX~PjiNJnB8!*KbOCv$9 zO@MdffrXs9qT5uuW876Phq`pCq`4&w!bXf0M!Y$w4{}q{uue@oNFm$8)ZzReK)c*H zQzzJeR1xs~$zSiDVybxfg-tYXZ9M%ChX5OOdvnYU|@4tY#l2!dZ>2d!T$; zT{qrlZ>9L0wmXTElDrCv)2NA(-cP4xtPe}Wb$DGjCK_33?Xbz^z9zK;qtXZT0|sk| zHT}9=*tRa0)h(CNA=r`8X1@Vd9hl&SouD-2;ZJKj6UZwzSyP)Djl&ujfx(>vU|MyP z3$BTlWsWDl{($vydqM#*b~|wl91$T~I2_9#zQIMX4%`PCz|{z`?EQ9w?kxS^Oi07` zmxRNNBqw$PuL1Qkm+pvt$M#ua)UUig3sE;iH4(+e1SQP{@$=E5MD;Y7dl^8j?$8IKx#3grv+rZchDL;N4cUL=eh!8XFRK~Ycm7* zNA_?RLA%grDARs*L$c&{v3f54fwkq{Z=f!uJ%aM{++nay8cjd?E#{fyvJ!L0fdYBl zJ5CoqIPLhx(%}S8)6TI*+k+Hk8KL_b&FUgu(+9w^Nq$0_y;_=GU_S0^UjEF*?WcT) zD@z!~jNT`;@;3UG~L$VNB15|j_#+;e^r-PS`W%Cd8ZT>P2zSp%r&3q4S3oN{>)ft_g+*=A9J30 z2{GA@-&1{0sIeZ!r`CY`5j|8vQPS{qXs))3O`FUsYTpA6c>NjSb{QS?8Dn1tw7sRodo1bD%QK+qLxf{G`2*VyQ~9Uszu2)J<9t4c^Zm`{`;uK=u=rOiJu z+TStUf9+z4b5cVB2%~+h4VK0=D^?%MqH(qaVij$X5U7F@s^t)emRWR+q~C3`sCpyi z3&=(1i@@=vvdhDzyd2PfXKmzsFg9`5*~iVz8yHWBfGvTUHBw}yk7NgU0z<=O%m{#4 zMG01|#$rC2Eu+F9+YyX>j(Ca>i+3Y0&L)4eV)6WRjDOM3>g0k3gK=e#^bLFNa3{-V zxM%m0%cCYWI==)fSU-@$@q3+-(2Gp31{+F8QTKMrZ-0bn(dBjZeoy2kOzT}zb0qi> zv*q9bwGi<|6-h**Dyheqc5^}WCX$RWwA3)Gnwa5%F~N-Vrv&LJHEh#zYqQQz6bFog zx%jI6LVwF&Le4d+4>MXs9(>=4M`*!=iTdO{5)$h)o8JQ6o2`LoS#zjet zTYuJl#v3K0DWj;5$)(C5s`|zgrb^aGJiJq34cY2@c&;N0z|K(4}uyepqx*ep!88eAOuA^jU`e7X5hhsa(R2G+KsdF zp-~uBoaHhP|F^=xukFAAJoAT|FU()A$3~fdWp{S?is6^K+v@l5PX~rVaRO6VE|HYT zBq`F1^)&}0B=$<9G$8X9%x&8&%PW{^7G9>RJ-W2M^xS!y`7R>3qlL;Z*=ftoyvix{S3F+v2a?b--q5$sM5+RxBzI*XZN}Fz00Ay)VWMVsQi0gsqr_4I2JY$H&WTyT zdh@|mvmvR=YO;G`IxXY!WZpTq$B&IXbhxdUL6y!)Bgu5~_h7ez79d6Y62Augv&#^J zg1K}fKKs*_bB>)BUjpO0xqyq7Sz9H%c`sFp&!DZZ8}hoHs1Uf0_&kX5-B)oPmNln( zAH4U31qK{Pr8pSVeaWq1SP4Ow;gGClmij<>UZbxWA>Y{!F(CD|hI(f%hkA^-ry0!* z@Zh+zG}Zfv^~nuH1U9QWYXTijG8#&LpXsiy#f3~HlWmm39p8B93TPBXOzB|#->HlW` zE1;jAy7bK7gW2ehQ2RfMM*qJ=^*1%;Y+-9AVQXsl&-A3UY>TaerZdRAjU7MWa#T0R z2v1wka6AVd)=Uz};VKO;8@^MhfW;qiTFpFdjPs6V!7WLZNuFTZR1>dyz-&UPr^668 zv^Fxy$JWSQ-_2pVN4dOyINjNdXRW_J?W$3v^wcvDHR1F?uNe<9w)J1Q0?#*eK$sv6 z(EWp9RK-2!9@r4I+tZ70%0bWv*iWxVgO5FiDqW4Dy5F1BN9ffm;qs|$8WUL?BA!}O z>z6A=v47F_UP~_yCoP?kr6$c%eHp)t;5VOjq$-@fT7VEv^)d1~2)3?0+_fp&om!PC zWw+u?Viyy=GHuRiA^eW91h1rDXUMrXe`Y%F{Nvr1B*nZyhy8sxU{%dWpklXr1RbbV zsK$~mx_AdpNA@PyNUU1KbTVuWO)-piTcFfa5WpBhgE$nR@mnTUN}ouh(i+$pQEtOg zVLk6e`7GG@i)9rvOI-a^ccIBrq)0GqU_-NbVPkL&oAmOHG^G`%OCTEV zEVg70+3p#GiaPrWH~LK6?k6YT(ZduPJ0cBTYMj_}z&gGH^6X8r@CS1ioI}9{i-o8O ziZaT{_<(qlLgo+Mf=IG4X1(u39O@xC(%}Q0_hG0G#bF=aTyWoW48glp>b-rXAqXUj zns&p?o4i7)K5Tc-h*kNu#U-e@;wPuM5+GO|@9rV*A=vS8PHf6IS7}JncbE=2tG~H> zlqPe`@-Tbz*`c$fy=b%s#^=2IJB**2{cDW=?&F3WBE0YIl>Y6b{`J%bocI|;I8C+h z)|rXt#UQp5jO^8iYUvCZS1cZIRoAL+_7R^s_YQo9@kLHnRI|HlJby;kqJ9?B^KEys zLJ=}A=+*f?*|&RK>B}-<;-Y~$@r#eSsSm_~UhW3Fl~4P!Z-|FfjP+1HyBl6f=4#*A zPJ7da17;79GAjdQXH%y(R~`NL53;cKK{aTH z&xJclJL0V@8dU9k?-zNAVD1{X@tFExEMzuOZc)E$Yc%5ul=AV&zoIrfba|6JNNcGO z#gCRz1q^JMK{J4363s+2TmmFgd4uM7u z4H+?TyNIbpQBgO2W~-Cbl3=Ppq#)+ZzMqKCL)?BVA1de`^QowD@b$nmUi_b;w-A#|SeVE2)ZRI~0VK#YCegPUh!**tYY|LuQUKsBzO;kHN+YKYpd^ zjj0{ecg{Ebl4~%AVbs7N`mHuBLb4>J`)=HNMRjk>&3|Fwf;--hnWm3Lhs!EaoF-@u z{M%+BQVb0Y45Q1H^i+lDBvc-=(nO8*7I0Xog~UF!gDR6h^utJ#VA2AjFq{9dK8hL? zfeKeY-yv##H2LGm**D$X3fY!MJe=l;K&$$cVs_o?0aEY%mXAJtR zhP*%>v&eqG?f$B6Mg*iB#_!e5^pDkz?N4w`!q(Zu%*64(AT_0TB0&M^6+!3cscUm$ zcyTd>WMf^`Hwt|?$V(LQ05TTf_G|;r2%CvqZN`?}rd|k{J`3a1Q8d%UFS_KPSKsNq zO!vmTugBTFd|zIla2cfy`n#I^p2=D+38KKsob#oBil?Uti;67-`cb0J+B=V>Bruj4 zMRlvMuTWLmR<`S>P=|cqYI1388_Y~A9#hL~p(aK2Hd{?@>j_|&ag%un<|d#XoJzd+ zmaAmwJGo&CXdrXt5VGsOel#`e>oa8HihMKt@ zMe{c4q*q=QS}bYy?f(_$x3A`S__kRX-f|NthYPvHk8-bre9{_cFw)`t(xSX;l4?ci zzVLIERGrmCI-%4uKZyuZDQw;|^ZAQNH-jTO^kEkLBGK*`?KJer?}8!{%yz>Jiy&cQ zkws$7Z?5l${7w z>B84|5#KH&nIU?|{N;r~Cy{K@3{uhM*dKb~>+xz>oGF?q;z;cL@aXcGc~gytqm=rh zh7Y?w_{ebiq48*72N-LN3j=}}901#z;7l?_Ewj7=&+fYG=mVzo^OnHw+mNyG;syb5 z!&13Da;|876K3)QF2C0#?``N|>piFl)V|Ix3iFsPiy};wLwdP^_Q^d z>|YeV|E_*#`BP*|o4A@-|L3?)RMz>6AbYocu$4mAMndnDM+?^v&n^~0vW&H)oL8MQ zpUaUxj&o_$C`2;qsr}mB_jN;R9voF7Vu1Z?aA&W(S*u29g@W$gL}v5-So8f#<~bjq zk0`Zl-uF#^P`JU30e7ITHmp$ZE<+^&vM4*5e^Y6O1iTVfrA5jDLmpwtrY905I%C-; zs3w$PuLqm~TR4qSxrsSbnn%J64iNEsBHyM$(so$HR#k+0>N-q7%IrGm(3+{AA*njk zF+ zxZ%lLf&-$%cqS^VI($WYCo0F=j+r1EvkZWnq%POm7gm=V#yeJi!HUP;tP-_fV5Gj= zcQKI)aT>-_tP9E_`_|WH{AIG)I3l_et6kBSSIUxZv`NUR zbsUiHH;;jqMx+OMUL-7Qsx=ltFRqXX7 z>@FBUY{44w(Jq}lOS|&{4(>1EWwIr9D||j((6w-FPtdSOb)w+fa#t1?Hj& zMtBd1h~7p!)OCyM_>Z5)+ew{oH?)>IcW3g=l1@xEjM;y|5R}J$w*4qc$T(bqv{vQq*NX2+-TQh?}v)Pp=5&zi64q=reSENNsMUUs$I=*X=-g? zmaV}%h)*mxXJASz$vfp!hJm_N-*=zHRJ*FO@FS-$accLL*qUu`o1jaH0d_4F82z#J zf>-wOe-v>BG zY#=hG>q!BI9cw&*n>-y@3E=uMr6Bz9-a`OH&Q2Eq@P&ClpA+?~U;8)NRE>JpU_Y-A zW#OGdskxuIDONl9hhADvu^BWHq=eKV7GvS!oBa-PO69UU*hhdm$3BNQLO zOHhl?Cl!Z5bo8-(SP;G<_mMFTKZ8XnB~HoahXqgB&XBky64&~9N)>xw1m>L#Eco}n z)FBp$e+_cP*>g#{r^!U@0XO{zeb>WL%xLglo3#Iky8f~&{2va5|L;uUuSixhu{Uru zaJKs|*sDOv>hBsQw7z^*MX}M^KxGqDNr40(sGe3TV@XQc4dhF)z)Z3knJ{gwxYUE; zeV>v+J>*5WHNc}SqvY!CO|&~rG&`MVKHfe*%RN+u%rl$?F;)unzCiEAMiE3rXx3oi z=@SNuL}qA1a|tymm)P!82NCtg*Ej<*m)kn%ZwgtGuv^A;)HsrK*=QBe+1#3_Gb?PX z2r8oN%^L@hEL}eWE*%5hn$~uba@2GN>j3X&Tj@!MwhRw5sko3|1idG@<{g_=iZ;q_TDyf)-zfN(Dk*74n0bU#sD8#z ze00NPn+cH6Y$!=CFhF|Wgh^^;`(E{HZ8?v-^x>O45@-&>8OWlxXQQRwx*TUTVwi%tX6Q-3l8)8e6BC%K5Z{gRA_d;=MA8j`2C9r5i3gw+TGEL->^B z<6TgqUd+`f@(9cdXC98|RsL7XXn5hT1i$Zqv6Ikvo+g<|nfa`{1+CA2?<oL&*D@^k9puXrn2*jyi!LcQU9FodnnqSPVP93<}nd|Ioi$g}3a3aze3UvmX zw;!G^2L`mHHBuTFgsQyJN5vlkYnIV$HXn#CGFCYh{Tc-h)|LIj8lj03&rbV&1P!$C zC40V?eR`~Q(T<9jr@`1Fb`gBoI2v*irZ^ItxvamhJ`5z<2rmHn5t8&fu{_sMAtLIGJR-Dj5BEz{8l=&a(-J5jq}Npj(?{Lb~8yNc-WW z;L$7U3TN59=N!e1k7M%kUZY-mcZKw7{(he#<4O`iZk+2J41IylQFj*yD1#2{Ly8dw zN&g01Q&KMOV&&B(mzq&%e?Q-dvLJs_StuuME2+CeoE&g!(~k-jjhh8L?&b|2OTT6a z;PsY4?OY1;IKchYbkf7sEsVc&1StOk&HsIZLjK3}XJ_`e#p*wXsTJxl?#hcUuaoAY zb}XJK;)d8H{3nJaF*X+i?>`L!N&RAxFgRiaJmVlmnGN?~2wOaV?E&apG!htUowO7 zAv(N$CTt7UD>bqUx}f&M3Y5CMOhN;0k$;5&Dym0dF$$t25QR|@-nMX%6eierS)qwZ z3NZ_BRk0M&dmuHCdbl*qhjPCw2OC$@ zC@3c>@@o`=b}iL&K#7(Fc~}|+rm-<|l9`&7s)`i~EXM(k0iJGoOC1uJ&#VRO==F`N z@W)}hrp^5^T0%d^ZJK3l*^PaW`+t51*U}#n6=K754E|1GBc|jkX5ED{B~>DZzcl9>A5RjR&YOO6XPH)8w=FUZ?SS|1aj*F?DG5WR6<5;nbf90?x?Z}! z*CKSA`pk`CSqG`4%FgZyZrb>qOIr$r+*(ke&Pwi?P^L)@`xPcAkXxjs54dq=CxH?J zuf??hov63@YO$=^i6IWqW>If9CmCN|Ol{B|56ROM07rDQ-0b!`m?6uZxt7di7VHMQ zg;X159GMkWa|gGE7sje%RNfbWZ6^;{>cW8uq5DiopemGuJ&T9}OrzaIEvzb6 zWja^!rS@Is^yeBXl>H};thg*mtxpdD)=u62ymlVxF85a*d{Wd{^Hv!g9h<7b-d4-8M0qap%QQe(dS2`TV z{W|HT0(}J$T!EadU=$G03&A=vWymbm)K2}G)`xP*R>aq-DOl-LC?H@sVGCJOBVgEh z%+woBRn|<{BvfZjK$m&A*(cXuA^$XLo~!1&XJu^`inagz?4uRdPrAuM;dliMQeL91^U9=|!pIlE$pa+7L2x4CuKORH5C<~fcj z$T(AGoXeN7oJDBGsUk|-V71nYZf2yny*|%^R!TCFPaWux_JUz(R88wC)M9ak?u=z% z<0P0WTb39dr-XI3(TZrjisJ1@9v}5BEWgJo$(xiIg%YvOyF~9i)eP%UkMOubL~$*` zDJ6|l5|Z$;_NPbn&$wzrE$Na!ago7#Xkcu6mB+e<-%*Vk=YbGyn>E^*;q=L}9S92j z%^KF08}b%mJR6azox{pMrIlGFQdV~uvBM_qbeKymX~heLW}UcS;tp3DxJGL+8 z;Y>JcXc$o5q#8!6V-1U=blV*DKeAFV`6-8EA$24asrti-B2ltX+WP~!bej~LQ`a^b z0x?-NC598X&c>OvV zjk785N0+6!#R{3!?$X_Z6@Av6zZnmGR9dUd?XHTi{sM@{PGURq&GX(un}x(cGVTclnsO95NBO)DEb7Ms2Zl}_ zZOg#?snkPF=su~JXuVvA)% zc*lLDriabXNBo)GLMuNAqb*$T`Nd=++DGoW8SU39*l zGJF=q^Q&T0893~n$YgPnf9ENG?=zuXkiOjA>JSFa??Uswr?;oU%Yf{bQ>cLlL~nE{(nh5jRVei|w-zWfW! z`Yr!ULJ4HQR=bi5)!Il=3*g0=I49fM`NoN#4_}@(QWvphKdMQNjW(^Jg7aB9uVGV8HlPz=aQi8jn3F_pqPr%>5HmyFhr&1QG{m*JyKA~ zT76s_za&e+!6&@@h^COL$Q=gzn^eRTFz=2tdTB6M1JUvkzn?5anD`a8a}e8&qi5s( z5=!$YnT_)_vx#eV7A5iLgH)#PdXsAbe8T)?cVd;6dYkD4*^!INv^Y~O=v1o!IriRp z8wb4cfm)U?V&fA^wQ`EJaO!?$^1)^f#OWnpEz^jG9M(8xn>jVfiaO4gHf5)86ef3UNll@1Oo3)+~x*ry9le^5U6 zCsA}nh9f^fEA@dfUPbi0fBQtf@_c*t^5HzaIa#WP4ytVwzpM9b7xLZ_lRjB@hAAN{ zEW$t`icJwls8oNXhsNIouN%>GrtUJylRY{i0ySl`Z-!I3Oy3i$mi!>8Y%WnHADeJh zQ+eo!X6c%H>7-d_iE&Lise&Zuf;6E_>=cO6$TSA#Qe-)4Fbw|4Nd;{TH_WbI$YPkp zVi09LOH(RgN=AD_0VW(ISMXUoQMjbI0frA-yWxd9PgW&f?*hEJ%$g&(fVQ5hm7=RA zJmm`|?p|3{yw|YTXO(4{*UX(xsBq&SdwjwBriPFT7cr_0OGZbktM^xa+aExh-j^hv z2!pQHM$W2I6E666B36Ra#qGOV5fh+J%+sVnQ=Y85LhNH6@9QkFQoHF1>JqzTR8$w@ z=955ETRL&8r|i+&hEYqWvJvR|3h5$eAwzD+GJYu`cWz_*p(G>CC6(U=p=_{9v>S!p zvG)pS!|l(+nT5{f#BJq^BeyFNSF=Lm;kHJyS>%vKLfIPeEkiM$ac*DonxMb~TIA}L zTVhLTy#NQ@mYx(FLYf$mh0W&B6zglP<&%0=!WEnjJxM<8329aGuobS}xQ2hHH;_BO zZ-4#7&mr$Sin+oCT3&dbkFEL&Xguc(xP|y!l~ZPkgT1U#6ohT`Q`L059`w#IL7Dh=LKi7VdEcx>~f zHH@G=Mr$|I`?`fpfl-q&3Okf00XL|O2JIkLO&yqvC1**O+hJP=~IYf6NhDi?%hhw+q+$HF}h%V zKV^z!zG+xYSQ6l)+y2IGpf=*B8Z>c(*)upYB}gdj1v=eHQY5zm&S( z`AYxS)W8_O&42UV+f$kNPm0k$I_le*rtmWi$Sv34h()#UaU6ZURQpC2W8Ndi_dYPKU4}$m9!Qq^OI64{Id-yokFQ5J z9qmzGz?eSZg9_#zBY=Vk|C21!0~KLU8jaycIYi*}9Prv?0+bp&a~DG&_Co>6rQx*8piXgbmcy1-5ae=OeqPOUlegTre5WX0 zU!cYGW1|5WO#253K;GjI7FhV8wrX%XLxlp>*~#mjBF@ zI*qt-cQINuE9SD9i~@PFkM(J>B{@IG|eOVNgwgFnr!P&!7o!JmQ^#+&@=XokDVj$I5srYf|%8k)M# zCbcS03`LI2D_7WoCKqC7UvmF(Sk$9jc@1x|e?=T2C!XOea+T@iH+P7BhqUW=m(`7u zLu=(-rc$DixaF{7o%fWG`JGvfBUpiEER)zLYD~*4%=MX}qu#wp$2UWNV7;?1W`3iS(ga{u?ujnP!@ylm&RAmmGDd^RsNTm>i>@GdJpz@$>HB)vj0qCXDZ6r&GIApSbbpxpIxBE->p3jB9qb7 zqaZ-P<^KpX{3&0xVs4pil_M%+)w~vAtLTRBrU!xq0lM-xMPIp5Vp8$@ng=(to!MGC z=J{VbqwapLB&*_$tmtP}clV9$oM=+e(Y11+X}Uo@-u+{W7-q6Wos~0He1%1R_2kBB zF>yBoc9CM+IFwD!z55qpV_sF9L~$_L4XzTxn8;Ru4p-=44mk#NVa6St}dX)5~o(Ylt105u9vp~h*_ z;~%VAdR@G+#~V~WR>po}2`!SmRooK2tl$ul(Z?x-D97V*`|e{1Le=%xq%Q&z_-tRh zI7O=#-d7B6G0*UGQi%-W&9me;Yncr;+m5~ zwybWURHdXChjuq?x;V!|QG!)Q)-Z4m$Y*2FtPAFqyxrAl!fJsk^ZVl~#rBFrcOk#` zW6gC9^W+$J?eq5j;<>m!Y9iWMVa#mIfF>5znZ?j{2MEf&IQM((puB>y_~|0{_^PK( z^k(et`?XY_H7*p>#d>`FbiBV7davsb?we`XBF56FgM0oWL|0t%QBN31BDOvTC^{|s z8+1G77bAKG_t;(a(s|S*EpM%Yoqfr{{gSvbbjWO+|IkGL{4trIJ1s6>4NtlLCATSz|R4)!AF5c zli>`3gm0fY0Gb?)cg`-tWaLKNN1;Xj1mDh%eCf=qn*HtXSt5B1Kj28}j%jV5xTU z>)C|25F--GzulZTx$MMt&whXUD|dAe_H$9Z=dQ#*j`w-~G~SmnaW=Oz{7Ty zB>*3-%9UFVVK|~!45}H7k&DHcCrRKBNfz7Y#{`)0r&hM0au zuu~&CA^Ga#k&W-r*j=!%+S`@9=D5kH$)Y0lmLu2;GbEf-=}(4;sd~Mtf}q)zJ#3vq8Zfi@JFy)?J;1$4B`7-!0$e<(Qu#$|%R zB|iGh&txEmXQ5lv>Af+i#JjN&=2_?{;!Ob!q#zfV89x^;K-O!_YXE_|w^k-zq425=26HqS7RwT0n<53t9pluX$MIAT7T_dEjYUJ78_|InWZNN*k4yvMcm zztrS^r>*{IF(xZ4^q=!?dMre=RC>x$M^TyL;AQf@X;&@E<06m z0bzq&RPMRklvyRkM`(=7bHKV)*~Z3s3p-{O8hU%_GpA?%6ro8E2lFRxfC$0(MC;vi z$*;Q|VQ}MmU=4PqbFU02LSflReo0r4fkwR&E%S1^1KQL3t9Weg?l14AK`{2K`0prq zO~jx!v~0Isik+E4{pAN^+$$LNU~Mi_c7XO_ou%5T)a~!7Tas$}!YPGyFnW$H!rnqg zDV{5x*(;o2#YS>LPz5Haal8B5kudROI7Lv-6+Sn>^T6h@BjzP*P`)K1*|o+15!K|j zk{!XidRsIuQE|XVn5OQdXRgxS=_goqT7EIze$9_ANMG3u+zN&+*^5RWnTG4$YFNetk^45petfJQG!n zE6}Gy2_#w^_7cgCn9kIqt=U?kWE$KVGw_qmwEYeX4`~~tl0vp+DjhF`91bH9aCRW` z2)jFq$%{RhwqFICWKV5tirI)-F7hk2Skvbe{F;2ox9MlMRp~96DWx29JOWW!;1|YP z+3nRlYxw&~(Xu{sf;X_EK_&C`?>K|~Z)|FX*8n%qXDROw**)0jJ4?chmpxDy$iJI!_%4zAn59&5 zV^ZGF>rL!robQi$+`jZn0h=P?;NiA00s4kW`!?EL`MiZR(ppVJF_mIGn9K~>1{RVX%78~~9JE*yp_?$~A=*k=(C_M1gq_lp`h(~f1{W8Mpcx}SB( zv)ho<+RLc(FZv^R^AP!);KR?+=>Rf|OqvgMFiY$rlwmd*-)D2IBd^d_T@N|cOD+H< zk#{1V}ZPR-{@2N)t>6) znm~4;?=I{>L1fMuL(4tArzG*}g`zxWAn^)Ila0(iHyRgrbar`+to@rGMybXKh<;G(I9D(V!#s+4NVM_Y2o4gpi~wdlz@B z6`j`U)QrWMNIiUfMUe5n#1xTspDLb)`u+J8>2e}TRLRnCYjh>L{at6fKY{nc{rek5 zr2%6QNnxD0K39-bio&4vr(~s+cQ5@Et{N^!Ky~S2+()ol%q=(ELvRtu@w(b$W7W1a zeINy=VtG9qbRmeH7bAMq>cYtL7=Pw9|+}Yh# z1!%8fqm7LaiL3M;zP(#oZA)lL$E>i}3mAWU(?r^Z@oAcQeWGK`cPGzZCYNrl>vn6} zb^KK|;&(nY;8>>n`85tJh!Y?o(Y5>SnZ>qlrj$A2r6LG6uv`HyvHsmbsWluuxMuOH z%y3=-7U0G>Sq2FPS&1c{^DYnlbaNQ~@`iw+vMrpJYBbprU*9d#wKRzIiY$?xu zvoT>xZes>Gzn8pb+-Jh|E=;axO3eZeLAFFM#3ZRsA!GV-%%)k)d-iFua0;z%p zdrm0>9){>}+X%Bnx0Hn=*n?=E3~N3&)R zdx-XXG?V->vHlUO{(EATm6QJ;2{kkJpUcRHde~+2n)OhKJnqH*d*2BZ73v7k1ktJ5 z?T*-1^Z!;3SY|(=ABVf&1%NieDO%>N0y{a%I`B zG7PGvU^P0pBD)V`s`x}USI(U^BSyPl-OJP$yd@)ph&jJi4C3Z*XTEBdNHmw|j-8pF zqnKeORX)h>u7l?2?rI91^e)7B9Y|H0gU~k6b6bfk#d5{3pelFr2xmFnGWXPxXbl9T z@(@5xVEk7){^GYpVR*_{#>?bS{Wgyi4$gf^KTYrT_$MW}CL8-P+&6l}XsXpR&~v)* ze(H4}X>a(;(X@TokJ~B&FCUL{Vz`r3m%0jcNk|npASR?%m&H$aEgiU$_{OF~O2U|k0EC6cg4ZjFvOhnAm8n(&ZphGm^5=Qej z^m2x5kxg}l(_qd1ND;tth9!u{=t58+=4?fCq{azreJd3{2UFG7S!tOn)#{p?{`G3w zr+%43NMDiNg}mFII&EoZS(}=xC8yxVAN5?vAjY)ZrCUCEMut&utSIQkzX)E?pOU&a z65aOrtOFo90g~PE|4hsztrN_ET)`(7EwK5Og~C;gMPr7cDRnjpSBu?gGVE7*Rl9JC ztX>?ftzwl)@UpVf*On9$^X8EVv<4c4^}|)e7Bh&RN3b zGmowCGf`Q}|1kXYem4L;9;h}Aq69s#W++j;S;>rtwfI!NxJjCm!98hh zW-8tbO~Z}f6$-hQTP*L{QRq9L(kBMp6b?#}MMuaOYl z&=r3;=xORF9u{*1J3U~Vu?=2B#INY}fzp2ozF=$<8&`W!3z+#x)p4I#We4pIT3Nq` zxgwc{a4+CQluWEXf;o)TK}V<1VMrMxC} zMj7BfWK&rX>l9fb9E%1XI&acxJWQSTw*_-)29Q7$gVoEo$+8GN!vEF76O5Mfp1!|V z24;WZ&wq;e6yM)51vH&jB`#;6MTPb#faud5(HvY@VId4g zI*Y|OaInkq**kdt{X~WKn87rSTdS8zD$*})%W70ib@XJw8 z3r7rJeM)PaRjhtUEyHycQ@xJ^32fzsW>5u#ecQt9#91XEY@Q~3;V4t5r_Y45Ezy<2 z-+tE2w}m{qmo+;STg#W+d?+k0A?mlNt;8);Y*%h5VG3ELzWQKX=%=#_%Vg&jRtLm; z8Jcas&|vlH%Mkfj#4VU%d_lIO=bCOp+Pq5kJF_4fhMVV)s&T`*>cW@FS!Ss%+pN2n zkUwlswtSz{BUuZmpFbC?(%~`*$yt?qEY!2Fe2q7py=%OgFoeIBr z7KuAmxznxsamg3^3_c>0Nf1{n`@NlyR>ef&+g3=N9|ycainR9jgLEf=7zpJl9+`uu z7%-OtBb^oX*!61#D$)D%^xU_3k#b|PHU0jEwC3oLHW4~I%8QvZ!S%yl6s=R%I1ep9 zI!HHNi@xlavkV>C(0m2JG)EkZAB&8k-;6p5+`IBgcvihYqkP&s+NE-GE5ilCLi$!+ zO`q`%x{RX7G@mi+Y`i&&@fAA*eg8=Txc>JM*HmxA`Lip!;7`k$?73s^fOp zB4}?iZEN-H%Pe}!EE_QikQZX8!3|B|`vG$jVM>cI?HZcMRGqQ z@FWn6D8PnjE5#TJBdL+Re=48Rd2<&w4b0LJZTPTP@1~9}-BoUHLB)y~0?8bA3b1qh9W@3WcqPPQ1do@rn z9VcOd`4MChQYkSxxq3b6D3wesvgQpHT8}x-OEY#@X*yNF1b5yaT8v86i8m;{%uqI> zw6u-o`!*odUez29!z1rHDBf-gB{U$kAA^*H@M7I&DfUGhH>CZ&M5|J?nC}8RquWGV(U-2%!NdtN`i^(#RiqZ4Xml=A6k_voc7$n(# zHHYD7l|G5NqHR=le6L9H+UDN=T3T#u<0jsG{kRRK-PTl7^<7KB7b&YFLW2j>qo&Hx z21rAUdhv@A2&G0N@=p?4!z zAxrqMP$+H!=?l|Y{BcODyG(a$ldRcl1;0=l;S=9CPJPg3Pb}e`TESZ}HRch1wE=f~ zzS#*+DXc)Tt_)9h?dMF-$ZJ@V9#ra(kl+iQ9T#R{et$l^?^ovT#;B9ap6{rDM`WDp z&_P%8$iM{qM>&~f%gZJ3Tt}0wb+T#8o`pe7#b~GY>FD9pszq>DKc{Z|TXl<*Bpm~B zMsDjP$%hsSPB#h1c-L!M%jQB2wm6O*Q+x zX;g~mY8Nv%0~6U%{ai~z>q0o@txzV|ilr5R$vw8V61G-AYW`9}5rFhCETmN3@3%Di z`DPTAXnqWpRX^t(lFh zo6tVQxNI2hkA>d|PsXc#yc!T0A`&ENhH+DC3750~fB5>w;L6&i?TM|4J+U*fZQHhO zJ3F?MiEZ1qor$f9jmejJ&UxRr&iT%HR;?eaYS-RXyH|JLclX`bbpeROu0lq)Y#`^imWJKm6T ztjU6jw4RcT3bw8l-k68f^ZjC3P{JhZ3cS+W>Vszp;|Q4%z5g;iNeW$y`ldJ+VA|_T zP1C^s(c6#%_r^OHaP;ltulf9r{-c;jaIs z(5%F#j|7SeYTVqw%HVjo|H-7e-g-d2y+{Wz4!$Ti9_%Uehk)JVf@GMDX^~C4A~7aq7U!VQ}?Z_vk0krc6TD z2&8<`uYj+^uc85_$#<)hQXW(v3z|u*TC}vRA^)BcJUsX9W}<%p=MV+~xw)2amT6D;A=CHI_Ogcd@(Ad2(Utv|Mfcy2 zxBpi8;Xn7$qvSPZvG_5#>(N#0Xu{{#IJ?NvopsHkO0RQ?rSTzIa!Ic3bNcIyR+-27 zcDv_<40i*0b9l>6So;AO+qtVoUE^7|nYWW~&#zCQ$fD#2dVA7nXq#nwnSqb+JalSE z_)q`=$C2W7825co>om}~kKkUs@v~STlc@mm*dtho1LGBk;xZ2J3TL<04nc!u(Bt`tlD}6*1TY7j9gG0*6 z@Mx$cApfX}woPz0VZ5qK=EXaY!Z!N5k)&;>VpYYOXW$p>Mh~KW!<)u#0X>p9nsuXa z(c9Ea=Rb6Ap!jzh)JGet|_uM z#Zi^TS;u@*JIePR;g17A<8ZYUeAe#hf7?a;TMFxcI>0}KT8I@s)q7=atla-s>pm%Q z@>8P@V<=lXm&W?3Ea1tXkg!3xgpd$AAe62jvTtNK(qj#P3ke`jC?Ln{aKAmA%wR>p za}o*RH@&;SKX|givM^WdJ-M5_Xm9WK-i+B_zO>~i3E<63U)R)RqEA6jWABSkYGSc2wd9YxZ&%#1IHwRii(TCd&#!35s+Ktx zQT#9uY_G~0+;JaY9kI`_%pf#X!|vB)&+yE$n^;%4ZXLi?W#cwRjK{Rcjmp(w1a&n_ zPTq}AH}=qC-GDGi$)%aGwR0qDPTl&N&YJ8l;F&$TsB}*#xCf*cg$*{R+pXv|6nw(o zKHg_&CT%)4CqP?jELe31*g$uYlyWs~vbJ4Lkq*cs9x)X?2VR{;oT49<2bRJF2O*MK zK>E29T@g^;jq=;rasOU0ufr7w+Gvdd*5Ei*^Y_6{56;%o~rvq1Qml;fm|(pmy|`(ZL~tLw6ZizdTK_TTNA$=Z_rg0hQvO^u z*S)xfy)2&rLuK#tD4!z3E11Lw&nc=`1>gSo0I{S&Jj#`@u9T~^S_4+mvY$4zp3>nL zmh)-jCdj53jQ(QiWNkFfJQVDEF*tVnLg?$a(3O(qz5}M3vKgJ=vC$=Ha(UdDGdTg? zszheFef&Y6z3fZM3kqRmguFPSG?P_2kisGygm|Th=O${wgNfc8RL4Rmd|?mOK&Mzs zkBolCq>8Eg*BPnV;%`54RE}N*A^CG1=QexHwe@h7wwC|$7hxmh4{M$ z8TH?lHvbZ2|0rxS)uHqfmE)^kUY|ZL-u#Kb<_P+s0q?_``hBqw12%#M5@!Q;U^?Ly zH!S9K`tZ*oEXi?$hYqC+4`mzzK?x-7B@cjL)7hh++KGmAwTiC@A6Q-KUH4zOzODqza{Gdb$`mmb(p2j7ZxeUHC8&;6Ue|d$=LvZrT6Dm z;w&)8!-d%9!y{QW*Jki7xLPOS!WzIYg<``98DUfMcl{L`z=Da}@of_Rge_8Zl%eSM z(T~Fk&e8W!-LcJo~dOA)ZM>^GBF0fl;>R9^@ zT(WJUPlO;_8fFo>TFJH&o{XYhTYzxKIHuL6)bCW1epLn?li~4eXv_%pH!Cp7)KaBh z2FxRJwiMOry;293V^Ibj^N?0E)4TxQLn^gVDr;z3IlElk>ONs;l~Fb8ka9X{f7un+ z2vn`Ub;;F3W9TJvJHMk8suqFXa#XF+1xhegsumG`bVWXFvvOhkq#z zw2ktW2$-$GsR>v!MT>T?W@?*qFD~?E(e^K?joj@_=*`0I4XJmv-b{u~!w}DFog!C? z7ES+0_ZHFKSCnhe0;tmN$nPs>hsPZl2k(`qjRcSS<` zdUv9!naMSb!)f@5K#l^StH)J!zu^c-=ylj>D=37>(Qavs{gG71%m|cJw314|$ z&{ax)b)qpbu9>=f;(9TLESjhwfjs=$t+^L3)N6L$m|iI&wli`=YY)aH6+$}c5g0;g zWde3WLwAP;*$GDQc?ZL2`{$tLIhXLHN4k`z$}-AMd_qtiJMBpWEhU}n)K&1NEPs06P7maf=1jw zPZ>!?$fxw{*aTn_z=gzipu>|nVx|x7wSK-oU9`6hbj^shwj@NSg+=vx+*HfEAq@>c z)>mLme=fwpzLCWOg5Lw=t{a!1NBDD3O{#BLAuO0_y`8M&QJJz2N?Q=RMC~5X#UY8N z>Xlx=6iZc1>A--0`$fJgzd`Zj^ie&1^X@69!~qadatY(ReZh~v&Iw1@BS6LP%`SUl z^impB@sb)GcE5n~QW)%a6B;U3^1_M{Im;(o5$F!7%H(4W1Rv&MVc8}?&F*Chy7tb} zEAe=U6i1EO0fF43z1mmyN^ZW!@s3?`LFKGb4E_l5m1}~kW_xc`s$y=Q-*aSn4#aC( zAH`#v!k;EGI{KL@sm))!G$*7kWRPhi+bKF-2*#h8#W>~BeNj;eGr%4$EV4OF?Ldzmli${FQw1RKvJn<)yzrYaY#EnZkL45XNQdBTG!Tjmy? zuOFnRuP#GW9$@7%0w!~6doF{YraT-{y*vH{y}@2Tx+J2SkYH2PQ^Y1opRLLzwkkbX zpw%GLs8O)JU(fxV8>)J(>?J)of4v@8gTIZ!M2mYrvpou`kR{P~4^@a48A>eevAF=$7C za4<_UQ?0DH{fn?Xt1gKdm_*Bur1-Fb6!uLYVgrZ z9CCB!05s~_9!%(#eEJERo*F}d67&B8c9W~9=*^{KvtKgbkJgnW*!#b&> zZz7K&c#RuXsiB(3EhX-m_%dVsY3j9`ho!YykqbJj0qJ-);S*^GT#M3B%|#3NHAsTo zy@NcmG>zv6Hf;lgo$`a|;MG*48ZxzK78YM|_vrFoZR9F9V>uK_d95qoJSdjnevvqh z56sldmBa;ap~<~kj#7U=YHZTGUCW7;ndZ)lXuJiQy)>UYb7=QuBVH)DqFqV5b8u$% z3}Ys{HF)6{epC&vkbf5AigQtntxBddpVZQ!)yh1EQZaM(s9jdPoL}!FF%q!hoqolG z$4$mG+(U9Q`u>W|^qs`D#;;26MRarFDOg}g=$^y~7IR~-8BCvd@bq$*!l6}o&eI&x zLnocJ?a38)^&0z7EEc<#a(BJtL3Kc_B~KM55w*IQ8RCRrNWbt^P}|z%FdV}(ie7Ye zCPj-wE5G# z`DsykZTSTl`GpAig)s8dLh@516lcnCk5+7BvKYA-={rjy={(#0ZWv=%zkAG`s(AuZ zzt6D6vZMThxbz>0 zrktr_=zQi`rMOn+nR;6Y$#?zk=D%WVn7E5^7X)LI5;oz(GU@d2BS(-8!Ep~q>@tEX zk9eebaaQNm>cB@<(HtN?()GVg!slg162XwgW-{)e(_wF-zh+t;7pH;r9V)n?Zz3lr ze;p?HJfM8~;sZrW{}L-=vIgR8n9Gd=_iLw4f3KPh zqLPlwGu3_a%0-Nl;fP~mm_R+Cuz%9hq$!J&v7-1MUl7_$K>v**GKjZ_{K<7Bn70?@ z38zRAnGN|jqk%V;{TqOq8A+7zExCfVp4kBZo5*DZ%Fe9lm2JTwcsJ2=pGSR638=!m z$^)j@c|c-%q9ljjjHGs~M#BENU#~FQrgHA05_tx+btYZGNS1|UBVN2louc(mD=|$P zC4~dB6)(8R4{8JS9;gvVv2QvuhP5}}VC~ctPzjY3s*EILr{l=IDB2u3~v&>x&&lowsMi3&-`ecR`6#sXf&p_kDJ=$w@ zL$w*QSdW&pL#G{t@4%AXmheJV-K(~z@d;{t@^sqEuEFc>aeiX1{0)R%*xRBbF`UDM z%_?E+R>_3GDhXpB(E!~n!MfvOj&KosG5oUt;@af|sYB8O|6b*v{*PmA*v$l`? z1gcCWgbzTkP+r2#GLj50fB*91v?$Xz*mwa?5y)QgSFho!FdB7TxByY{7=&J1zXv$! zd~WfW77a~nF#-LuK0CbnR}^(jsj2+Zj*$hHXRWi=WlTGOVN!wdm8^seG2b;g+9>=P z#mELk82p*|2u)t7_7J1{s~+L>zOw+2Y%&UkK0jDP77-1RaR60sPU?kI@k9(eM6^9nOb}?azek@MJ}p3x2WVY^LUqGAt%c!o1MYD9>cj=cx=m#V z(*@3XO|ueQtdEf8Z}>#Y6>{0*`GjZwfm*nExsCmXL6b-xc+H68?>iu#NIicoS$6`V z<*zF*kV!J$)l2ilx*qk>)A>Ym(SLb`8Gg>#H%070J%I>B)sJU4hx$rFGKii?>?eLPrVl)9*6H z4;(WR<(dts)~9tiq5Mu!Q5#VeEwS^Krq~^9RqBe4S9bZ`?jOe$AM6GmyIr5lNLg2S zP{3)0(&K2tULsPAy4tQsOK=ZO;$H1!-LD~`#SjE8XBA9?Kl1Y2}=TO+FQTyiSl-Ovc162zM-2~Ht@FnZFVM;N6 z6DEajx8Q^KedQ>zJ%L1~fISQr+4qs>T_fqJSa+>7Zxrcz&!2 zC8eq|afu5*VO5i2rAGdHOpMgJ7UbjwoWN6+nz;#kHH(14Rf_T5JZ9^fAG4_7ZCW|w z{l|;;vq{!9;j@e2Z4x=ua>mSAlxk!^c<8rTc;8n!vkz?CrhaMs)U zPMQ85pr=sz%LBrl)-xj|(|p)qYZxN$_OnGSYSU&te9JIS-WYIA{!?c4{-?l;{l|co z;~^F^eWwKfCddYoNF8kS&d=U}sZ+hb&i9$K{$n`%SsLq_q*-h@r*gh#z--2D9&2+i zi%2u}F^j(qOn&o#u&s3|-7O z3%LB(-5e{)=o(@656__Yygf~R?#hFl`s~s7$zT$6u=w683K_=q}yaY&1Zd)6?}rPB>aN8sIMIa057{$#!Vx$?Td>I9JS*ce2Ey>NC2~QPD$h zO+4Q5bIV8xRHedR{2&Hw1raX_JKsRhKS2L<5=%aHMy`;*e3AH^gZS^GcIJG(Cs zbj(K!o_|~7%ew_0c>PeyKK|++~oegja!>s*t3J#pICa|Skb`f(L|oaO;wD3awbe*|711-ToSXSGB3C z@l-DQG(ZOh5ztdRcHL%(lAsur!`l$1cL%Vm`e zozipS`sF*KL&c7Qc=|Y|)+!Bdiy!5NHNQ0x z%foWMMHqGeFlv~$*@;%hs0rnyOOh}JC=wY0hpm-ac_qZI-ORbm)66ddj=fX5&Z|z+ zmq38?7|JG#Hajj}q%o?JZmcPf4^WWLNrM*8m^UW*6&|tQN6r}h6>Wp3sTu7U2<;wd z)Hhw7a&XG#SQ7KO0AJ;S3Wo?xveBtHSU#!-0pD9?2 ziI=(X0i<~95n_1)7;B3^1qVS5>=C5(r)fLrr@^9kOyJwEe&w*ABS1VL7C5L;G>ep8 zlL>G_3V|JoGA#x{4!bZpcKG3G^`XCE4;lNAyxNa(J8JJL{s3QJG-VUyCRbHqV)d+N{zoBeI1`G%}3Av~CbVMg>OFEs9fz=UHeDFzhb%2gV{ zaXng`_?lgOQo$!H?0QCGWbu|f`61ytcHyzDf~R0oQiv**XYf7c<1d&j+mYL%{^yx9 zCDh;LG0DHnV+zJrpUOlo#!BY@zj!XOpuVA*vC*H89G?fz%5vDfAg%o;l*6?8uF zOHrbL26?Oqs3Uo?iWnkLUO0Ik0ho-{Zd7|*eG`k!6Ozn3Kqr=54Ce*N3&mim)J$0c z9#UOndTPqkcJuuA)ARcmw9^u_xZy_NDZO6m<%kyl@IVp>R!rzpoKXES2q=)BIo{uw zK(dMm^~hOYy(Zp6%V)ME8kKgi1bwtA(|8Q7%CLR2sv9uRMQ|Vi3W}814ot_88@rie z7+o#n`7FzG=LiWdZA2kT8`~SWNDa|#_PXRGPDH!?xj6wxlWHM6vQlaIgF6;mvQpU^ z&(*>MitK3MZx->E0~XXw!sBFaQGsE?fM5#vtVj>;++~$bMGy%ut|eLeD15RoIesdW zR8BZCXU%Id*Fi?}b(;6e=2t-fAp5$*&UQrva5#Lj_WBSmgezv&Z}f54deKQ6mzO-r z&C0S?O4~M=bfoDDhV^op*8TZO-nZiDYrjonJU)11MyT*H6AX30te=*~^!x=bw^_#T z!Zp?!4P*RP4R8FVqY69Hz_dNqhNdvR3aXx+$24K;%R+F**^v)m!b z4qS|CsW<<{Jgvzoikhyz?N868O(3sf0oL!bEm`8&y(#x5+VP6sr*}(>c4Zbf4Y>?uRj1S?)hr#6tIR7)^M;y zYYG%ZPzt47;3o4(qyT5+uAgvm%zrNH9kcKV$l`6tLzl~24-`fv^lUuz|n0>Xv4dE9BhwJ)zR*=2g zoY(2-aQe!v6F$n1g}Ciw+I2%!SHzgW zfhA>2!UAS}zZu8{*K5!^w%7ckyL%5yH5(ihsi9u9W*4=|vQ=|ni92g!B%wF=VIUnA z3juJd>8pKwj~M(CmNJJZg5LiY#YZC098ipv#lK7!sSIZ+M&(wekBbe>!rZ}k$ni8D z)d@XKSH(JAHV8cD=Xqy!#LEhgv) zu!o^O+QW}NY|ao{R+NWq!Yi;bQ)u@Y$!T~>&><*Vfnbi8Z>ohV;Xbf4n0?;>ST*%Z zB!<0?psun=FL&6*GOUiJJfZC^stIh7uTd`uiRiKJ$is5W2E%fM*T@at17iEISiFL- z@w}Uty!^u%>Dywy3ETcyJ%n#}&0SH%VVb>=3aLeviTjQym<2pSBWKm(Wx9eI`52ZX zUVb4#N1{KfsuW?kLYFWMf}lzg5AtEw`+8*mM9wp~Aoc9$aka|7@Vo!}*xBD=aTJa1 zosDe_jb)sz4gQ-XNui?kAE>R5O$M{MMy3hJk*DP%Nxh~}B0VQ~z0?Z1uvuDY_C?!I z>il-rBJ+1mnO^?@-X46CtOEP!M%3Bwj~)B$)2&DBty{ic&j9M=TC43U|M)PjG56yZ z(;S0c9=u~WGlhj+yO#4K7v*p_e;(xG51T7Q zz`yYb@`peEti!jr7?^3;Vu62-ZRe)tZbl96`cwk()sk1`-=`9fIwYl=htx*J`;?9A z$GeKSsX=1~63| zpN{tFO4Xmm+Zqq@^+UXpP5@HH+Iv4dsAu*NFp6!~b8NSS!qxi*Wng*Pz z9dqjhS5;vnXV6qUbJ@~_>Ap_(Ww!_jmn@qEk=7dT1DYN84zpQHMi7hLnNy8~0vt-0 zfUhI)9&*E8!Tn`<(n&{VL;LMtNTOX*GIxYe&(r#sakPK;yuYOuQZjS+oIv_7rOiLV z-PNDq?#Ul6jxxnnjTZBjmek~wxPj^{7(s*$QUl$aYs*ae(gNvA)dwgna#tWvxdRzY z6SXE!dw-$3Hyx)x(cOQsXBc8TgJDBMQ`V825bVW;f5j_z3n*q7qZD?+L z=zWECp1-FF^z7h79GYp&%EtIZp1~<%x(&1G$$6Vz#fZwI4hNWs{Y}2XT%Wc4$^KLH(1@@d?a0&?@A=ADmlNJo&4&e|g+re-I zciar3>z7*8n6F%d_V*g?5G-o4Z(r}b&SKnpYRaecW4M3PX^-bEyqDSq4B_-;hW48W&tzYm;Km$U)_w7Xz2!Ae>6p-0r&?kV1-+I-`(; zf`I^OI7$bGCMKm?n*5^P9!Bszd||j+G)x^WO&9mM@X--S5+*{{8k zCJ`}W$*7(24F`)x-s1RpH2%5ie3p2WIxyjPj&+|X@8G<65(3myfjI2_&VsGs<0+d% zbsp#FI|_CR>e%3zg~}k*_2`j` zT^TJ)1s9HUl?{&mK1_zoDh4;s_1HWpNfCy6nK&w=-YJC1;@C=#$;V8;C zk1-lOnz8e`Z;IS`4U^V$XY0y0blQg8NUwI{&Scti^ET_qd-JH{=zQygXDb*XPz~OI zEi{hKgYNWI-`4hfkW3_49F!qWHu$wVhWfseR7onVqd8@yWeeUA+&ZiwR&zkAJUtcS z`b-l{8$kvz;djK;8MuwCug6io+G@*94NlsOWhe30CnC%#YG37wCY32>4y-6`Y%0Xc zD=HUG!q6tdRdna5H_|RSIrp3Re6bZQ$1BXO%^oIPgjuXMm>hx{Ejie=s8Us!E14T4 zq>Ii>oSnEgW~?+dMB23KCq&^bHq{)P9rpw*t=E{E3WOv0%1#8no>ho~0cQfd5?Rxm zOq52d&(#mH6CfMVz>0QrUAc!ac1Kirqj zJYT0gAu88{>mN~VF6lqYY5^J|Atg*7(5@zTEesk#UP>-{MdH>#NKNNr5a5y)0}P`T zhHndT`MT!4Fisn{WozNQ8LP%QwvptlUh6m&cZcI~3!o{N8PXH1Pp??wsr#i&$%FS^ z{R`dSQQ6$noQw|RvBmXcz9>u);`XM&l*I6{ZeSN`8a;2hJ)|__kfp81FXanaBhSn8 z+ryV_%i{(TnqV}|z$~4qtXuv^durl*!nJGIE+eI9{wh*6jkM9;?ug2t;h#ciUzW~EQg3DRgOaVgfQEb0b<**BFET+AsB*RF@1(H zGmYOsUH9pxeFxn|eED>4abB*~Jh8bqm01 zUu?<3l4hb9N~vq7jOSj$%Qo^^9!sY{%5G`GpQ7s_+(N@OCkUa?TW`P8BJXiDK82Fu zd>g{yG4Y3vGqDTi;msclRrEfpXapf)&f*(O66*irdyd->61%zV6o)G;z;D z&-pM5%42V;`j3(~=bEf>|Fae$`@xV#{t(-_a=#iV1cbMHyZ2u&E)w3J?|=LA zT-fdGtoKLi6VEYKQBo?*!Ls&tEMTH1iEW0KGkA`;t48&q1L`BCDwG~o7_}gFxX>+o zv58Q9snb!-^^MepTw3xU``v56lE@jl=rqvOQGVCP^rkplMV1$Sw01H~FjbB*zb~9Y z$avOK{nDoOb*#r<9K=nZZwyjcDLg!J?t+xL zifB9<2O><>@EGJuE;Ju<)_(_Z`P-|lJ!blm*~LCgKAb>g*Ixcs;( zMTL{bgaMt*QBZHCt92qRZNV=kfS~|G<|pX|!H(WZ>zjcSI?B%Bo9MO@%5B{U41Pmw zRE3I!T6MMMEQ<(=jwPzZ;?t54{^m^tST66acF96#?K$Q<2g!1ES5i>DulCQVpq;Ik znCZqc!fexZ2{7cv_+^*OMs8qzh;6n5Sz;+q>{I$;%^&_>vqZDQ5H|MV{{i3^`axEA z_Gv%9zqQ|glzIDOKOtiieP=5t1!Fr~NApiw6n8~qD`UgYhkqC|Nxt`wh`4t~ku-LG zWu*!j&G8_q!Cr61uJ8#3x!Q?0_Og@Uc`_5~8x@%F)gK({E-?*ifq?Pqs(ZcdW`{9v z_a<*_KS7h!aY+)nE@O>Nj*VVmZ{(m{lu_QUD8O5G(0@E+{St;%q1XO68ZdXSgc$nd zA;P6k{$tEvY6|1WVF0&F3O}!Eb3pF-sDBI2)}xSDzbBG%S=NpF!a=Rnbwdf6H@g8_ zI`6i?`n7%Ho78cr*i&L`yT-TpEpUwUqRy+=iD<0hF0=6kvAt>a%2-}I7-1_7@*X|2 z_h~9Y5@kCg9?11_9WmjT;8v{LbVup-2<;bnTv+$Dqy?}qQ8$DVDAEGfeOQ@Ax8R33 z9Ri+HY?3IdCD<#Z!MMAudPrrmD{4iWM(Sl_W=ZCKLi3Y!v93Bf+>`sz>h|9gYDt4i zVf!tH%6$H6z~uuSrb~ams<_W=^55N^`16JS$803*;9%<@XlwM({GmLfEfdIx2yvA* zhgSI@SviDmoAbI02C02Y3JIsl#1n_gswR+=yU_o<4aAjbZn5XN`69-LpC zAhT%$;efv|nC?DkI>BxfdkvAs!a4Z`l|;BnWUZBZmFRHw#y*KJ`bhuY-SaqXI($Cdfp~o(x9V77P=|G4EAx5rbd%>>6j!*1g0(-u)Uv4{s1@D1`woS>{MX zq7K+Iud0-4+1U+_ml!`|cwQ|$l7;oIQ{ntM$n^a=oeWi#A#J%yj$tF+$TmPq+boI4 z?X+a@*HO$z*A45!9120IVI6ZR2M$gvDeF+I#uF_WjkZs7FOU6$CasRD2?*SghcVa& zDk{6+;n}8RW66llvk`1@rF>Hq?4ESks-t+G37h4hNv>|cFf1(YW80TJJV6BS9`m4< z*-Q}#{tyhXjr-FU?nl>}kG~-BhN@;Re=_sI-`bSmzq#tCiv8!1)IZlb`G2t3G;6mh zgnN~Q65bG`qvgH!VM_=|M3H6~6tGM4!X6TJ=n33OI5a# z&SJzQ`bDG`NafcUpL#CY;%LT`Jq9H%3qT>G6N}ST(L8F|~GkfWhn7V1d)fm;L>-qyh5!UOA!`I1A1 zAq7=e4{mf)2aqJ!!tdt|-u+D2J&x;sj5vopK{b=_Xj{W$VtU!?VB>3>&*6QA671!q zTuhUR$v0JTU>OJB`P}>#Ei-=PULAzzP9w9rr#e@@ga6qNDR#`C{!uUz{g)>1zsDQ> zfAbA7^G~%a2SYRWzsOmo%pIMKZT`nstYhe*`}h!rO%0`(lJri%P-d;Ih?7DMkZKUm zhbL>qL)-Q1!5x@k5Vms%glgzzZuRjl8E?Hd@Mi+SKzU%jFc>iOacsi_l%z~lyk!91WqIs?s$EFr~c%!o8IMnMiqMTlRXub?4M4s@5Sp;Wrw39O8E)i}x7RUTzS zd0;R`*-71b1EJ@``*PAK9=5wm`f%g3vvSn+!T#vAE}reZ7e*!zAccCpV)xSNM`!Yy z4WYUs!C%#V(1*agH9*EwjoIV*(bEFR;ASg5)Sa~(3xSp@88yF8Rf_3Wh#_0piv)B( z=;L*jk;+oe7Bg}E>Idd3!ZYQ5N{7&W+{VUxi!$KK-!Ec%tAy}9>aTzDgxk8)XYVT9 zVTb$J?r*-1IlY#}`mG+uQ;oMJZ`Xa+@4=Oi_q`;lt9bL?``%5S+h=mOu zSn2ypU<%3++39s%%#Uo!)2)R)R5)HmPwt|fBOa@s?@VtB;&1H=Z%Gi{58KMYm#kPm z0y_Od5yxdQjH+z@dPk|7R9(D_XuUu@qy}ss8`GAk5D_&q-!YrmX2cmZojqS^I zd~OWw%uv!0l}Drb>;0Jn;AuTBeJ5d2Sbx^!P!k5UU)c0@{iQ&byO*@2&d;XSPOj_L)Ich&VMt-r zo?BQ(QTmiM9Pl47FYx??KE7GEaNx933@71)fgf?i)y4)xwJtRx#g;bl%n!qnt{B5O z`4o?`EMUA=au7AD=WA;5@!p@1)V%Q6@6@_nYiX`;x88dYb6wsPQx$FA4wzE%FmrZd z!op@zAyW{ve5js^98z^HGMJ@)l>40rb#QHD%ass8$^w7QT&Ic1w9%oJ*7&uikMhCweHuZ_6cb!OCbiPcxOZ(7OLXQ=F&3c*|44;sEV zX;$A-)r4)5OCPP9a41h8S_sK4Wn-gIr`Dept#9YJ7ul4NDOtJK_g_bbrKa1{5-VcQ z3;tL*r?OOgBJeV0PZni#`?kuxBI0@C*HQS^ zviCkD$2H2mD-yrstmEXAC?v|kQ9w<+@X~yvdoq6mi>A~(rA(81_py_zpQFH?eJIXY zzd_`pa6FhsnyR`bSEiPTPJ-!ZCgP!fYb|WtZw=O_Ck(}iB_Y(dH#5i>vn7x?=2GA= z>c4yIfYTNlt${7Maw4Zv4U2+0=P6;Ol|iwP=|XjN4x*(&yq;C3MyB#n%Sj`yBf*qJF$B`*<=7t;MY;+Cqz?r8H^}&_D zvvgA|_U@bJbQ+kM4rlsV*v3{MHEeS}N1t=|Kt3j?gztjnA+JGFKNK6cG)O^<%Sl0I zGkaV-=U)~f+TrEQ$b0|Ra5JA1A* zUYxOq+pPQ)`#oWh1#Vjy^U_8WlS>I!C>|$lQ<+K`OsAeDDbVUrN}$qc0I%JcMzgxp2NAsH=n}A zF`p_@2$Q-s$>(KVNWjLGE>!oTU9mDcallhFns1WJ4GWIk?{(uyQc_=-2<*dlmt&MH ziUt|kvsRIu6qd|Zcvhg_6UdPaWP&~|I`A|htJLd{ByXe55~Bk|B5I1wSCB&+E9L@W z#p3A)Lz7rBi(JJ!8ofnM)M2Sy>!&UUJ;AAzSNur22DYY4YO<9xVV*F`E4ze{Q07yq z0JC*|miG?M^`UXz(&23mOg1ZnD}1T=$=0JdJ^>uNylp&y{9VgmO>p(5G0PU;BJNI)cOU>MotpzqO-k< zI^sn3>{vLb$0BSKQlU6%tro|m*!c9(qzb93_UP{OZZ8Igau^$KIoHBVJgZ*tjHFev zpqi${-AX`x2j+tc5ql!8$<`siT=Ng4CBqO_1YaX55pw|+kE`P zrw&FBZ%R=hS<`}YZ(G^dB(8TipO3Y#uc8YucO7?|QxCLYOv~A|F^x?;3LHi#pk-|f z9K-465R*63JWs5NZm;V@I&W3#TevleTX8OYGZ7Z+1sXU#wbOX6Klkt)Q5FZ;UX;XH zMOB;~jn0^dQ@P<+^7Uf^4?k5H@4@;DaLJES`q&=O50@P>QBxX3QGFbZ%=bOOPqR~$ z(AaqpfTuF;1d26Uu9?<%OmiyJ18xV{8cYim+lF?aSfzVjnY`YtonOW^wfT<{ru@-d zd)_JDY;ZhgFzs{=MwJ>Y$^+Iwr#J&_A=an7X|nu6<`8zQ3}>97lcM%Y@>)m zJ)729V08ziQ@CNjAN)WRo4x6P0@dgMx_qCRJXoXu3Uo<)n7|VV=3R_rbr0sfp637z z;|lKmo5Pa0cKRO?a#FowXeh@PSWsI3}dOCJi%D*>ylap4Yw z%m81Yk?A&*ZvW^h6xNK+H`culr`l#v9kO4QpRm385bk02Z$?M4u}@~cnG9boTkrKN zi{ofTlypB+D;06{p-;|mj6U>&z?y8uGIYpapBU*@w^1hQzvGxf+siEA9YO3!;(sDg z+U_su{RO#Aq~;%!2jse~;024@QQ@>FL%O0ZmDZkym#ufT8Tj)`+~M+I+WQGOdfOgn zkoaO)-3xqr7l=4myIHmjYKuB83cJQV%~wdi;|v;;`KTar)T~K??=+lE9q9`t$5Gt zA2>rW4$xaEM^|}VG58~oSBCDG-$%b+AwQGoBrjiucSL8ik6k&2Aa^@FU6mQ)`Ru8M zZgHGmr5n=wT$&Bw+l6kX`DHiZ_T0Dlm%dVO20w0h%K3Jt_q~qX&TxL;6+r`qLzh2# z-)}|A@n+pINz@N!yGkkead(LQ^yNUSVKz$h_>n;agV33(R43t)Kw;Y6)e=1!EGv|f zB)lz(jEM9e>0&qMK=vxqMSf34r{*FDkf6eR+hOAe1G5M=PsuQFx{ZJcDVrORR$gZr zNr9&_5az`;BS#k4*K0jym(@Zi%QO$xm|SPhCUr_%|GSjKrTIO4nL@CKP6^i%ww$9* zBIyUIvQfZP-pe+0n~L=AP0sk|5Gkpp$s(BQO1e6;|KL>m8d$F}dJyowbP3_UhCmiV zHTAdiZ-%}E0g;u)k(D0o_~`UgnnqAdj8iP*3Ql7TcUJ~oqREDQDxVx(%{@n*(x=Ep zVc~6}?mr87N;|9-mJ=KdFxRAtTgctN!~d*u^QV%aoC-io5BRC;qlG#^aYreF3bU}* z=q&aB82bvCJkxI5;_iI7ySrO)m*Nh^-QC^Y-JRm@?rud26xZVJTxRCTKXYa#H{p9B zAt8Bq_PcfMwJg5{t3>N{j@lG+FILiO4injeDw9zcs!e(QG8R{5EpHTYGG;7Q;>M?M zDF~v3HS>?5RGLIBR|F!n5em=>XRY(IUJh-ngF-SB``9Ed(GoznWYtC_Iz$VOfhK`C zMq~t$XeF?27fIcBBgSuvT4U-LXNw+}-mPSgs=(|=9)I9E_++JFva-2mqONzBX-1vQ zSk_~BKPszH8=-PD8@}GKZLVPp_rpjRX?qNKdkA=&7HmLl%CChjrbU3Tsb&=e9k5wH z{b)i|rCeT*bWXkviWGxr_pyc{m=K9vIj)nvyAGvOZTJZj+CyCN|I(bz$a7@t4Gvj)i2j1;^ zG)_=+#GLu$1_u=9Pd9KQuL_2#d0Sqi?C^xJ?HZ&O8Cjpau9g*FzR4y+V_f&1jvV!S zQiO~V#&Kf(FS4C_C$Nk4sU15ceI_+|F_kYYY}o_l%*zW0^v&L~1*arzDOm$XZ_e9W z-^Vz?OE}L{2e>6{k}7vXrSs;xjgU2;Wr>);**V{)hM+9Dp<=ot!n5>znpv0n->7NJ zsZM-SL>~L!zR^e)%r`%IOjLHF*$KYBLs%$ruYd35vQORo{y@;QYP~G8gjX2G<6cbq05K28R~~pIsX` zQR-f>&w07ISs%imRMp1{d9CJe37)VG)LmM_8^ArJ*8g`FG(OM9ZA!o&nc zOnxFaDk4j%VPs-pVqokk5r_W0r+UuEk!C(V|J#}T>!GRt z{m}p0NB-m7UVkX8WLXK#B`12v0-NR93fRLB^4bbymd`mKsM?}-tNB!@@p8-Vz1_CZN<;P&?T>lr~UNXT_DAOMdCz>uhZr&5qWH)$hRVk(&L z#UCKsBcG^NqOU&2GTc&gyhn0Z6utUbJ)8q(Y5Fee;imqTEElB{+XpankHNPG?_~{B z9<8B-lBwSd>>nN;t|VpZP12y*UCtmG>C^*eyfD{RORcA*@C(E)Y&$B zcpr?~wHtuR!vM5G^cK!p{GiajN;YCe7%LtPyUL$#TTxM4u~a})NHPGdyR}FZ3scK1 zWLTXI!EXos!dxBny=i|wbBYd3sJ)_ft~~v;BH~pn*i*ygJ*QnZClrJ#E(Yb z11alv0+?l;!mBNNf3rhq#2Vn+`tNx#0ua6z@Q#=sHH({EJK$?ZC;enQyvydc8FmoP znCI-bKqriRz9Eq@SU7!rJEdm{0mC;se&pT1&t-lQC~q}=^fKcAwZro-{fz$=-}%E7 zJ~XO5QJ37_eZD0bH)27lY-O){xYY2pQt^6GasUeXmN@ChQU0 zCJsyB;InAvkl0~y-d!LY^o1vAs!i~XX^_n*G{YX2$z&7DS`OP8`uO>^i@m;JMacg} z!~5`q$mZPZ{?g}p4quKhfM;P2#QS96qZbeo%>F_#O#j;PLjAez!kCcjG8wizd5fK? z<8;{OGJ4`oF?eJ2qAGafnGx)rl>Ys6_`Ptch#VJS{0mXF;_iY{6!1ijBr6rQAVB7N-Z8y5e@^<5XIxGo zeG=rSo?YphoP%iA9+-d>pkh897!G8XNv0c+5Un#6ak z?+ElV(}4;Z~1-@Y+xC>6}b8hW>elB79xzQ_$IfGjgUuMd6VDOIl?j|X>#UtzzuhXFMPWx+5bF;ErDr>ZKZjWo>95Uh zYbwMHyR9HY72e_H4VIv=3D4kl+Iq7!sg@s(pmtd*n!09t2QS!Q;d0B1$aVRFuhG}X z@Y(!?IiHi#l9AYsIUW_-t(GnHm*QK@FyWSA@g#3ag|BcY-B8jvp6k zLj8PlMgPT6M2XC-0siESY25CaF!HrttT}jP@x_|nJ)tvhfF%0(NG}ij4zsrK$}v0v zr4Q)~W4tw4lW!oYveY!Sn3rG=^!rL{bm0J{N@QWGMv)Pg&6KbY20UUj^ z@uFPJsB_hX6_-c-3fs&EK6_Be2Fue9o;#CEF{QMGSC$(hYJWe<}F z{_Yn98TB#!!y=O`Z3P{jRqD#RdF52qMHrX5R?(ADwO-DaO(8O9t$IEsvo_yqdK$Y) zf3nT2Sj0J>s~?_W&X&%iL|2`k7!J3wPuf|T1k$uVBDHM;DdQoD~O3( zJ~cs(Dts?YFs)p6V%L3bcPhz9wq^IcJ{mn$G`=M8D@!{L>q!hrJQUz?Nqu&M51tm7 zZb%gy;sSh zlw6fL2$<)YUOI;UHjgxW4Ff;42rSR!-Jb0-eX0}=@M3idR6oCr5%&|z_I}++P^QG< zk}GdpE0Gg@9V(a2ilMDirWljm$rCE(uqC4D%9q@cBzA$3K!~1*%h@1Cbk*@G_T)Gr z31ge8xCE5#Ris*RXYw?kSi_@%^TB)2+ z&~oIqmdZlhS;G$-GVTsh0SV3pVg-Cz`U5JsgQ=pgT&JXW<5CleXo`ZvS@};DVdaIQ z(ysjZ0#ZX(UVPDVhYc~{PCw)z9M__`IYR4VYt{C0W!dLsU@zYT+sfxZ6g@Nyp#&FJ|;&$yKm`+wm#Y(*tZ{ z?r?|8%fD09C0yjQM9$o$gy&dzi;;>JjiO!c#NABHWm|O0(L7hzgkCWIHfz+`+!3=Y z>mDUJOpCCYW3%l2Oefd67&qp$QDLX-JxqOAok53lA((CXsI*jT8SUODu)}ejb1UaG z$(_um^qw0n*4SrZTfvi|60qd5dFpa|{d)=DoEhhm4L{+zuMdcaI1G<$-?)SvNU)Ol zv^0`Hm_x`mQx(I^)a@x)(w2y|$tp)zS{TU_CyMiPOE)FErpcmSr7ev$xa~I{F-Zmu zg1=`n^%9_Og!#FnS<_&ORgh%_RJ*jo4^MI}k@2y;<7l=tJB#US|1!{C+-{}aY4Fiy zvYwpxH1Aa-%eX3g&4$0KR3<~tM6|Li9L*LDa|K6$Z5#WBMc!IuXz{~Bxb=v9IbE+v zT`!TGjSg|Qw)ZPdF5M>uYe?1ok>C@2v}D;}r)xFOJ|j+P*X*r*BV0~r7$U8fd)rN| z?0|*4>%yWMANLiPLGwl0ZiaZO%LMHDkoWrB06pg9xVV*N=2ubt8VjFqFf`NI7`Juc zcYapZ{8`$W3f*+JU8FlyUp=8|H5Esd#*d7YTR;80(E^&DLo4}?e>aMUohOU`{sliq z^3w>fuhbKN1^rLJkxKVqDKS7@ga)PwP3=<>+>f8NML;k!VRVSF(jmIBV7||wLzmZV z0`@Po(2wYd6dzKfRH;*L*N}LMa=}{1qp@FjAr`LLfP9Xb8%uAw0lGPkU*6u#NWb%BuLn(ExUzS@;$F%3 zOh@I(qXu^L&lp|=m50bI3dKLn(a(xxW;tf6UMw zmFI|IBu5E#wslE#$n!?pVVNVi*bb`8kBO(3))fl<0!U`is2VCzn_}36hRp+t+0o&% z3nGQneD)?i6*Y2FV6JT!n;aN(Gbcm4DPIA3U|tk)^q5&ZeSSIHG3n=Yg4w6ugBIqT zl$VvFY$gaXFQEHAq}GU1@k2u86qV|dbz##?E7YV0ep(MCTeVypOeWAy$-?&cuP=1d zip=s%x|71Az^o{wtVO#*$?={ZmsD=xG~C{N`3Wph+cg?ZPSu_Zi#3|+TJ^W2$t&Usy+mgq z?>^&47q$&KJ52B3#s|*hOYn@n6w}`7nPB!=zvdc{?)|lAxQu-(l^0y2HgD9h@aQ9O z+<7>zVr)lg_L1Ue2ctHDVaEWfW)@Zt-}>oRJ#SX-z>qZTt@?*dT(MKC zD&L^Iom4wC0k)o#Xhn*1j6ob7Dx5(rMnoT;ZM6bni7SZx2fY1luc9Dp`PxPT&+O~| zZCj+zdl@lds5wI&c3fa}vS3x$d}D_~X)r{w8GtuZp#h|gsWcDmqS3=T?a7(OF9Cow5_aqiEBf;| zZNS8|OK7VURg|8*Vq+m0Ow9v}aq-@qWte)`nuR0p?h{XvGt)c6{x#T>4rxZnqg-!- z)`rArg2G*m!N@#K0fh&MgSI`|k_Nna+^|C_&j4P+k(m;_!6+gf(m~9ofAR6b7bi%E za=C(XP9+CmDNOB)E~LPPuK3v7p*^E8M7(BI@v9Jbc|oL!{PogZi$|vLE&XrZ8c5fe{Qa6aM;=j|{mT!G zowD;M2|-!zab;`RMq)KG)+ltQ{wubo%Tvf1+^U(hEj0%WV$OJy*ff&+J?VJ)Gnf?# zWR3_=E}{ga1tZC82B~Xx)VTu(k}$EVDIUq$_uBPF-Q7*~uH+Dyl^EJalNDZvAzP%S zJ3Ux@H-o4c^WmLJI0Z2;VOgfc7KS&9`%t}WD`)7nFVPzQ8h}Gof}yZi5We?{b?;6e-s;BNe|2CdRNhxl^@^w8 z%r_265Cao5k$tct!TQ`LIfSO4p>{EiN7D#-P?ywxTE~VsT-riB#Owvytcc zzSWN)OG5MqnbpFvaoa{%nU|%GC?j*EG`5&)U5d(N@18ezIB$1c#lsSh54?#A#m&c4dVAnKiR za=%TxFwCCj%X{Z-{D#4bcv*+!=TreNWfZrd z3`>f^Fg){G$jY3^%3JuB!b(BsrK0FPE|^%4m71m{wrmopw z6c;tqJQ2AbXvCXkN+*Jgb|Knn>-*3gXYcZ?0{8*N=ieR>Hghhf*>mSfv4eKrgvlS^;RA+pKIw_J4C&{E5G*{Cpv)3H*8Wtqo;acBJ%Y zwhSG#cSs)sW|s!4zU(vJ{ZC4*>8WSi`B*`oUb0|#c0-Li5S?jf=byK!TdhEQ*;&y$ zV-Ip)i`$)2t}!j5Fjx+NorBW+&!@_sVsKjOGgd-3lMbT0kWNNWePRu4K1X~!&qbp< zGqlY#jz9&Aa9#KwaTjH$&@yZ&n{~M69HHHCCwjQUUrdrJbd>wn;BK@-oPmB_qr7DN zP#tRqYemq!SHa)FRtPmufEL@ITADH_z2uAfK3o%y&&_=mO|<3=n-cC1Rs`$9{`mg;4#XX3`7JThr%x?p|JH{4M}Xjm_|hNk$Ny@& zs7yMn3Zwe`is7iuYLLkIK#L$#Sm7S@Q_4`177@x=L9#xQS!o_d_$dp~!n_`E(}+aLx* zEk_9f7t&Gi*TNX)o|r|@7GYQp=YgO$oeU7d%jxG11XMcP3U6G=*f{;!`r3gsV}(agw`yFJ}vw73y}Dm{}2UAxeB~I-vkQ zmeh^pnv*>vp;@@WALI-tCh{oUIx1yls!>Fp%HItaQA~cxD$^T@RGMloL)r~y*3$%2 zMU0_;`=)3wG+U6`tnOcr6;VG?AB`n3=;&;(`Xz&ryv#*_~_krh}yqrpx z43lm}nm(qgRlqQ%_1i;SFM-?c=cYXf(O)WX0i&^FYA)oS6*KQ~@MQLEPAoXa_Ddxr z@Ayo1Y{{i@nrXTNg6yX18%4L3%R82BMvLc5b~JiqjhW6|I}XC_8L8W4n<`QSLwYfd zbjrfVtQi@W9Dp?go+U6byHX?iGYRmi^HL*mkugn+EZ){VJl&lq&LG0umXrmO0BJ#F z6@y$-fe=YpK52iTb5gx%TCz@!lyO@)h~IonnEhXWXF1Im>8_7GipK9queYpbKY`EhO#2lM!xJRF0rf#3N|YcFJn@DL+QKdYg>Jh;EujiV3DOtZ z*}z8coQ1;qd3dc25@h-&9QyL!7N|hxKx`%MTu}{!5`=8xYH{}Wf?MXn1aAcqYF$>? zF8ZYozSrOn01)u$2N*JU?1AYyJViG}kaCuQA38t}T3?J>Hu)njz3@Y^afpr{ng}n1 zKzArEUX6XvQG>H>LmMd2R(#c#5l_ruhumn$20YO~JwKj5tum$yQ>V*)e|w6qdNMKW zT|wEYZ=)Z!yyNIk=U%siFGFM}lCLG|2CL$HFg~W#!pDh@4`(w6X$cbk6g-jUD8`AD zc!NjmRk0Gz!+S?sdRIP6Suw_0HGRT#{18NTjV!3TWVHlabzNh$CadES*;i3($u+TV5BQ+1ht>Gzu1FS{xTbOPM5 zvm3`5x_OQ}65;w8aAjQdW%?N&o;$Jh+p21s-8ltuQL_ia9 zo?h*Fo({O|e0Ji8ScLWlE2;dnz4|Hi$Du}m6VgBo5aI*eFd^G!736lwr_juK?)%Ly z$nB+1p)#4jV7K#Jb6@E&Oc4e_PGg!Neh@R}dI2PF9jsv>mWnQF-fdS65mZ;H*X<0zc7m zKt(-6(V8U$laS?+`8A-B@#HO!X{6m&iM7K!29tXuAoK_^gD}5*@<#1Xa_(e|K1qbA zZkXdWneMz7-|2pT+@UwLgVE*0=ueQZ_}b`O0$*7PxO4;h1%Kt5kB<$HV~O^Kb~6IF z3F9iJh`s<2Z0Y8mL)eVR0@}ZqelsXr;zY3k<9#?g=1N=YV3R{#wvjNXwU9{IFG%~v zI;h#Llafkj^XKQPKr8i?mDe`3mZ~ov`M7~h&6!IVEuEhX|=%Gq_Tvmrm1k1`;4 zeSvKrcP8_AsYBDcQMjpm2Q#)cY#|iNk_b?sw3+r9X4nox*dRgw2{j!xC0Da(4%$FG)~xdU z@in#4RP7+n*2xOaBuk9MNgQ==?~T0r=<4O`X#pxWd)%?bct0@fR^a4ghX~o@5OBQ< zm8go_x|$J&(-sB1e#<475^M@ZOecZ3Ls~7_deL+Q{wUe*iT?75aMUJ5&K4D8g-hZb z9+O=IuLuoUvn)yHSn-Bg+;<|(hXK1&8oshlRM1vuk|Z??mbOaPVnbJ!irRzbHq-g* zjuaB*-O;-5K$O8eVw2=fem*4YvK6V({nxn#$N5yZquC8OKS{I9p;9hHfmTU9!_=8} zFA}N?a^g@>ld`wQ)GP0T&ZMDO*QmGX&KMJQ@8OTGFg~B+;h~`PPZH7{2FWgG$wHVk zYZ$5F+5)XqF+JzeLCZCi77?EDZwxuEebUN{I~JWDzfsAHo8_ppg}%do9w5ldy9ViZhbJq)zb0;hI#D!W;PnU<6MZp3SKTtP03^#S?D+gXMf`fIAd1-Z!WM+)KNxRW=ph!=+m+Y_Fs238%x?NCGqcPX7LM}Vv}>ic zT!6e#7~C=lphzDUKL4yLSA2bVdV3JYLoY>Bcg8*8TqZ}gUQios4)i>&kg36HBZt~6 z#u`_-T1(n0I0VNjV`-%KZ-Pu(F%h7~D-X3@TiEIWpll%&VITH>ABiFxvmzC^KrL4b zUIJZ3a2yC-M~F8ac`|1LEm<^k)p2jwka-H!D(Ov`^=jI!h1pI0;yEL6IPPI2OZ(2& zg%gI}d&JENRYb49Lfz{x!=YtM+?Ot(U9@iYNo?e@J5{e}9^40>J0(f&^ojd0N(LUj zI*3JW5V^X@3@BONkhEfA^V%`S?P3!_oVlg}gXa-lYfuB+eFbE{XCAdeE&sbPYLLakDfj=$b{PkA;alS8S>u6?U{ik)2znSm9 z4eZ8G)D6Tdz5B;cymaV`f*Zq4GW^HPKJ#PPPZ(M#rmrI@YaXYgqOVRw9H8JW-{!3^ zpl>f)?k(%xCXwuICs526E-4BQ6sz$&Ccx{xuwr2snN1X26r2nk@v9+_#!wHiK4T5A z1Q|HnM~s1htj1d`4gavI+>g&c#~iAEvO0y|cNLB76`gEtZS4O`ozN<#7uug6G5DQw zULh2|Qvmnoi^mb7D=ZhLfZ+Wkn&N=A%IOx@%_m-^LSfABTHtSw?>5HP_u~NViA^;O zwHl~?wM6bX{2(;zV=L$j6lW{4GMs^%`8z2=TLzYB+xSSrT;2FpkNS6Kc(E^hvB)cx~b+~3^y*3AO)?^urf+y(!a z?TPr8{Eu6cf&0JMjn{2|X)nj3#YkAK-p3-5(K72a&az0@ZOF3Ch0F!RkGTqUElpgU z0PZ)p)Uglv20zw~1M>CU?Q24vufJs^Y$U8SEJ4pmAEe`Fn9#ukibGzyijmsI=%AdC7w-4Uxd-c zN#FJ0mx1S7=3qfZXVCr*e5b*T-gbvP*+(~N%+(Ytyc|2g5r-67Ji{@wr7ZKEN1O|d z(cEqrWMSKy3B`~`klr4fS3I2X94x1q%L|fgqe?ff&RGOW|K!}cCO`r}ngY8YhEv+) z3NTAZlE80A_Z)$`P`F7nRjd=zrT|t;9YleVs*x;Juc_^sF&*_oxyrLe282Z?h=&;B zoH;_+ofMuPX3}jKsm(0tOtkyCU{O8l;KvzPrfh1MZc_FS`AmoDxwUCyf(kOyEWTKT zq0}wF&7~=qP*ts5u|}!o2nG_JL499ZyP35ORETF;!)AHG#v;}`nJr?@%KEP(#N86UW`ZU5QH#-Lz(_UKWdGv^)33gwTTI=4x z{3GJb1eGuPD3YZ$WloKlUsQ3neKOh6rj)rwXbF!7}w+ep7z>;0K!{RemGaptMF2uLhFC3T_iJui1ml|Q& z?J@A>*Ld7lQ_&-}j_giewBuNO6D2z$&fOIx)v~E^eOkFW%Ko2JJC5M~iPY<;uT2OMB z7n?_yl$g7io(-K^L4(>MT&IM2D)XOZvkFh4^az6P^gZQ1K?Qt%2tOttqgE?vku{z9 zZka2_OPHQ$MSIcWuhOe3owKab551BkSvqKR+JZ&a;>vP0eYO||vziaMM z?})@-eK={Se>I}NRYd!(PWn6Ek3U*qQA(={C<2JQ34xZ75%QqG{?IxOKq_DWASB4# zuN_E%bvH&(LTXeeg>2ZgUh~}-FrMG31TK5~&?~N}Uy#SM*I5O$S(M9Gj?&KNy<}bX zS7YRIkc2;5i_J#*iI&dn+6$zk0~{rH6i@>&6l`0ekhH?Kse&_X;fSF{0g;p~Q?@M= zv_kZBcHb-^uea7%Kr&2Bry%Pse-(EYqEmNlISj{~(^oEo1dDpCBTwHX5t*2dTOZ0f zRhDLeSzRt&QV5Y$uXYer9KdR;>j79g3lElBRZxTS;AnTW;|*rENrMYw?zeU=#~EN8 z(eaq2`|(T;#UF|#=okZ;^plQ5jo=bCDlMyQEoLh#NU5)cCCSYVJwgaQ)+v|^-|5P< zn9N5U_S0+FoHBNEnZRG`-vg3qDtNd0C3F}f1ZZtNYK!%G;&}LnlV77j>j=J^OObfp zLdPZhZ*_cbub@C}0YxbRjT00VKqF;^3I1F*;c~o zZwXgvC^Hq)8ibQBJ>)O(mjEb^Fx)JqFso7~1sZ)z9g6f<2e8{o^=5{FPDMJ1q6lq(UvUQ0X|#`~7hQkyDkwwvT6Tj5e2%lR(}0)H zj&ws6%-}0($|0cA{wpfs5GXTiq7P?62jLp&ab!dd!2G>A=qp@H5P<-KDP%vWXB2W2 z$0o0}8h~ftZ#;_x$#yRDYL3REXxJ$I1~0mBZWZ!0-_KhU@;%Kuev#6>jTw}+K1Qk| zoi2L5Fm~4-+@zBv-5|ld>>jeCQZP8H-wi_43702TC$ry;-ZkVwTyz{2=PevFxk$zq zVg0HeZclatU0A1})doEdmxal2UN#`8(8aS!N?B z6^ru6+lmA6Z&~Kws%!n}js5R*^N-i{Z!)Xs#FyNKj>H8bQE=M-1joik=D%DG6lN+} zOq!Bx{TEdBF9|kdi+QMenTR_9Z6vJuqsY-y7+BaSX4wM-4n64A(c% zvp~RF8=Cq#HXt1N>QDb-*dGseV~vK_BZNs<+wX5imNwxz*ztK9T@;eRg~4GHIT>mx zW#KR+5;do_rB+W!&ErizLwH1Ch!UnKL7T3e38nL_%Sg!w6WfNOuN!&o3p?4qP;qL_ zu~T0$O=DSeYloB|1MCYn656^ng*wSL>K@J#3UIF=`FQaiGPbE#q(lZ{Cf)GO@17c; z0;jY1(FCJdTWs=~$Z{CWS&A z@JT$wTUKAEt;8_4jgzczi zjiRp-W^Shi4~XXXCs+ZE%5f1BcN302>ps&b`_I$Ux=5n*#RVML0X<(~?J^^_Jz}#^vD>(ZZ-OlJm*OcoiZ0&zIt1t}( z$LS_vj_Y7*jia(le;)2IXp3ha9NG&ez7D2N0X1jCso#Xgk#rML7u6$m@UV*#3~qtf zOZio~mfy3`hrzwDs|pulAA&aYmN(HBsB=j%xQ^;0MNmGc3AW91@MId)(uxBSbh>8* zJsD>tab*hS>yZstbvjueZP2mF)Q09?2bFM4g9Qu$+-y+qJUTiqk)YYuU1#BSLOX-D z{4*`y$jzDyev<&90Qk~pT5Z^yGVZyGmgROH~RI*b*Io5_1hesvUa{Y^@#W3G+EvY3hDSn)MC~7xh7KX5ZkkC zf~$}Tn1uEF#O53GDv*;bXT)NgsK>Jc*Rl;1iO_?3IC%&1{G{e0{-VJ#ik6|TbNF9Y zU!i7c7hXw+7Ie^&=EE0H9-mm6;yFY~$eJDpuy20Wt7e9A&)_-P5To&*lCF3G7F^S? zN|GC%7f|?Ee=U`jae>&rmiF_5$x`pIWEK`PR2j(CO~;*FD;v6;sNXS+b}5KB55JF3 z#h=Vl)p6fz?ScY0)r&@CGw;geR*&3lRCY^McB>ZM@;XlP zu31n8z(_D#!@%`d>gKLP$B<}lSejBAdc?P$-Q&#JEy)8J<=G>4sXSndan#=>b zzAj2TD9rgX_c?sce+=ep9@yHOQjXA5AKROsG@cf}9b_$^qKRfBOEe*~*v ze+pKAWr0HfJKycBXK$wWuRKtH^#Fg{S^4A72pd_u30PYGCpkuxyo}rj;X#&q7pVLN zNcTl<905iV`i?;45F{B{6dr>X53P|RnuU}k;nrujnS?M1AK%Yo9IGn%eST!?Xp} z258EhNj4(%mM+D41?Hc%6c2f<9YRjpPwZ5&DMe|>EEhgAmk6~FUwg?ug=y^|1j3*!loYkNSwU5?cR zVM4Tl5u)lJoGT3RmNRcNNEa?c2s%|BXCFYN5?Rj3%h6)V^s|7(^2OT9u~S-ToI?!4 z5Y$V4B-|~*!&K5k$LRd_l3b0wJm2jRw*X_|HyRiz@2p}GOl+yD9`nP4 zKq5u+lgAw6^(8HsaswG~&c4JbC!gla-^owt_C>AQ|u|HIo+SV z3B%2Hoick3p}DGlwfJRtjvc28%J$RZLaiAaqz^`dEB06Lo7byRbkHWNu!DiaaMzK_ zXmYVSP+!oN-0|ny3_4-?Ir_&pWKe~1`iWnvBZB}lPTzt6)zTqWvh(^~`u|#zJ{pzO zPHWQCTxQgqYc6cPvk%BcmP$4N8%NE`qLP0K*Hi{R^4}{xm|U;BYBpL?ndq++Zy!iA zbjhsEwPpoHlXKU<70UG#`*Alcv(%(~8)o!PEW0GL{OZW6GS5FM)|#e$s@kFv1fn(% zdae#xjTW;sA6-qU&bMoi?KNDVz;ELH!lcCpJea}qE8+pm8e^>94iBAaDtnEup5JUj zMt^Gr_}O;0-!l%kPdF7AS&We#>g1)VT72gq8a_SlI0qJ&Z41(>lkO;$RpcHbME8;W zud5ZsC}JG`aI!14a_yz9v<^MhOteXaQm2j)epq6L83;e`IwJ7|-Wh`&Qi)UW_tJU2 z;7!Wd4)I0gLC86GYkVT~b)j90RCd9-oGy$_N+TX&XbTL|aZ@n^*qy>M&XzR+jp7Q( zBVp?k$Z$M&cEsTbM7>hn?*f}B*9CH*s*M!{@|(_TWmQs`7jT)MZ2VJ=~YZKPgA z^c#C+g%lv3Lo{W}HdOHoE`qON1HW)|PlZ;6BG-52nyM8;*2rT?9;3Mk%2ti^H3+-& zKZo3>3Ps(`91|v-$EQIeRlWXBJ=HFJM`8KVrf~-Sw|xC?Ia~fPY0(dB*4Hzz_`hsP zNh+ESipr>O&a1m5M`nJ45daFxuU7Md9iIdvOf@t@1JPC$s=(yp*KZQ%GhN*k=%8H% z_}~4Q`5w13n5V?~)AVix(k^?+eZs1zN-a(u;)BUVP70sv?wS{ytv$%QUtf0&SoGPu zLUSP~;mCcPEuV=Q#jFQmM1HT>i#|t{fw_NJQKmT zakVLet}#g92xf3;h;}_$-Tt{Q68s@kZLluQb?~`4$?N zm}z!a;NS>pZu7h}Gs~;{}VCPh7MiD@Z2gM$r@#Ze8U>MK(ii;-KY9rr;pXB`s3 zE=z576Fm>9dg>?Vr8~=0jKn#Oi4I$&N5zS%z?vQwNuBkCcjTszEmN#|yOnWvlSchX zC}2ws*SqrkOa=Tt)#&v`yAGF>ON`g8c~ql^-y@Z%ge1u3(e~!z;}VjTl)phia+y|t zlOd!Oq<15tWO!Q}XtbjRNCU8sBI(9Ez9AFRJh5{!p+TK^!t>fUM`K%7rcr%4L-VWR zVlBNv?q^5zRJ&adS1=r-+7-g;w7Q=Ax9`^P$9ewclJcP zQo4R`(#ozd=_!`=aZ6B)qvyoW)34;k2S$!lO*GHY&-urgPKLeC;!X@>6FCnfyRP{D(OiYnv61HsVT|-ozhx2v_!==m0Wh^gTj7m9CZO$vH(bJf# zr?c&J0k6&aB4L|_=#@jR(^KZ+@9!-Sd*Nkdsp9lsCP!l0v|k!DW{3e8rAw2nMAh}B zkK7iPF*De4_20jxCY-g@idVLmH7|lvhG?y7!dO<ZBZp$z3V#xut&LBYQUQtVn4~ir6?F5%Ey3f=s78zJg!BphNQL_Gv1tuM&76Nf&HXo>ZW<lrzq*n?*fa`chRGEn%y>_6vN{J z32Tav-#}9l%I^)?pQX`^B@xj*)b_+fI6Ig14stSZ?wq&}xv#Mz))^59QxF}TGMX^H zGYo{I0_X5T+!MmJ5#NSl13^juNVO`DD>@fBQhAVoiV=qdOYRaCiG3C)`WC?wg|wkc zc7VgkN)R*5ttT8`@9H|135U!Ef!YznX3I(vMv;UE`Vx?3%EAu&66vH%^n&*N%~2$s!-h45CiP*Cz_+D1R7>MGH#p! zXqo+VPb-TdFpg|em>r=gEyAZpn~Y4Z{xolex|TkADv+t=Xv5;ypk0FuPJ*$=m)xQh z=Ri~Tt$Ynvzl5l}KfWFJeN_`jwt1pl1oiYtHluk1wGN)=lK)tk)EQVktA z54A$gzgi-bV!9|{1>-|&~_jJ}X6LZY_^MP)l9XtRKkb2tBK_`AyjMG)LX$9y^P1wZ+ z!vx4rh}y|txhPNU@KSctkNSs8sVQCf`I7>K)S|bA!tB)JEBs?f-qnzW1_2cmYRb0C zz2vaG%5o2|G~LTPKD|nMngmAwW!y_Bvvxr<-%6)vx~6jX zVAMXu;GNk$zEm|g)R%vTEF<|MK3U(y8N_Xm8>M&|)xUuW&-BIKs-CS7xU3;Tzl)L16rYbsG z%BWr{52Xn+vRFTf0v1{9%mvSsxtdV4r^|YKgZ7dwTw?hAbH>PHpR-iTv8HAnkSIFu z(jHn+!bNA`-tyWlO0nEU&Nefc8yxm>V!$-3ryo6FZRP?X9o*|Ra9g)&9Jh6HnWUa4 zIsS@DkwOmU7HwISRg@QsDv+J&0*9lCB`HoxVNtUezeVTtmBYakV0!fs#eB2c2!9}8 zWA)KG3P6iaO%^7s9#u*qi%1pFf}NMxu?1r)BQ95wH`P8tMD6DfdsbJVL5=WVI>d=| z;yOtk#j$VFDE;ClXODKObXAJ0E{!uPm&UD3XWq7zbT~Ytxf{fF8E0*$kX<)Q zyy8~wA*5dv4-u0{eI6q1(%s3rk%+F-t&Hxk;Zd!O0RCf6A=m3pr|AC4)aK`Nn-k1n zdEn`R=ll;H^y0y|Tx?H7{?Io^BErv(Jgeyv9iM{HCC27Cq^!Qve;h&aQP#^EyfNoqD^*=MdHnw)1d zhrAzIUlu-!nwUQo!hiJ;e~T~v zBMbcS5VHS6K$@lUtcoIr`WCzt>;xq8r6#nt5vz~539wqe{Pamgu!hHSR==HOzNY15 zXcD|O=3`#8>~e~5<0flG`h};2x8!Znr|jV7CgTHjyN56-VSKjs;VC>fI++u?-?d*s zk`9N&xZt;xXn)vgu$r*k9C1V}IVx>A6>*kqbiMkHVwH*Y>&lfD8F5zP&7qdVkUhq4h#DT}62gIw~-qEmq$E_yCXANP5Kp z;#U}nv`gBhE!m7E8Meb!X!ljex!T#x9ZiQi&$h7~d7KazUEU>Am>|u)ZLpLoq8vqp7CmCJ{T+VX=BMFtgdi|urIkK}#0h*y za2pgWazXV#TaivnJ)Jm=kHT;*HFCqP3-$?j>UnF;$N>XmcbPFqhL?YDBt ze49rA=%O45}yvNcT@U|faAl-N^4sDt<3kxfynBiH^_XsBi+g;y0p5A^oKg!J%kk|=r* znwh29rFwUlD1Dz);+G`6g3PH{8shB=>LWp~L43)dGQVjuAIDd?;PKv<=e($ z_~>1pinl;*yZ-aspKS}lA1$Ekz)h}gByO2z17a|F``IJh%Q`iqNaoyJ9X^@!bV z(!OE38|uOz9+)UT3B%v&F)rwF3h@(%{rD zNi{A&(GnCkY+H=E$av-FUD)E&&L6Le@Zs$$6=cajl;GBO6iOv+RTY*b6>a<4EOQxC zz6tG=MsA!GK{>F9dApeDotNBFDcS3WIiKM}TFb5yN)nPT#rn%paV}67)xw#wwP>Fe zO}X?`)n06fNEDT(Onq1f^@aa~$?OtrKwYb2M4^gQ^UyX7+=NFD3U_DYwu=g$s1)gs zXqOiJhVz46OU+alM|U#1CeaM=2z=%(u8`R2N*E^DmzG@sz8)$-;R-*V(N~LM%W)xi z^5)VZqFuX}q$@D%2hwASEdNI-+b(nyS`H`M|b z`q;lPXW%H${-qY4L5m|`bvOc!Xho_|?au}fDWzeeH!w%9Hva9TD;h@&}gi;Y(*P`&A3mL-|**w<*$bEx>VsM`n2V6xR zyS8DM2BzC-CkI(24Lx5UAE2lQsbjgQ#8u{fr8th5hYF+mFe@e;bZW>JsTQ01<}J7( zHvsDGdR$H|80)OOqEYJ3RvK=A&jh^o!gf;@tu30jo8?C1;hr_FNssaDNf3Fz2N+9= zo{{+03mb<}2`TqyX=g)UV9B#+oNGJnPF;kRD}gbtU2#9Hg5f9|mkP=4tliSHb~i;h zA^fQ3&S7*c8gZMcs)br=@=bfc6FSu#bZAOSgcr3;*oWjxz0w-8aV5e zF;*bsC-9YpG*)YvHC79Zw81(*_&V&ji6CH3X;^NHTvVM4_m5rgqqAim0}OK?K6pR8 z@N0NAoh1Bs%XukTdz){K>_t!5hGn1%Ru%ji-C~S#Fv;8(tfXuMg@1Kh9Q;;wDkIoY(8#Ji#I6pZX;#4;-CMbESy~ni( z{1|yfOq%jO%PXW*-na}uOWyJlmy9Z-w~u^;+^DQlwd^gHk4-}mCF1BQPKO3o2ZLOZ zI*9f35y|O^_q6DCiv2>O_7@|!`V^KJ+s#arC0F90#s!;a{cxnZc&WyfFs_m{-VJFC zy==p@UeSgi1$ivRo`Y;=B=0=9j^{Kg=ZM8&8z+LlMl4qhUlR+S)T%5>(@x@I3sO_G z(gk!tMsqFbgM>w8J}Th-u^$Unwl5`~$Ah90u(7PsrWLuedLjNn4u=Al z#J>-ITYzv71*xhzr)_F#cH(X7`n+=UO>GGVqI9KGazBF#b4dRDreX!kcgkO5R}TEv%e6^*7DrO1_}x2pKP z{q@5=Y5-iLPww+G#50?Eh*HLRtPbP-h3~&Oxv3H$_P5`IB)0#Ay8o|7C+B}1M&D+b zb_T}ZbP4(YFp&26-z`tF6vrfi1rWTG1|x?{9d@F^_klRk5ef5icP-?@pxrM?u2u~) z;j+SZ%{&kU`rvO12G1F6F+{kXUDTF-*P#75d;3I`s3gb{z$${?F(Z5P2bS5V0N61tlX z_lc-oAI?`pRqKZ}Im|}s>UUf6lxfR;_O=MGFTMy@sfKR8G9Ca4x~}mO9PS*@ye=%l zTZncZNIc4}XPW-x94xO&6#>f%dgHs|j&OBGSpIBa6r{_hN65RuODwO)K{VBjeo6yi zeo|T>;PBQyt&zP>*)&Ymw+wtu96L_9)y4GEpg2akJA)od!zw4V$ z8t{#<<)M4ablLEt$>9T?VQ`}(m*^$Pf=fIWgU2(v_#{$BHzE3S1pgDG) zJjkNjE;1r_#M-7k_4P1|6m>?J%P#^NPC zG~$9YM6T?mI4pO@4v?#OjqLPN6+`AN*?Xve1@(B#2bg_J3e7-{rm%3=98kZO18OYX z1&UWxymSWGn7x!}e5D3jOrE$n(Qb86ca-kDL6;<`k*ZmWe<$o_bWaVb~u(W8C`qZSL&l9m-gjMg)CPUez~8`9HebG9V1jy9MxZXG zJgj1_y0WUS)XthJfh2UGXzl1P9=C)dj7bX1w}0p6vjRmwiFc*5=T13Rkr0cKLoScS zpEl$rCid|7#bE>9qV9T?Yeb@*uj?{S%oaivcHp923uM)ba0vA?tG3FzRpiuawYF%7 zc1PieN$lO$<`kdqrINM$2PioegA(DLic=O;v>h6Zhd_>+z0%RFic<-SxmiM5)A_7e zXFyKlJC4MAG)2u-K*2sZm`|t!=<$_4Dh{R51~i*aEqT67_stuXkMZt^`&=)mcJhb_ z>MzVz{|GR=VTduW!I#a(Ge8<+7i5Bo(&)NvtQ{LY4W}JymI$!aYgl*=min+s@|LON zG&&jCp*@CLVf{5c4;yS{u6Zu_2`Y}MK#LgY79N`OZ}r(LgrudVAy=40nVRcU@a0XDHXGRf!pyt?(IT`&sAv;tSAX zlWBEKauZHl{RV@^helf}-n{FS#ntM2m=CC)WSJfXd;N@crdzEK+2@aJ7XkzL8FBR* zs4?Hz-U`64veyFtm0NOPGkaBm5^E-&Yo-U;^(MyRq)gYYwa6^V4N51k?Z>N1^wr{| zi8b5s>WbF}|DN3%=Yw#onWIbN-l%R|6t5NOdSr&{uH&E^6RGmUe#qmrtDdmsY^alf zsJazuV?{zWk9CHl%4F_)Z6zep&2@S`gXOPjb7{j*;4kI7U%+3(zWGS2-6aen7ZNyl zIr%3=+?6>$Tq7Z8@C|JewP8&P61v&2S>$uZoHWN9-#4VpzR|Si!f4H>P|Nrx z^Ha>lrT9ppy}by+lXm{-?O|_w3ThsDoe}OEg5{f+pC&xg3C{CelaTW%y^dy!Ge{r?l+Fohlc`1Wc4(y; zqNpKe%|UH=mJxMn%#G&owaB2}k+U4)?~!B#J$_oX!+UY#_`RK6 zOBk+K@82U}aCrOY5G(k3hfwDZsF2&khTDZcyLLXi1TPGmbn^Px6$%#EA!MD$ zx)QQPQD*8MZsfdBYR>1lW_YC%Y}lKctm`%QdzAjt6PW8>8L!C*zt-!vGQb;xcS2B(d6Os zxPW`|DfY_rEUC|8^bwKQJF9=YOcH|2ycT zqT{q6io#I0@V|2dsU7+jDR>w*9@9U(GnI7M65iE518G#XW4V=Cd zEIOIp3P|ngxv`ab7t)Z5SF#Wh?SyVS78;AzX?bh6O zsHhXKQP>Qw;yF0(2?lgbGOOwX@J-0&HXa0+Hm&@dFgkeQnA~Kmrlj|*DODygmaz_< zIe5)!D){#AJ940xPw1KhvEFg|k$NAJP`k3OnY^WK%s5BIfCPa{WH%#<%HBu;hK2| za1FJ%J>dw=c0fcu8twwcImIrL+SLv{KCCG%UdlPH*5 zgR;Jaz?b~4FI6q7Gj<9}Z9Xd-S-(Ey@gug98fDCUO6BuM1w~EVAKFfD!9V7p#}HqP z27!(tajy_ejf`HAaukC3+C>;1$!cGMe+=4l8}X??oBm$lDU)$`^MWPB=!=qI@(z_j zSo7<3FjqY+1=I(AFBO&1bON5CWtE{VPe;|zsCtN>z>wcx(aKD7qiL?=kaiRT!0PM( z5^^yF1%-}o>5KY%wyerFDWjN~QQwxpHOd4^Ws0AzbXJK~xQ6Ir3cpt|GokA+rEMT*6{nw`x2g0 z9IkurUCL1Fx(iNDyn`dL&RG; zM;_@@uPFbmqI{NfPlPBhAx&(JI$%b4e)9FN1zoX>yj{ON9E z{eQ*y{I^>U|NM!toXr3CzDC0EAJg?HWt(q|_}hb>ZCRmw;Eo7OI5upBc76?rOm`=vx%VPZY-2+hZvG0Xpo|9>yM8Epfy1(suI1QS$fC!vp#t zeOLfsV$@|H>Ao|vB6SvFq!2)fK~>CBI!ekg@V-T2>XD=5AUEWV3J?0|y)c$&Bi3%J z0#(TNJn6k)FWq8@BDCX54Gokv^kUmeEwq6o9@OC)n}&w5ar+T&uq5Meuq zwJ!8KAE@p9a+gTM7OBeDjlBum$53ix3o; z1#;gs05>S8g)PmP#cjf|Zceu0<8xllw3HBz%p>>cz^-e;PodGf11VHPxf>c|u>qgk^Fm=F9ymRnqgErmKNkxRk#B zC+N9PT-bblKOTM+(ns$wKN{8}b7=1!l>@9^bq8ENa`(Y**jS9|hi;jr_$*t4bCfri zbB!1yFt^PbDDHTx3}uIKQC~n7(UTKmyu@-l>MgozrPTn(^9AFH{AN!@*tVmy)sV*h zuw0{Z5ZaaQK3k3Hsy0ID(P};I7Pc%nPPPu+hiw%`QZrZyk%9MnT3>{E<>U|e8~MF*_ER8*YDb-9KLBSIfU_Q$EQ z&fJ1YRrnXJAW9=h$toY^Mj&z^fB4`{SvB=e!d+%5vVRvFfy#jnI;FQfdy?Bb<~TaI zBv6&?r}YYJ9mBV=F*($Mw!>wC!5yn*dDN?!`fslmT%{ci0h^ES&Mm<=$8XctP;UK= zhJv|bJuJ0-!S@!b^oL0S(%5%4uU!~cdS22=F;IM)O;HW>te2XuyV65=@N+l>ZE!|( zdon>77xj?YZM-|`tX+Ql!Zr7w7_M{j{Gc{sDHA+wbx%C_m20d$O1C5H>k!SG*7ay` z8YqY~#V}^pD;F*H`zA4e2cbLvVTELiz(&*)Y6Bs-u0tZ_QnC)z1$!;{?y$H4)W*6qxO(L` z-k&*v{hm>(8wbHRKY=6S`|*-hBSM3%yU}@2ZIT%cPG9gq3@`_%f}j$5G_i_YabMDa zosle*i6Do~ft%bK>jUBH4w18K(S-yq2`T6jU3^)NA{=oKck&;g$akKYCkO40MF}PF zZVr3}fB!}7AT}LE)cLM?$k8@F?)9&Q}@Sm;PKeLFbVR}t>+mf4NkVO=$#3i-o zUt2jYRZilMiZzvYHaDfQMe%ga*P@IxhF)pS!)|?BJIX$bwke2MX7pvSO<+O(ItM&= zD{L%P#*Eu0#fr};D`2}cmn*f$quy;~lO{~79un1ZhxAiV38931D=19|6e*ulv6Gmr z4bkZE;>_&-iZ7i<+hR3`q9Rtn_>2^WpNR$&rb{MQpnv+9FftV7*tzdbK8M61WuZ7#3w0$^Ij zqBd+}4su9QZdC=blwdBQr_}AD3?cMLR@ZrEmBj|R0NTsds`A;e)fRwDe1wL zAog!_W#9QQw%)%%5F`c=h=k|EQ7s}(`;JyXke^4nUqaGI4{;TPfh3AS3qj3C=nx0# z*Ez(sP5}MF9>}m0yL~i`xSyKo5LwzB)2BrTn|38n1P9d%LA_Om80IT>m^EH~J^$Pg zL%LbUuD{D4#Q#5e#QZ<=2>KsV0FN8)g(_9x-7}&9n6ptO!Rsy|6tFYrAiEHWXu^Dw zLVc6e2H^*_v@uEiEATfP;KtR7Af}bJtLx>FYsc-{P44~TB_;=fJ}fhcu8{aNXgq`x zaRDj79b*`#XGYRJXoM(RJd^-;Sji{nAoGnL?bT3sFhA6cD4R(fT$Az|L$l8wgVX7K z-M=`!`Omf(8V1oD@x%rbw~`rz$M0h797BwWHCcrvs`PHNQqm6gjvBmVsq^z=(cxy- zDZ`~X?u-VZh+-2@DHtb7tHcQ)BrTY}NF92Ne2xXiZPf`bz=OaBB_h(qTD$7{JWfIl zwtj4c@sGgFCYph7W^mj3_CQ>r~2gajH}gbcC+0ksbqMgSGW7}80f z@QS9mR$%z#gg^Kxa>uMk5tj1`(|2{~22o?Q16W)`RwTql5S8T-#-cD9wG8gg#Gd3`v0H)k>!6Rkdlqu zw~T|&GgMbI4vLgWC=mr}&CyY~U$`G2fJ~-5brB)zZorXeJpMw*=~w8NB(lFS^1n9a zey{vUM(7y3nx35Ie%PG6yy5%al=AoJ9Mwng#KH=9q&A_C;6Q!Ad=!#15Xc5RQkK+G zB-MwDQ6(!^(i_LL>LAE+!JONkrlAeEPd%j%J4~>8`B*Fes$RyJfBGeBuMgU9EDi?V z|0@yGH2=r;@$vU0($?vXK74!Iqw9$CJs;DP+ny&}wi;RtWCoMU(~fY3k@A}H(WVVH zKcfnA#1pysndSy}r(rCvJ&Czll8@` zUvt4KZfu$8hKod$AyX8!N5tDgh;K7R>xH%zu|wPXVg zWP8?gD|jLzq#c5F&;2MpPXd`Igyc(u7y07t_^UIm&)Sl6f5l`J#>6*DZqLWlX89+X z%H<{=%f`Nnl7Y6E%f%BTVPg<$BrpoZ9Kanmr1k3D$J1jwQ#lu#N$te3i|nk>`Jp5B znqMydjGZUBD-IX)fx0$IXHL(7>!4aQ9v{m&{jvub%kqd&O~;Z^rsQ3r0B?;^rL4-j z##5$-J%_a7CJE=gyTck_#w4Wut47E;tyG15hdB3t8{(}0Zp`=ZKmmCJM<){jqkmMz z{ztIO^T-X*qj0CN0vh?>FkXM+>i;0_OD=09P@#fSL{~jXj~%Te=JNkw1@rmA2W4PZ z>KDfLdUnmTm9=xS@($i?A1xG4IE2@iyT2?EaOkXYtF~R=tXz3eY2-wlu6X{Zcx*{? z{m|ONdU(4UL@UFQNd{tnLv^%#I?5I09mPnlO4=y6b0SHNFT3Q0sUYW}vaXZ$naEuM z2SFQ5MAzYtD2;K=We)VUf!71VznBOE0me6PPSDE2ES!rRw~w8IX~<=GzC|yf1UQChMl|$x-p_wo&i$IG4a^6*k#TKjs)WE$&AYlEam==baO z%O%7fB?A$NM<9p?72+2_1Q^Fh21%z%t8165E?RE6*^iJem(y61>;qXgXe2hl; zj(m$m-XZ_09Fb$v2lx55sn(sH>PP>0E&}MCwQh#!9kB6!p!FQ&_)jPwK+n?-r7uR$pGd=TF63OL}D-pIev5ir*{Y)ocxG5X%mzbvEL%{k$EoR zb?NMj&`g8u_!x%Va_$&kIMazvE)4$U(+hW4KF3ZuK)5sef_Ze|uN(^K3Cs>bbYvHX zP;RN^5f+>DTj0<`aqX#j5osh;3X{gBXZsX%lgmTj0$Gh}lc=QW6i1dz!+ha?^1$y6;2o6z@(3L{wBOIPl^jtd3Q4Qk8}S(xv%r?f-q#}ZuXRMBbM z)khkd&WXyySam8_L?YiCFNmrm;B>5KheWG$gYEVmGF%+O>13CNEYjI^(D@ebJaO%2 z2QhUWirJ;m`Bnzk+E?=r$~ZqY(N9kIwRJMZ#1VUDhu+#-{m9=^TUC%-JoXqII>@eX`+erlP~F1y&i3hiJ~ffue(zB_#L!(Y z4Z#(6El|6LZMC<`p}#bwmRRop{MAX&?tDj3U<2Rc_An!qzIqX?mevb`Hpem5Wd}A1ij{bpGBv$I)bhBT1Ka{D1z@^+IN0vgVXkS#qIS(N2haS=EddC zOvbCX#`lge^lPuB=N{{;I1Jw94pHe3mzqyym>dRV;b4x%zLSiX|E?BJcvFbf`9YjH zb%4U$I#zU2Q_%Xxz!gN`xMlf?8jZT9mZF-1QdfDk@y~#s`@>nl>|nCI)xsd@oqA!D z1)89#Mq?8Xo@LZ8=qBeN#Cj{LlPXDu)l%A;Y&BgiT`RPZW!lOrj_Sh3dM_2nK!tku zx^nsm;4DL%H?IaNBL@i^iKMsdDjE&lhQ?`{Qxj5`BWO#@6yid)Wg2yP-3i=9=onE9 zLpODnp2EuLoaOdOUWp{kB~q263d!?x_xSqpcv7j${`&awBG(9AEhQr*wVH5QQIog& zdr2#v2rFt!OX~AVi>?Q1s*B4ZSgM+;sxqCX+Cop=BAt>4&Z#l*#K{ab1+}_w2#!UA zr0Jd%T4T^OxJjnsI4OlrM}G;nx|9rRi8M)~Nf%39*9;Yq%2-pXTRYy>*te`YDOEm2 zQKKo1L$mkd&$4v%F-*L~8v7_~-zT^iA|JP1TkN3V&g4(CxQu3N12G~gBm)B1U?s*q z&*)%K+QosVxqwuBZDCVqj-6*)TH(#05^QuQo_g{y+;21YEY6KpzyV(qIh;-gSQmVn z+6$wdD=s6Kv$l(nu8d@52{vky6~j}-51)Zw$`}~NHBViN7>21jVTm*n9llE+PLn6s zC{Z^Xj-RT!JGSRml(S9)4jW}p7-bhR#zDkX!;26xf)=WRrQO5mPX&I=#J31@%Gm^d zlrT&S@>VWnLCOgva3ZG>x=j?p7DohE7S$YB!o64yGIF6Y9rkGCU9A7!DY9QHe;mvp zCV3n1gf?4fPY!#qEEn&=CYHgiLqIG*ea9xQf+c+}wylP%Dv%g=TDpx=IwxlpUXjT} zuewo3w~d-Px1lF{EH;u8P57;edhuDfR$dmpe%LOL-G6&XmcQM5rIN>bo*%M*r{0tf zbny5E1wy-tWU7ks`#SE(lx4v>tZ@X>lgC0;k!Qgsbos^F6;1q)4e(}SkS#i*D2{y@ z$CzU!EE~XRZ`m{5(9u>>7M_MfY{r}@$iT*WJ*9cL|7osxd6aSw;HU-TJk z&ts!R>Z@AP#;G|#yWH`EK6F+XFAz}c|370 zmSh;Wpps?XPGeciuwyAylhg`7V$*_SFmob6R>sJLagLWpD2J-~|MnhUtw;Gz)7>hiYEZUL2}Y%^^{uRS}%2 zNNXPzybADny3t%z<#CqWbhKVg6*RziIB|V z_+`L>;GL)9%jG7D?r+QusihPYR_kd#W|0x2Rx?eZ)aDH7FNVR1VgSXEtdUX(`iPne zRrz9wx4KXxP2af!W$}&KZb_zc!GO8v=(q#f3-u`G$#_I)1tuDP)!yr1QBxB5K;UI4 zqF{ae2=!ojx_IKHoR(N78aiARS8AAd+#vgi#&arM9=kvZD~@4{Jp#$RktIa%2M@Ub z1ykO5y%oGoeW@sy;nUvN5YZz0Mr2_+(%vA-_?r^88$u*OTuRhr&>0bH*;rEi$U30u!o04}j#&}+uO@T^#d3YFVS`}wX z90w1Ag@QM%ek;y&3hQu@<=POaguHsG@zlu!S4OfsQ%kGpW@d`t$;;by0OGR&Ma)=b zF>p#2ZkNS0%U5w?R?0<1Br8QC=Z-s*FSd?#Hw{AFk;WwkkkCO7QVj+EDBA!rjm>qMP zt+>p&FzwKFT*3wA$$a#%=<;*l(p)a?DEQh8Gl(3$G2k)2xe8`kj7t+Mz^cDbRM#|) z&|7+(-k9uH9i|+!aWW47vLBFw5ZoXej8~S=A;B3Ng$?Nl2Sd@2R2+i_VXHs+Sl=P^ zCQpEBD=PCLLVV4n=|1@$l~&`}naaGVN+Z&%5v~nLw=QmCfkQgAWhPuYj^!v0$7JkJ zngVlG8$0@WEEv1+5Uq_ea7ch?^Ke91ku=BVB(idAiG3c3hBcI95MlNJZaVj1Eb$$W zP;`93So5Qt6{JW!MP%O7c-_6cP;|lptVinWF!S#`7ckfTEHK?*HSBx)G^<_A);MO) z%W(^s&D`@#8M;UZQ*U=ZC7E%NNg#n@bXoHZRj`AyTPA-E6zT{VZhwY&@(0SUD%hGE zh?bEhX-nCrlDT1&hvX_4TkGgWHgN2GgLqSByvRzc27dlb^t4QL%IS#Ehy3kKu_jnjE8L@mi9(B|1Irym zmi16uL$FU%?pOYpqEPaf$Dkd)29e;Wz~K;pCgp)LLvEKyR4PK=7P+!TQUBQXb-cTe z5PdY*%TY3Y{GMGP0d?GzY~E%W)&w}3;gCqfV%Nl-LqnKr1Wq$gfgaBiiYlyDSKfVI z;MF);x*&o+-P*ZEKz4j#H@R-0GpVuxu^(x8bTtv<%mIY!xg;jp0^IWOefQ+>W7p^i~P--0%?5UVxos7fgxE2?ZEW)DY$36m+xY*SMl zM{4h3HF38-8ocpv=t`SfsRX0{OdF-r+%n1$1<9IVQ8B;4f3iuN=mMq<#g1)|G=6`6kF{6^EPe`L zimN7XobV?I$UwcOCq@+r$cIE>E;*EK~mC@jzCD6VGfK_0*#_e z-7&i(VXp2r)=fN{wcfiCu(rA0ltVHh+vJj2Y+FJVx!MX<@rYp}z*gW#LrT95i-kki z?=F;wX9~G;Vq_;}#UmW@AUn9G1X_p|FzkMt!asSf@m_QDt_zH~Ndba}^w11A`#%>T zM8eLW_4D>QopYgE2QRJDU(Dg7KyaL> zny%iiUuopbL_%yL0q%J)x<)#G*(Qma$K_j`o)0u%W$*b}9yv$iu$UVqUS=;_On%ux zavV9Cu*}w_EL}(!`TeRWvBD`)>$^)Wl* z7<08DT7JzKe-yEMY#ld#!^rr)>9w|dT^!n+OhqZrUEJF_C4iD8TDL032kaR~qz;a> zDlhpN?*;N%w{lm>12u{WZ@PRTAKQ#kG<2eE8)e+#g3P$Cdag1Z)KFa4Xvz-7%MA$yAdZi;`{O za`bmU#=i8#H80T%KQer7SVGSTdu7bTexV%E;xUuD%q#q~j0?ZsEpl{u$gl~*^W{T{ zKEAZNo-+L#%n|+6@3S^a;qCVc9=0o8e0i9Qil`oH^DP7&sn^UjLjm$fCk*{es;yC~`$Cig$B zBs0zDBT7mxtwCA*oBPKT#Xz^mc?oOf#SGpvLCDQr(xu@KY&BceZhW)7H_4*nGLGTT zhO+vYS423zT102a;ul1^G8dInmcmD$?@8P_t0}T#-O31faTjc%&HUT(ByT5u5%0_g zReyq&4~f7tkEoBC>eqGiyZTNXILo^CU#^o}(dXA@Kk`(MT4ePc_}kS?-s?XyIX2m5 z%FL-7*%V37DTaD_NTn6_A^bpcOn*X3l;1>ceQ)m8mgU$}wR{iwUFpv<#F3DO_Tm8Y z+9E?fP>r6G<7~r!31kzZ-o|7hh!gm}PLH10h2N*S1d=NLmyCgA{Wgbxd z%Dtyo^MFeNuD;i%f_F>u&Ao?Ed&^Pg9aQ1rO zo_0&>$#wLv_#hnXnN|5rT#wK`swm~2Q|V40PtZPUQsxCBUwEg_+%NY9^XqZkld;E7Mm9_C=4atb~*B~LC?)huik%LY}j#O#XHMSC7f8zD#tcp4t?f*!fO zel&J=<^J+TV8r`zLuU#)akc`F_opk=XC@E^=?_9H$ca*v0o*WXt9+fR|41sbQ6|~5 z;+BoaOJGYbwAXuYDV-P8Jd~SZ9r;eq8mtCT_`>1ik!5w+9*Zz9_T_id)22y$iq@F2aHrdJHRmh!| z0sl5HMvmSrY=}fElOuLy{mZHSI+h_t3ftml0VL!kEV;&yPJUxulDAG_M-Nb-TPo?` z6`f0&fh7#ZOBp3ap{HRg;!|HGd)Vc`K`>GBrX%D61H;9{SrN2?gNJ+QXMSCBVk15j zYv5lzP%VPPcogRWVV@Ggzjs4*EYF<{Np}qGQEW(i^~$c1%l~5A+=bA?@aF%Ck(l6h zNqvluG9M%(-MfS9IKXus(Yo|}Eg(o>v6yX@RxBX5cp&4-Fq^Om55DrJ?$9rf-(c44 zeWYWrZ;ySfTx~!%rGa2=+eTCMMR zy>X)PTlX_Fv)rXI*xi*~Kho|5=E6O+4jedr!Cv39BTVr+BzeUvPN<>v_Z7f>dqm#r z;rFK(M%+VrjKvfWgjNW-pa;IrrP%v98ML#@?;hNEtAhiRvxo5N|Isbm%?rME05%E@ zH)1rVmLvShAmA%-1Du#xwO}{(xO0@7Zwvc0eCY z^wyZ39-PyjR^c8IK5bR7^a)9=z#)rKd&-@pIeu^60LfPQG?^ZEg*x!iA4$*$Wn(lb zOMKXl2eZLhQ-gBYJP6>{Pq=Dp3C3%&f$15M;R=R72xnr({ehcYhG|InWIC04xE#49 z|H2G&`)dn$B6$bgB`a2zr4>AS1AHW*X{1rGo_7MYRu`pS)13xayg_^B(V0VMV!3{y zW#DEE+@csR_M*=DhbyS_^3Nhv5Ghqa5S}r;xdc32p(Jw6r(n4Xg;pPhRS5uDd0@^T z26|+OS1|sW876-u?57@E-?kJzmcEjKJ?M}P`rC|sa;st>d_^EvM) zHH7y_FPJun*(`5PjEX&JTP1cK*md8~Ey2~!fjTt4A+DD|#~=KAU`{--x{EuM8anbx zT7t?dlE^IKyBSMtm0Na{J+j~;cuKmp$P$EZXHTxAZLiv&oT%N5yD<6ASWgdvz|Pc# zXNQ@3isB0tmVXuV#(^L9h=a?SOZ{I^%I7h$;gZSLRFk(c;sR8w_Re0Jj%a3T6@ou* zC@`3JaoNhtq1}Xl52&p7%LVqz`vz5v>d1FJpj=y}ZE{>uEfv>PQsb@F#>(G3H62jKDKPuy-9dLD=NqtK6|R!%7A!+`&4d%*y^2bp^4H ztu!K(2+3dN#sm~xs{Dhd4=uSf z`@uC?9L0A%gS^^$ZZ+yn%1322G&g@#$uiu%I?$lcE9;qo|32oFVO1S&^I0*8`-cAI ztOsui%9m^H3~Xau@-febuY2w4p_`Ye`v+or>+fYITui94-fm_3p6vQbJE;c~6c)@o z9|Q_7mpQA#X-g9`CbGBO^#Vr@|KWUUao&JHe%qDiJ)b>g-fFK4+FW*^y-TBv$+o7w z)2Hk()gwjqK3k(#ZyeNzE1d(C`4wq9H}&SSc)BmUDH1*0$lb%8&Y?*!=E!)dkxV%) zW`LgRW`v#h#`_oUoV|M5Mvu*94Og1MPVRNl8pqNtjxby3JJEbMl1qKm$?e)t@1XFC ze$!*|TbPI(OV&nO9qyzs_k$HJ+vXD2Y?^w)Q{rE-ZgTCcf<|ZIx<Cftaz+P3M9TtcFsz|sMqkKdG^Q0OzgFMm3hWm8Gl)U z3s|RfHQI?H%`8mIn_5|fBhRhr&D_-(SGFU`JyNocM)5?hfS}5;{PaX^X z*@&`w<6gbAgDq2H+0_G`)*)%Wvl_)qcW1h%w3b64qFMS)%w54`rdE^R17r3Dr80>oDQ@ z;%v^J^6KvlmSKR=Z|Mh#)SJwA6yl#PXZBO&r@#M-;^Jye z|8o9RPd-!IwgPXrYX?LN*2{J8Y*lk#dhW)?L(3)=Ddsx(X1Op|$XGTAt)gSN94#el zd8slaT_tY4Hibm?TBW^fSGcW7^h~Ndq`zrZ8r|V8)|1pZQR$=i>JA>S-Z-VdjIPS_ zQE?0atJGc4F zHs=VrTP?@-vmG|KIQW)t_j=NX*)m~@=69eMhG^%hRvVRosU?^=$R(xlkhBx>h$+P><#WM0o6WnH(1 zalLtOCErf6W)1Fyqfv(30xCDoQ3*y1Kjpiu8vER(;2e!IvsOj8-3n{o=&rRcBWtuO z-`I%{(PicuCzM~b<5UptuPm@jZ|mD|>v*#8jm?Ks&z~x5*pn5daEx_GEc06-1MhgC znccGFF}Cpo2`nntnaE7H@$zHY$yzG5i;}e^YR7iU)hZ}Nwu{++K4+hO(b1W-~TUMgCtOWiq#uVOJ zb6ng3o8OM#kFa)^sB;fcXj+zZP<4Gu#pL~0o4rN%e*X|N{ovh-T6ft^AL9I<58aRI zXo~+9KIeF|1iQ{*rs#UnjT34U)3Zg_4xe4QI+qgn;Ye_N#gy;rp5}&-()A|_*Wo(I z1Fql(+SIoun(RAnsFowm5PW>O|@>dxc_L^ zF^)J__BgAC{`fHW?pa)Yldiy&>=7=h0%i?bX$} zy4FiYt1@#nt@VRmo960R>y4JI5NEXN;`HKpEpan)f463!#%n5%sI6V@UX*2`g_Q%{ zW>aRbj~&(!q@llG^ROEy=zWii#&|4$#n(NuZ?})-)8uTEe;ZN6nD91JD6#88xj?b4 zyJGDlWA-qqYMtz?D^j*sWSY4$?bD;v6_;IQFFX9UX_a(pU>5eO_$4KFv%V6>TQ2LQ zj&SK;9^M&bslxqe_+0bF ze0D_JfiFjSzU<}d4JFIbJ$z8|F0D6h5AVwLU#|FRbn$#S$5SM+MNaNS z9krl0@67ryELa8MhjQjL5<(SUgBjoEtWtivvTIB|kcU3)`O2=^v8eNWL);YxmPceP zj%f7-lrtKrY2KTAC_dF-8@SzT%vVA@A?=~v(_XQzJC%_k_#?nbR1jts6W`Kbv9tC{~G06Y-mS8_C2jP3>1=yl$|V=quYyD z-_$!s*ULkfAAiJ>+P*K(^IUd#NXfFbbv<1yyH=j|78X1CXivBTjafb8o;YiY25+XS zJ{MWzn+GW!6s`FUB3!ZLZl^HCJESZ0Sr0PzS>E0x7%yq;cHn;BVVWy^o(gJ-MUn9* zRA)K*Iyj5TLf+SiQx}%|wmi95SpK&D?DN9(eVdDn4D-i>k9F@`KeK#?&+$x7H;WJ# z-U)3Xk1ti@`?ODOs_-d&L^I7EuQYJriPK4kn6k=>k8Y3LXmZ8!osV~nU=;hVp5QkK z*!5&d{qTVpr?gMi*!+DIc2m@T))A*%hB}U9QkU! z!t%kTbGx&7)8|^r8E3;X&+FHX&c!WFw&`60_kUiXzIj~t2yL zZaCq1lDa8bC*aE54%;g8PmJ0H78g`@3))F0f0FSZJgqBlqqjLWLt$dW4x2zj?YSFd zVlQ$nYP+dKpPQ#|jhs=Eds8Q!zrQ`5r?WwNU-(S8@(zzpLiHow{lVOQAzw1jjjVLK7j|u$YD>p`p;wo#eXuc|>;KBxFvn%y61Eky z#q`^iYvZPD5xGnvCG>f+n@>z91a+3ta-V38Q?=1+I;yMTn^KU{^}@R_F|uXJ8>MfZ zrh41SrbK0<$wF9nC5hHWSyLXE@puXQ)DXD*J;zS{UWC`Qe40qdgAL;w%FMpgdt54P?+p&j3E?X3J3po~ zz2v+8_vO1!hE>z{b&T$q=Kj7Tluu!Ngl9*nn0K$BUF@t$@1rMGYo<%iao?w#=rhky z4c(B`8^9yp!I)JSCZcioOs6-y$yKqs3v0r1>-S6MF@E3kBJWV!<;b_X%@?C&?`7vF zCuXdfyRfus!xU|Z#`2?2y2V3Gm**)P)qG})tdLzdaAjymWr^$#tb_cSlhZ~)EaDY9 z?^$YM`~r*Xb3z^lOHRykrU6B9?D=Y2gt>MnYD?=2cO@x}tls*wKd+ylp(8cOfbyu_&5o{ywB zElkTO@Ol5a2Tl~~J4SlO{Eu6_$KV6q;(>UFct%;>u#=W?ld zhsPHWXzWp;bF8rYAhbt8YL{~ucXs~Gw#${01J^#I zcd@W5NA^J4DIH-+%u2`1;4bcsbHP>nlpGbmSUt&lTs!yUk%XC)foH5xPwKLpRKY31 z`vH)GQ+UTx%0&a`>+Bn3g!S=o^Z$L94;=V~`vK@e#Y0bKtX?WWr|Nw1@)`$wt8LB9 z46ARalC6-@(b;%Js=4G9vlgp{_vfWY!kWIXo~eE(l^W`vrF^YfQ8+#E)v#~P9h(uw z(tVSy&EGzM$+^!P&LAzNd*M<>c6kLglT7Aa{=?_5oqIOQs$@PI9EAT=MNug&##OjPZE0MA}`)U-n120^qQu9HRoX9QTfWg zc&zCe(c6)fXSUpz_mDcn`B*^`cLoZEDl(2~1!iA72xmF658mMr)fwd%+iWc>ti7eJ zjX|l>w;?8_uxnp>9=6oo{+rEs1pV@5QZM{MVg%*Qaz*ZDQE_>&Y!E))6`=lve_E$&{}HtMRn)c{(AwsI>OcRl~pGw9)28Qc{~@a z$2D)$gbj+7S=Bw9{bMMy;7kb{QpzS^IPRqPnbhuP4LgkBojdjlU;AiYt zcHE0P?s@R$cGago6sJCIETi4C%o$by*`2?g;90v%Q`-2|%CB+}y3dm04mWSy+rmi3 zlq#^}{IZ+u0&_Y6ei1`4cDPHC&kdgXy!)W&5UPInshWRYg7H2>r{+`dlnvz{47MrV zdL67dn8~UC*;8IdGnCdTw7iwP_l42@t6ybhl9e7zwK?qL8tBL`8e!2>%ls^2)A#bq zmQ!{?-8;82X^3US4iwWDq;hLAUg>14xV*|Qc~s!Go*3=c6Shic)b}kPzr^>lM~3O= z9RSp!ZXUjHD)JEOG}pp6`-m2h8>?90zjhL5;%eLm57bg_-y3_}ZvS&F4OgZy`nEQ~ zLNj3#tB}WovL9=iUeXEj>8?6`zeQs3*5+?ZzU@qXOKn2O0(CB5(q8)v+k1=;j6CUB zWycyUvVxw{UPWWs*v7S$7Dl<*JV(x1o0$x1*EKh9Ow;Kpkuozad$5BkXVg;Me# z9XvJxdEa@>V4FCav~o3nDSk7!PCWMH1q z5Mx9pjd||V6)meXzU#jY+zNNzjyGnZS8m+;J$Co5FS^HeyQP<3?JALvZN7WtqD7{m!9&fN~hQGH) zHJp4k8)tVdMDT;1nT^;OWx5BmVYlfK=Ih1g;cV}&rf!ipe-<32Bsud=*)!^E+?D;G z-mU09SIyuTQX=eqC|5AFx5|{&N6F(m7jjT*+srqQDmcY5fjY&q&^NXaJH=v(^TyhH zIN_Z4VI4Hwujm&=TXTkF24;;=_3Rgokd;RjiIXc5sHZ;+w}B-t3Hz_Z(Q&lxK|x zq=c8MF15kX_RQTq`jy^qb6je*;=%GCmj~5Gy|~{9>fj`K8inB9`ABkLp{J{h3G(ps z{dLI4+|}2lh3R`^*>FRzX1q4f!L@?+<=nJdSF5Fu?A2AJ)?LcKBs`&8&wF!;mt;Vh z5986o+m^SYg>H*PZ?{@?CgXxSX4U#BzRVYelUSRZeY9V*TyMMj-S|GZyZ!D+8BT!r zQ|)V?$%&BQ^95hzCcY0iHCD!Hh74&jeqsnA=bbItq57DXRx|PFL~W*+n^Po^%DVN{kkF96#Uf z6gtbk(!$@c=2EPhq;0|&WrY8>3Jk^lHHK|l&5CfbpKu@Ey>8-OFTcFLEjF;psON>i z7U|0+OYAQC_RM_dSXZbaUn(CQ#n1TYjj}Xf?t00LW7oURu^o>H7BfDs<-xIzt?BXU z-mQ*QSI*$7w)Uv4kZa}XG&rZJx_N`mj@461pNny~Ph>sK*ISw-S<(F4` z?>kOj`?jguM6=Go?Uc)VU8i%Lt`>q^2ajSjD_vAMSYA|Y;@#Jy9ay_pEJVwkFGHWj zer@@=v(K~Z#Q4<_?0vKgY%&bjwgbaFr`->y3a18)6zS4_qZbqjb})k za@Q=E%7phpiy~Mm=F5XK$9C-#)1_hv+ENoj;dbo7s%;^j2keBcg;wTT zAFrV{-Z4p&Tr>G8bS5}vCwF!r0#@42{-?^PBr(l)$IH%LyUrMu54tZp{jcB;B9JH5qoI&Cb8&1lY$89 zt=%z?PaZzV-KKUqEH&NU;7k$B|Sgn0>JwLs{`(Xs8mUaEw&CI*+L>k`g z7jocFGZZXh2s)5?XA4G#)+K}gwePLK=WLhrjTk95C<#5KOM=?|(rq`xUf32sZ_*RC zT1C5-sB^F05FWSz$91hT(t7Z_N)YGA3iL zgPliSSn~)Zj(LwgrRTrmGf^)&Oc&0%j4#;dB{kEYN4_~3uVlC0_1CyuV65|LMce9z z=POE^_%%0(+*RaYS4eEt;CU)5^IextV_a46f!58dOPgJLXV`?@o(gR;O~-1i!}yiI zseHLoZ~2)uJM>Rlb;dLvosc))ds6!5YhMOgVH^Kvm){v>lzi;ZoL+ZMUibTpWQ*hy zPLce`$6hK44R(eYzR$sRsSd0+E??DutfMmAB>ZGuTar%U7G3Y193plPO2j34n@aLy zhvd3a2fI^?U(+`Rs&7zypF|e8^I-s$qPrP=pDN`unXQ9XgIWPGidi43Ikyg-Jg?@f z@$8FqhVQ;T6l@eu0B{7zRfqEwS?+KTE@0s z)%A`$*WWFPyHeylqsPR83t)*(>C$xDzV423!gmHq=Pc9x!U<3NIVxsblLJ^X#rSQM zkDtyI7Al&^RtnE|wm!)(I?%UF&@fF^BgyKW;eBtmiPW{GG`nsadz#`3AEX;?jtiIe zq2C-emU%OR)!Mz_UP_B=oydkpA)7aEWDh$gGwnGO9G+F*9QYlKF(;p zgrO+!p>dB~+=pe~-UPieTUD@h;wJOn@fU$vUWbFjbL!1|GM~=4l$qY^6BhG`fA4x} z&Am3hmvx)>=56qJd_zd?V918rGn9o7Yx0}5f)a1NW0HHQ*Sc%4T10DkD*Ep{9+c2q`fgS+!aa~Zz@BzEGt9qqbXi)(sWol3?B$z3iM`PpX&4w? zkycA(dm*biG(;UtY1;GJfgu%S%ox3x4NS%GGSgkX5}Yg+9yEtW0C1jZ#)nsVZoU;_aHS zi)Y_=j1^AFyrnXo!| zA?)0v4=bG>TTNA0n=?~Vg)5%E;(H!bxF&6alCw(TWI4vggeTz9mK9kU$%imJCQ(zZ z?k(YR-Jj3#G^gW6i)-TYiT9XY`~S$%iN(t$*%ohRc>dT+RR@Xk=5pAMa{)K z%JSlELr1^^?-(V4O?`IH;F6;K*?Uf7-tBJLoK0)ULthmBZdqaR{j%vLwpXyH1+7wevxabs(A^c^U8sz3bQuT5kyD_-ZK-Zy{#e20-#f$v!+C7LUWrT8WvKI7q1 znYq<2=*Al!*RVDB!_a*LkFDCCP#K@HCwBC;C}q#NI+N8eESoNT&v?~5qj~Y+;MQ+P zUq9p+Unk=^yva94_5QH%<&=GwKip3nd}q|Nxqs+K_WiVVEcYTLlJ6h`|MzI~vyxB+ z=;?nxobVUkYw}1k>4vN{i97f@+}*Xyr(KgFbZw}H#6<3;)fqhIoJN-IBAOWtW)AhD zMjxiUR#E9#bW%yOoO6s;R(j*p$s@$oC&tO=eJm~KzS7E&pwG^yDzt8nt4EvPjp;hB z*vS*`#bcJU@AyP}!TDg%&+>uOIRyn|!|_LBQfe>o4{nLvS;I3cb85$3Y;@%fG4;n% z(TBe8_1{W1ThF9jt|IbEyPEvyrKcjh*05?sczAbwKR^>wxCer%PHf(O$Nmnr^<}_+IWly%>@|lm%-SV(Kk-;)D zylojDD{Ck;uVUQfo-c*r>CV*84C(E<%gD`~)aBynOIDICL4Nr#+2ev2@}&yO9k?Pj z#|t!>mZ`xFjUy)>y>koE^dt{lBmBgtci;)n&boJYE5+0{(p#;sN^pN@TO(5QB5>A> zyWEaRbvTwoD%NPM_hkCL&&y#W`RTfbJ(3^FB`&r)tX(mRlbrkb-HT>Kla}`4yAa8= z@^9qU9R?l(6A!R|+ufF&VsVGR<$D z!z3c_RBCdK?&h@d()(+jO{mf>hZOo4#3LkJDtT9jWAjB4{LIpl=q@~Ck9zd*xulQn z4cYQ85zp)6bRV%b7uXLTb-oub(xu7KUD_s*IT^get*3M>dCZsQs`a-|UHMU$M+_9G zPcGvx>Sd+H4T<>b2YebE)UOoVU>3Dac>hX|qS^^FYifNp)z3k@`f9A!zH_<%Rl1lh zP*XT1c-5H!*Tfn{Dw=`N3$2VNmkQ6-ZF-Y;t24Lr>gx*sRCQOiqx9uX34<0VgL?;8 zpRv5q8}U|`y~?k-vH0?pjkuJR zk0~?V+U-v_!7pmWaY)7NBDqbWU^HCYmD{CjL47V_gA9de&5(GE{06ND17`cHT(8LR zaFi;2-|q8VR@_T{FoTkY<%qCTLe`B7E>9%8rkGppS(YR|X3U?`;a|O+ZcoBNLm^}O zBND2{2d_+Jx#e$;-7_5D-O={!0_~=7o5N?qa+}g*pLlBv9rLPrFy&IfAT_Z><5>Sh z>BH>J7GBh5UMF{3evnH0ko+uUneuD>?<^hLo-jqx4n+w!u!SEgO#4EHHMP8*rleVG zE4p^|ZkF0)nsU9bFnX zGb^A_IXkla?a2POA`?8-QY++JDMj;mB%-3NAL)&sjWgoX2z@o%nZEw(nXL@1Qw{CYUtexXKGVuETRSC>wdgLJ=B%09pP~@!QNR3X#m&7E zqf+bw?vK>IYp9zDv$E!K#0Wdfal&`tAh#>6)1b zxn7k=cD>LePjqdw>XT3zOKZBIvt=vq+Pj84f;&tO$=^}(6Xe+*!?NEb>0b5ADu$%2 zYhr!-$p>X(21IUUou65H)gg&z%!7Z@d25QsWwe+nJzeghTgEMwnD!$!+5TtJamz&$B+M=wQjh5J76}EXn5my;xD!%iV0Lu{d&~bFduCwCUw{ZKH`g=pmVj<>)cwC~oS<&;Z)`P|}bhlinhI+#jw$_LD zXs3>@Op{7pKGQ@l^l>VTr+$TbvcOi$c!x>F+E|-8gNY`LRzS;p-xrffywlgzpPf9B z#@JyoW}5#&fz$EY%#aVoaie=B<(%)N#n>k(c0?vBzEwzg_fR|ArAnn0*DqQybAhdh z!S(L?%Dh`|lvd4bDjd3Jen5Pp(Erxmk?9GMcd0cuF5lO8E1fua@ASQ*j}I=i*cX}= zC1$T|WX^lFw@RMLHG8UN;yuN+Y^M%UtFumE`~Y{SJTp99z3UFPlzn7L|Luec zzpxSUwVEsEY%jG&nuv#5ETNG%%o@8sGHTN1t9)^sX~iM>)U%H#HHL%jUGIf#-sl+k z=HttITn-g&6;8vm9iDw(zI423zRzMiBoQHzyRCC~LDL)O@vW?h!5!*i{I_y?FDJ2Z z4Vyi8eTRFMo}B#FdiV5`zG^zHV|Ulz5^ZLS_YS`7P;**8Fm%g-ea)8{6-)~BR!llA zjpqCEzP;>AY>C(Qoto+^za89~5*?t$l;3=rLRMaH#KIyZ6=ewca5 z@{Q|lQuSfA5w-3Q%_}D|H>KH_%BT-~%eZ~nuf;=3#6tR|w^6X&2 zIQ2-RNu++MNk)|YwWlctA8if{CD+ny3;VGH_K2RmNEgyk-*re{vSjHRvNdF@;6KQ{ zxo28e?3aPnbZ;s$__y$)3F@P#VWP4@M_*GyPeorxQ^VLqTu<|RJw6KZBS?CFeBkM| zd7u4&{SV4~awIL9_MG_OPfJGT2zMAlsyy#gIR9t5I%*pF#u^KMJ)^O*!54mW0lok- z@8iNak5u2l1b#pN$G`6j{w*>3$LrS2`>{L5AB&S9^<$=aykumS_&@%U){%sOZkhKv zPYi-EQ0Jbi1P}OmAbgRhi~jw*Ck^E3;V$m^^8p%Xtk3)=Hbd8Z5pKOX1byObKt{&% zFDCqz3oZVqM}O+)r({3>iNHcOJ0EYMzR^}T1oI#yA13t+c>8$XXT2aQgC_Mu`guPi zBeO#P5MQClO8K6=hP}Fw#a{yR;6nX8h0xE@??KI%1p!l-d^frqHmV9}$jIQ=^FCXk zv+$+?zrl=Mv2Ix3-*-pN2SuJ4lBc&zGy(?+1>nu|AYtYqK?u|*2xtErnNQu>#~$PD zVC?7aj`0rqIlo*;!0(`cP#U%nO2L-_ao#5d`0%A(NZ^UgUVrbCF9g3QWLT>M9eSIY zj0{;=|Mw#y_a|_CaSr>#&sz<^JSL z9NJ{Bw?_cR&JAmX#W`TTiFw`OtIwnq!C0mNocmv);V;o3Lg3CmB%N59jX&xJz!gD+ z0RNcxDcVl}P6M|WgR{pv7-D>VvEI1(duH>)1LDk)MYFCu0F{w}jBMjPTzIzc5DDZ( zs=+4FCVDNPOG1Xgi$ZsbC4f%a9uRRU6rNcvp&}!@#=20iji(6!YMY>U4Cl8V@wsDH zzEZltu*(L*tw+hnnL!B78S9Plw%T38}M@+P-qXH(l zWMuphs?e@)uyhH2a-grS1~~`D3FqPC`(xPk#RmS`E?;Q;!`vAqDPUt-kSw7k3|vMS znXrTcn{;kp1qpM(^w*q)-}$bK#+JsnH^ff(Vy4B7L2frZJ9+B9Xlxn6*rUl+4|~BqSRj!>52<0E zi^V34yz(%Py93_l`EajEy%}1L-N;*9MP5|NYhP!`azFv?CGDFsbB!XzHrlVlQ@IoI7LLZ`6 zR4Q{9jSUZa+hcuvJcx9mLbmfRL0U*~VMs+g&Gy@iCWZ3rg>)?} zvT(rn&x7e0H#a+sy(_*l5UN9xlzauCj5FZrXf-o5k`Ne0g^!H*pvFnAC@V0K3$T2E zjv-tx{)}u0?~Fn#@Szc*Wjqu=R?xtLF3bqfLbr7M8QS=Veh4FXxO8vp0_n*fX?$fc=xDAn@4{ z{O3(U9Zmp`R>x6B67WbkB|Zm6`f-<`pb{(e<71MUHHTF z!cTW3gLUr!>)wb`$u%|-&^pEjveFwjk@6h`ouMQlTnL#KLtN!YpV~4^mQyo~UGzc=(}ZC;QUpIjKg>!YNhLaft?d^n`UJvX0O8U3 zDTh7@>|g6;B*2kQrPW!RmkvH70UfJ~(hLqJlSDQ13;f4xJl=diHvSgJ$FrNt1A;19 zB-o|~iZ9G`(R{ymeHKS~f-@wS2PnS-r6!7U0@e@~*+exrjE|2#ENCu{lXBL-s{uIQ zfLc{hoKcpG=0w8A;^>~~!rLz3I~$;h>L|Kun?=*H}?mHp1(+|n+|E}M!YoD#AUI3_<o%QXlBXlD|8 zKm=U-_cl0bbVT&?FPLbeVUeVdXQ9n=H4#DoIS2VO^y>O2_Y@#C?&n_!y{wxUw1*Sy z*OG=tbh5!N|A{1c>JWSdP~De2P6S#H>$}&(!4&t;3xxAcVxcpkO_uu;0Zcc?Ehv*k zl&O2p>@Cci$&NtSMz2{o5`*~dMw-qzj2oOYC$JiVAU{gkWmpHOL144!B))Kxh;ZsZ zOKo&Dx4;kvD^Py0zeQ{zj+xow&?%V7Si*KcTAy!bh`w-61bF}I6f&A6r7Tk zjs>Jr_@XldGuA&L!gQSIjKzWOA%`4jVHflo+Q{VDh~OGRdJQx1fBK9R{Pw+Xji>=S z2J9FucGEf{@W#FvZ(jiaStmwdmusQpqN__}IEmoe=Mr zhUoiC{rK>RTIyHW344M{j)UgWnb<*fQt-$%Tz=m61nNN$)S=z)4HEF;`;t(m1qkmq~%f|}8GVPQd9To^=+10a6> z&;_A=L!=Ea)7y@Efv@|3ucO^X!SXNQ@nuK)u#};abt%Xl3;l==d8>E-0o=n$)!V}r z>;22N35ky|7{=-rtOW)KXaah^iNpLY!$!i5P+!S;f(|I;@hArD|CWM4SV3ChSh(@| zYM@ZTqp)=RTM9|SttdA(C|wQ|bI=uNMb+;8TM8nDEYijd&7wddgVzcP*T1D8QqQsz zlQu_~;;#GOQV8d{)TMMqZPHo^KU7LRJRoNA5lP7wYl&pEWQ7hf=H1I zweE{(q{M&&kHQ`5bqJ;czk(cuqy?TQOW2~KW0I=!fnq%#MX3MZQpgde$hKgRKnATf zuponuAEyHUl7fg^tP2#nhSXi+_ck{8ZyAWhkG40ALv;cfj5`Yym3jDYDTu_6 zwzj2ra)1Kg^PY$QEd`PIQ8RgF1_w5aY-XV2M}OqsQV@wB`m07~2Y{jlQX@1)QS{$Z z5Q!h(L)X;#00nX|4NbA+*xynRi64O%)WVP?{Wc!O%M*V~K_q^toEKAog(kAcc=6*I z>}CIz>k+ZpC8O_kVZo4WEnXv*pZ!|~BJo2=Ys|46C@AnKmL~lz1(En69b2QM3lum! zn-xg?TM8oajzoa1I+b)HEicqsihUf~-aO~n=G7xd?yK?;( z*a02cQA0BXW&R}t5zm$mGmWDG24y^kFIj)dK*X`bwR=i70t1rsqubzg?JpUK`1Rn- z-s6NBrnCQ&frwl4)~n~$K~GnJJsF}_Qetxcl3`)C126J9cm({INfTSnbj7?}l_d{5 zL`N4c={?K)J9hk}2bqzb=8wyM1OaP^Y9aosp+DmPTFZhnKyJiJHclS3oj_6^TF{;x zN_*r`QSgVL_zV9>@xxCNVl%U|uT9^H0fjC!1wA=tnEW4T@O6bG>-{g-NA5sdB=FjD zaeA>NIyg^1U(#)Hepzw!DnXT1ef?i(h{Q;7rQPjNOhrZyzKQOc=imMp8X{vBpP5kH zW*AJmVd(=Noqc?b;U)lkr1BE^A6Oir4HJY_-~@&)4Hita)+}6X z)`HWYf6(9$R(z!)xH?C-1|ro|?y&icUU*exSsbn!)MmO9*p@`lsRq1?mBET9;H!+% zRWRq@p#O1W0`bhlD?~=1Ql88aYyiEkB)EQYWIqbX@%4m&zpK_vzYi{N4ao#LJLTkA z9JrB30O=##=~wygAXNq+ESUmIS=$6iQDii3-vk1ukqGjU^DxE_gKL;Wm7 znh6SJe%yk94fJ&LfWzo`3zS?*ER@L*f6HJGRu9z(BjLY<{#{=6dly^{3ng|A$g(qj z7a_(rU^(L!0R7(xI#fkb@7?%6QfvIYB%3f_gPct{ES`|*!A7GIiqAmuf93n7RtOM3 zH_S-wKQWA#dDTOEAA=0rQT!^>|B;{QNeV>IyyqKt*F(6-gAE^a06Z-F|G`UG+cuvJ zlaU373(#5}RBJ2M|0Ao3Hx^4Mr$8E;5YIo85AhKLg%w&T+J4Rd2U`5LBEd$ZL8GGp zNDo6khVEHvz5kI^#~n(*oWXlZc@X<1+y`WazlnNbxRy2kA9;x%?A^lM69*-1WQcar zn%%eSKal-wAEEC25yo-s2AV)hjYn!`{ohH6Bw1Yga^?MCx-Jmd(JiIl{ohH6q+Fl3 z$#XJ5OV{DG)D83BNr_B|MW@Hbkc|s`(h2+jPD*5s9Dgfi38G#nJoN@T{&!L$)8>_{ z8wTG2DROxOx=)+;{&!L$Gimvt$oLu{je{77Cf(%v-${u~uDimQ-|mNS9|IMk8mI{9 z=Kh~a3CF*o)GRw$Fe6At796Apsn06r9h|2l1KhWQm8K8i?3Xyg`O03OZ>oyU=N;54b`6kFmw7V z<;(dpby+3 z1wdJEH}oNkE=W}aTJV^~0m4~099)3#JKf)n!v=^uIXlj;HAHw)V%%&;5rM(3wxI-H zTCga=7xMT(rUEy1`Cq(fynih(7F4oXmr2Z(S{MOk-ypkt4eL6A2w9(0F8HO&E>W zLiiHIn?|i1B5{ ziVn*{B9sn3f;PS>xTFPd;QUu9q%35JTynb+)ZgjxE$qN>I15J@^vAK1-)bh@{NTrQ zQG;O9=fI}X3$Eg-L|{#@ZXoh+SiWEVIsbXEh1NYxah~%HSobLC8Et&Q+ZRQGFFT@V z3g-65v7l#WIB70|>be{QBCwWNZx3WRH}>=NggcP%aTmsPSVf)pIb}r%7eA!GKE7#4 z7PKk@i(6=OeMtmC-{i-ID6pRMTd{vDo3lLu**x$U-X1f4mkNZ4dk*qubZ6Wkg79m! zA)T6Di4~%V1y$XJuh2Y|k&%U92@7Um=K=wXG%C4=p;0TW5-tTrqP^4Bkud6SJfbI( z)bi$fQ+6$kAI@FDnyHc$rjrDu%uB8qXs(nBily;M%CNd!D*4FSO@s}n&H+m zGGs3aeSqR<*kUPu)^y{W6Vil{Kw*V;FoFYg3->7$4wArEgNrG!4*!;<69x}!+EbDS zf>(f!U~V+;;~Py1{O`^cU)pGF83`;#z@jsc18!4}Bf&!GBr)HwFl6b0BYC+8qPeE# zwgwFNcQLF_qSGn=6MsodSbN8x?P&3Vu5W?1p@&UbI75Lq8viy@WMW6CZG=f?W*+QD zN~<9(lWj#Qk1p{KB*>vmWUp{>XNT`Diy}uZ+AqKhiCxL0h!$}Xh5dSdSqEqXMC%1@ z7(Y)68g5DWyEY7M5Mo0daTtl5C}rKxAVH)97xBQ!yFcp*NjN?ahYLZWEm;@n30^RadmOzBwL)?=62O?vvC+5eI zx<5Aw@kI&BTQ3^GRT%I(hCGKP(_ftKci<{J)CDPm3Gc#;TPlwfhlkUTtc$Swt*~k2 zfHTTuCZHnp2;x#s8g$VH^{y&9s1|e+3r2=@-?yvjJf5E74VED(zt$%b)=_AlIfN7|p z8W#-Ph(w1=Oz*Md1QI@y%z`!o2jbB{)F*#vUK5y6=q!gA$dgTg2*<7@}z zqi5YIgNtP$tbN&mz!umHB@=-K40QEd|12rwe_z#E)Fn07hJ5?qLmwcy*bY=5++4DP zcqh-lp=4oy@co<(Aora4_+s2W3Ab;0g__ti=pQpkY|!Hn{j&d&m@rYdfM5_(MhJW8 z3u4ZWl_ZFav0i>yxY$bH&)se@$r@q}F^&FId_WZqROsQmnt}w?&&mEmH4<*kg)za~ zz0g;J;P7gw{?diZYlu4KKTlL3_qQQ86fP=JU$`;_E1~l~>2w4LeLS5}^J2u$^)0Ty zor51f13N>nF7y+F_`MfjaJf{7O%5O0O`U*sZaBC`?%bf&%SKq#b9!7$LFt<_MG2qCiw=lS&)0XvWe?t6|3XD@#E~$ZTRls}sQM%>SB8Cl9e`1$aAqvRhd~qcjpf11{ zZ3MTpi9j0sxVDg_ck#IypFrGU4G4?gOGwZmg8F+IljvRoB5exY%{}pe`y9ULaC?3` zQQY5VMLKSssSMKM0lf}F-=SSBb_X$N508JIpdxIuqCFb%&kx80{*4MepL_lqaFPdQ7lo)}xGvdd;Bg7x}|F7mOmxnX@WFJ8BXagC7 zM2}$nG5ax25_7+WDG2kwI)T5kED3=6W4xU)s3Zi*&BXgPqE3QdDL^{3w+#~qQODwf zRNUN%4QdE(srsal8@Pl&1{W{GBJJ;#hp=hf`S?{S z9AfYd*nC8fo!8)vM?A&;)Pd%&tIGd;Ics`bs^N3c%yHr4ub@cuS}$e}C{^#rtx+?0ckX{9)#h(8uV^^o@yk*#Hu;mFBrG;&iU5#bgh6RF>hytm~ca*ft1JmjdDMIsX^(hiK#$U%

    y3E z293 ztf@XDj!67oeWb{A{xj#CC``F#BA9MS=ZQP`IvDO)4qXoWVoNNSFz<(Dl6K^O{vYv% BkMRHi diff --git a/lib/ostermillerutils_1_07_00.jar b/lib/ostermillerutils_1_07_00.jar deleted file mode 100644 index a9931503cb366bd2b378faebc2ba7ef847d93be7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 525947 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

    Some of the internal features include RSS feed backlogs, rolling logs, debugging toggle and much more.

    From 153363a3201144dfaf4a0c2c9241ef940ad60182 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 24 Apr 2014 06:07:45 -0700 Subject: [PATCH 007/842] Code cleanup. More gradle integration. Implemented random quote via iheartquotes.com Implemented war game. --- .gitignore | 1 + README.txt | 14 +- build.gradle | 102 +- build.properties | 12 - buildnum.properties | 4 +- licenses/License.txt | 2 +- mobibot.iml | 4 +- mobibot.ipr | 12 +- mobibot.iws | 1010 +++++++++-------- .../erik/mobibot/CurrencyConverter.java | 68 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 20 +- .../thauvin/erik/mobibot/EntryComment.java | 24 +- .../net/thauvin/erik/mobibot/EntryLink.java | 118 +- .../net/thauvin/erik/mobibot/FeedReader.java | 28 +- .../thauvin/erik/mobibot/GoogleSearch.java | 24 +- .../net/thauvin/erik/mobibot/Mobibot.java | 547 +++++---- .../java/net/thauvin/erik/mobibot/Quote.java | 109 ++ .../net/thauvin/erik/mobibot/ReleaseInfo.java | 18 +- .../net/thauvin/erik/mobibot/StockQuote.java | 38 +- .../net/thauvin/erik/mobibot/Twitter.java | 40 +- .../net/thauvin/erik/mobibot/Weather.java | 52 +- website/index.html | 158 ++- website/simple.css | 7 +- 23 files changed, 1408 insertions(+), 1004 deletions(-) delete mode 100644 build.properties create mode 100644 src/main/java/net/thauvin/erik/mobibot/Quote.java diff --git a/.gitignore b/.gitignore index 4f84b93..477820a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ /proguard-project.txt /project.properties /test-output +/weather.log CVS diff --git a/README.txt b/README.txt index dca5b6a..1288bbf 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,12 @@ Some very basic instructions: - ./gradlew deploy + { clone with git or download the ZIP} + git clone git://github.com/ethauvin/mobibot.git + + cd mobibot + + { build with gradle } + ./gradlew cd deploy @@ -9,7 +15,9 @@ Some very basic instructions: { help } java -jar mobibot.jar -h - + + { twitter oauth token request } + java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth + { launch } /usr/bin/nohup java -jar mobibot.jar & - \ No newline at end of file diff --git a/build.gradle b/build.gradle index 2c96161..2bb6d78 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,14 @@ apply plugin: 'java' apply plugin: 'idea' -version = '0.5' -ext.packageName = 'net.thauvin.erik.mobibot' -ext.mainClassName = packageName + '.Mobibot' -ext.deployDir = 'deploy' +defaultTasks 'deploy' + +version = '0.6' + +def packageName = 'net.thauvin.erik.mobibot' +def mainClassName = packageName + '.Mobibot' +def deployDir = 'deploy' +def isRelease = 'release' in gradle.startParameter.taskNames repositories { mavenCentral() @@ -40,51 +44,67 @@ dependencies { //compile files('../path/to/example.jar') } -task wrapper(type: Wrapper) { - gradleVersion = gradle.gradleVersion -} - compileJava { - dependsOn wrapper doFirst { - ant.taskdef(name: 'jreleaseinfo', - classname: 'ch.oscg.jreleaseinfo.anttask.JReleaseInfoAntTask', - classpath: 'ant/jreleaseinfo-1.3.0.jar') - ant.jreleaseinfo(targetDir: file('src/main/java'), - className: 'ReleaseInfo', - packageName: packageName, - project: rootProject.name, - version: version, - buildnumfile: file('buildnum.properties')) + if (isRelease) + { + ant.taskdef(name: 'jreleaseinfo', + classname: 'ch.oscg.jreleaseinfo.anttask.JReleaseInfoAntTask', + classpath: 'ant/jreleaseinfo-1.3.0.jar') + ant.jreleaseinfo(targetDir: file('src/main/java'), + className: 'ReleaseInfo', + packageName: packageName, + project: rootProject.name, + version: version, + buildnumfile: file('buildnum.properties')) + + } + } + //options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } jar { - doFirst { - manifest { - attributes("Manifest-Version": "1.0", - "Main-Class": mainClassName, - "Class-Path": '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) - } - } + manifest.attributes( + 'Main-Class': mainClassName, + 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) version = null } -task deploy(dependsOn: build) { +clean { + delete deployDir +} + +task wrapper(type: Wrapper) { + gradleVersion = gradle.gradleVersion +} + +task copyToDeploy(type: Copy) { + from('properties') { + include '*.properties' + } + from jar + into deployDir +} + +task copyToDeployLib(type: Copy) { + configurations.runtime + into deployDir + '/lib' +} + +task deploy(dependsOn: ['build', 'copyToDeploy', 'copyToDeployLib']) { description = "Copies all needed files to the ${deployDir} directory." - copy { - into deployDir + '/lib' - from configurations.runtime - } - copy { - from 'properties' - into deployDir - include('*.properties') - } - copy { - from jar - into deployDir - } - file(deployDir + '/logs').mkdirs(); -} \ No newline at end of file + group = "Publishing" + outputs.dir deployDir + inputs.files copyToDeploy + inputs.files copyToDeployLib + mustRunAfter clean + file(deployDir + '/logs').mkdir() +} + +task release(dependsOn: ['deploy', 'wrapper']) { + group = "Publishing" + description = "Releases new version." + isRelease = true +} diff --git a/build.properties b/build.properties deleted file mode 100644 index 023fd99..0000000 --- a/build.properties +++ /dev/null @@ -1,12 +0,0 @@ -# Project -proj.name=mobibot -proj.version=0.5 -proj.package=net.thauvin.erik.mobibot -proj.run=${proj.package}.Mobibot - -# Locations -path.classes=build -path.src=src -path.dist=dist -path.lib=lib -path.ant=ant \ No newline at end of file diff --git a/buildnum.properties b/buildnum.properties index d4904de..a9ea87f 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Sat Apr 19 21:14:33 PDT 2014 -build.num.last=40 +#Sun Apr 20 23:26:28 PDT 2014 +build.num.last=0 diff --git a/licenses/License.txt b/licenses/License.txt index 29ab484..07d745b 100644 --- a/licenses/License.txt +++ b/licenses/License.txt @@ -1,6 +1,6 @@ Mobibot License -Copyright (c) 2004-2010, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/mobibot.iml b/mobibot.iml index dd01139..66b7d51 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -1,5 +1,5 @@ - + @@ -24,12 +24,12 @@ - + diff --git a/mobibot.ipr b/mobibot.ipr index 9f63751..11c9875 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -109,10 +109,9 @@ - @@ -641,7 +640,7 @@ - + @@ -775,13 +774,6 @@ - - - - - - - diff --git a/mobibot.iws b/mobibot.iws index a3687b2..b569a16 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -1,7 +1,6 @@ - @@ -34,56 +33,29 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + @@ -118,7 +90,6 @@ - + + + + + + + + + + - - - - - - + + + + + + + + + + + + + @@ -203,21 +191,8 @@ - - - - - - - - - - - - - - - + + @@ -261,6 +236,20 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -561,10 +684,10 @@ - @@ -1241,6 +1364,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1281,130 +1528,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1450,17 +1573,17 @@ - + - + - + @@ -1482,12 +1605,14 @@ + + @@ -1541,6 +1666,25 @@ + + + + + + + + + @@ -1808,7 +1952,7 @@ - + + - + - + + @@ -1939,11 +2085,25 @@ + + + + + + + + + + + + + + - @@ -1966,31 +2126,30 @@ - - + + - + - - + - + - - - - + + + + - - + + @@ -2004,10 +2163,11 @@ + - + @@ -2016,7 +2176,7 @@ - + @@ -2104,46 +2264,6 @@
    on " + entry.getChannel() + "" ); @@ -2878,7 +2981,7 @@ public class Mobibot extends PircBot item.setDescription(description); item.setTitle(entry.getTitle()); item.setPublishedDate(entry.getDate()); - item.setAuthor(getChannel().substring(1) + '@' + _ircServer + " (" + entry.getNick() + ')'); + item.setAuthor(getChannel().substring(1) + '@' + ircServer + " (" + entry.getNick() + ')'); item.setCategories(entry.getTags()); items.add(item); @@ -2886,50 +2989,50 @@ public class Mobibot extends PircBot rss.setEntries(items); - if (_logger.isDebugEnabled()) + if (logger.isDebugEnabled()) { - _logger.debug("Writing the entries feed."); + logger.debug("Writing the entries feed."); } final SyndFeedOutput output = new SyndFeedOutput(); output.output(rss, fw); fw.close(); - fw = new FileWriter(new File(_logsDir + getToday() + ".xml")); + fw = new FileWriter(new File(logsDir + getToday() + ".xml")); output.output(rss, fw); if (isDayBackup) { - if (isValidString(_backlogsURL)) + if (isValidString(backlogsURL)) { - if (_history.indexOf(getToday()) == -1) + if (history.indexOf(getToday()) == -1) { - _history.add(getToday()); + history.add(getToday()); - while (_history.size() > MAX_BACKLOGS) + while (history.size() > MAX_BACKLOGS) { - _history.remove(0); + history.remove(0); } } fw.close(); - fw = new FileWriter(new File(_logsDir + NAV_XML)); + fw = new FileWriter(new File(logsDir + NAV_XML)); rss = new SyndFeedImpl(); rss.setFeedType("rss_2.0"); rss.setTitle(getChannel() + " IRC Links Backlogs"); - rss.setDescription("Backlogs of Links from " + _ircServer + " on " + getChannel()); - rss.setLink(_backlogsURL); + rss.setDescription("Backlogs of Links from " + ircServer + " on " + getChannel()); + rss.setLink(backlogsURL); rss.setPublishedDate(Calendar.getInstance().getTime()); String date; items.clear(); - for (int i = (_history.size() - 1); i >= 0; --i) + for (int i = (history.size() - 1); i >= 0; --i) { - date = (String) _history.get(i); + date = history.get(i); item = new SyndEntryImpl(); - item.setLink(_backlogsURL + date + ".xml"); + item.setLink(backlogsURL + date + ".xml"); item.setTitle(date); description = new SyndContentImpl(); description.setValue("Links for " + date); @@ -2940,22 +3043,22 @@ public class Mobibot extends PircBot rss.setEntries(items); - if (_logger.isDebugEnabled()) + if (logger.isDebugEnabled()) { - _logger.debug("Writing the backlog feed."); + logger.debug("Writing the backlog feed."); } output.output(rss, fw); } else { - _logger.warn("Unable to generate the backlogs feed. No property configured."); + logger.warn("Unable to generate the backlogs feed. No property configured."); } } } catch (Exception e) { - _logger.warn("Unable to generate the feed.", e); + logger.warn("Unable to generate the feed.", e); } finally { @@ -2974,7 +3077,7 @@ public class Mobibot extends PircBot } else { - _logger.warn("Unable to generate the feed. At least one of the required property is missing."); + logger.warn("Unable to generate the feed. At least one of the required property is missing."); } } @@ -2985,7 +3088,7 @@ public class Mobibot extends PircBot */ private void setBacklogsURL(String backlogsURL) { - _backlogsURL = backlogsURL; + this.backlogsURL = backlogsURL; } /** @@ -2996,7 +3099,7 @@ public class Mobibot extends PircBot */ private void setDeliciousAuth(String username, String password) { - _delicious = new DeliciousPoster(username, password, _ircServer); + delicious = new DeliciousPoster(username, password, ircServer); } /** @@ -3006,7 +3109,7 @@ public class Mobibot extends PircBot */ private void setFeedURL(String feedURL) { - _feedURL = feedURL; + this.feedURL = feedURL; } /** @@ -3019,10 +3122,10 @@ public class Mobibot extends PircBot */ private void setTwitterAuth(String consumerKey, String consumerSecret, String token, String tokenSecret) { - _twitterConsumerKey = consumerKey; - _twitterConsumerSecret = consumerSecret; - _twitterToken = token; - _twitterTokenSecret = tokenSecret; + twitterConsumerKey = consumerKey; + twitterConsumerSecret = consumerSecret; + twitterToken = token; + twitterTokenSecret = tokenSecret; } /** @@ -3032,7 +3135,7 @@ public class Mobibot extends PircBot */ private void setIdent(String pwd) { - _ident = pwd; + ident = pwd; } /** @@ -3042,7 +3145,7 @@ public class Mobibot extends PircBot */ private void setIdentMsg(String msg) { - _identMsg = msg; + identMsg = msg; } /** @@ -3052,7 +3155,7 @@ public class Mobibot extends PircBot */ private void setIdentNick(String nick) { - _identNick = nick; + identNick = nick; } /** @@ -3068,7 +3171,7 @@ public class Mobibot extends PircBot while (st.hasMoreTokens()) { - _ignoredNicks.add(st.nextToken().trim().toLowerCase()); + ignoredNicks.add(st.nextToken().trim().toLowerCase()); } } } @@ -3080,7 +3183,7 @@ public class Mobibot extends PircBot */ private void setTags(String tags) { - _defaultTags = tags; + defaultTags = tags; } /** @@ -3090,7 +3193,7 @@ public class Mobibot extends PircBot */ private void setWeblogURL(String weblogURL) { - _weblogURL = weblogURL; + this.weblogURL = weblogURL; } /** @@ -3121,7 +3224,7 @@ public class Mobibot extends PircBot private void timeResponse(String sender, String args, boolean isPrivate) { boolean isInvalidTz = false; - final String tz = ((String) COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); + final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); final String response; if (tz != null) @@ -3178,16 +3281,16 @@ public class Mobibot extends PircBot Arrays.sort(nicks, String.CASE_INSENSITIVE_ORDER); - final StringBuffer buff = new StringBuffer(0); + final StringBuilder buff = new StringBuilder(0); - for (int i = 0; i < nicks.length; i++) + for (final String nick : nicks) { - if (isOp(nicks[i])) + if (isOp(nick)) { buff.append('@'); } - buff.append(nicks[i]).append(' '); + buff.append(nick).append(' '); } send(sender, buff.toString(), isPrivate); @@ -3204,9 +3307,9 @@ public class Mobibot extends PircBot { String lcArgs = args.toLowerCase(); - if (!_entries.isEmpty()) + if (!entries.isEmpty()) { - final int max = _entries.size(); + final int max = entries.size(); int i = 0; if (!(lcArgs.length() > 0) && (max > MAX_ENTRIES)) @@ -3252,13 +3355,13 @@ public class Mobibot extends PircBot for (; i < max; i++) { - entry = (EntryLink) _entries.get(i); + entry = entries.get(i); if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase().indexOf(lcArgs) != -1) || ( - entry.getTitle().toLowerCase().indexOf(lcArgs) != -1) || ( - entry.getNick().toLowerCase().indexOf(lcArgs) != -1)) + if ((entry.getLink().toLowerCase().contains(lcArgs)) || + (entry.getTitle().toLowerCase().contains(lcArgs)) || + (entry.getNick().toLowerCase().contains(lcArgs))) { if (sent > MAX_ENTRIES) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Quote.java b/src/main/java/net/thauvin/erik/mobibot/Quote.java new file mode 100644 index 0000000..a839584 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Quote.java @@ -0,0 +1,109 @@ +/* + * @(#)Quote.java + * + * Copyright (c) 2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id$ + * + */ +package net.thauvin.erik.mobibot; + +import org.jibble.pircbot.Colors; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; + +/** + * Retrieve quote from I Heart Quotes + * + * @author Erik C. Thauvin + * @created 2014-04-20 + * @since 1.0 + */ +public class Quote implements Runnable +{ + /** + * The bot. + */ + private final Mobibot bot; + + /** + * The nick of the person who sent the message. + */ + private final String sender; + + /** + * Creates a new StockQuote object. + * + * @param bot The bot. + * @param sender The nick of the person who sent the message. + */ + public Quote(Mobibot bot, String sender) + { + this.bot = bot; + this.sender = sender; + } + + /** + * Returns a random quote. + */ + public final void run() + { + try + { + final URL url = new URL("http://www.iheartquotes.com/api/v1/random?format=json&max_lines=1"); + final URLConnection conn = url.openConnection(); + + final StringBuilder sb = new StringBuilder(); + final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + + String line; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + + final JSONObject json = new JSONObject(sb.toString()); + + bot.send(bot.getChannel(), Colors.BLUE + json.getString("quote") + Colors.BLUE); + + reader.close(); + } + catch (Exception e) + { + bot.getLogger().warn("Unable to retrieve random quote.", e); + bot.send(sender, "An error has occurred: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 76505fd..d8ebe4e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Sat Apr 19 21:14:33 PDT 2014 */ +/* Creation date Sun Apr 20 23:26:28 PDT 2014 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,11 +20,11 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1397967273141L). */ - private static final Date buildDate = new Date(1397967273141L); + /** buildDate (set during build process to 1398061588708L). */ + private static final Date buildDate = new Date(1398061588708L); /** - * Get buildDate (set during build process to Sat Apr 19 21:14:33 PDT 2014). + * Get buildDate (set during build process to Sun Apr 20 23:26:28 PDT 2014). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } @@ -40,20 +40,20 @@ public class ReleaseInfo { public static String getProject() { return project; } - /** version (set during build process to "0.5"). */ - private static final String version = "0.5"; + /** version (set during build process to "0.6"). */ + private static final String version = "0.6"; /** - * Get version (set during build process to "0.5"). + * Get version (set during build process to "0.6"). * @return String version */ public static String getVersion() { return version; } /** - * Get buildNumber (set during build process to 40). + * Get buildNumber (set during build process to 0). * @return int buildNumber */ - public static int getBuildNumber() { return 40; } + public static int getBuildNumber() { return 0; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java index 6eb946b..f453f3c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java @@ -60,17 +60,17 @@ public class StockQuote implements Runnable /** * The bot. */ - private final Mobibot _bot; + private final Mobibot bot; /** * The nick of the person who sent the message. */ - private final String _sender; + private final String sender; /** * The stock symbol. */ - private final String _symbol; + private final String symbol; /** * Creates a new StockQuote object. @@ -81,9 +81,9 @@ public class StockQuote implements Runnable */ public StockQuote(Mobibot bot, String sender, String symbol) { - _bot = bot; - _sender = sender; - _symbol = symbol; + this.bot = bot; + this.sender = sender; + this.symbol = symbol; } /** @@ -97,7 +97,7 @@ public class StockQuote implements Runnable client.getHttpConnectionManager().getParams().setConnectionTimeout(Mobibot.CONNECT_TIMEOUT); client.getHttpConnectionManager().getParams().setSoTimeout(Mobibot.CONNECT_TIMEOUT); - final GetMethod getMethod = new GetMethod(YAHOO_URL + _symbol.toUpperCase()); + final GetMethod getMethod = new GetMethod(YAHOO_URL + symbol.toUpperCase()); client.executeMethod(getMethod); final String[][] lines = CSVParser.parse(getMethod.getResponseBodyAsString()); @@ -110,56 +110,56 @@ public class StockQuote implements Runnable { if ((quote.length > 3) && (!"N/A".equalsIgnoreCase(quote[3]))) { - _bot.send(_bot.getChannel(), "Symbol: " + quote[0] + " [" + quote[1] + ']'); + bot.send(bot.getChannel(), "Symbol: " + quote[0] + " [" + quote[1] + ']'); if (quote.length > 5) { - _bot.send(_bot.getChannel(), "Last Trade: " + quote[2] + " (" + quote[5] + ')'); + bot.send(bot.getChannel(), "Last Trade: " + quote[2] + " (" + quote[5] + ')'); } else { - _bot.send(_bot.getChannel(), "Last Trade: " + quote[2]); + bot.send(bot.getChannel(), "Last Trade: " + quote[2]); } if (quote.length > 4) { - _bot.send(_sender, "Time: " + quote[3] + ' ' + quote[4]); + bot.send(sender, "Time: " + quote[3] + ' ' + quote[4]); } if (quote.length > 6 && !"N/A".equalsIgnoreCase(quote[6])) { - _bot.send(_sender, "Open: " + quote[6]); + bot.send(sender, "Open: " + quote[6]); } if (quote.length > 7 && !"N/A".equalsIgnoreCase(quote[7]) && !"N/A".equalsIgnoreCase(quote[8])) { - _bot.send(_sender, "Day's Range: " + quote[7] + " - " + quote[8]); + bot.send(sender, "Day's Range: " + quote[7] + " - " + quote[8]); } if (quote.length > 9 && !"0".equalsIgnoreCase(quote[9])) { - _bot.send(_sender, "Volume: " + quote[9]); + bot.send(sender, "Volume: " + quote[9]); } } else { - _bot.send(_sender, "Invalid ticker symbol."); + bot.send(sender, "Invalid ticker symbol."); } } else { - _bot.send(_sender, "No values returned."); + bot.send(sender, "No values returned."); } } else { - _bot.send(_sender, "No data returned."); + bot.send(sender, "No data returned."); } } catch (IOException e) { - _bot.getLogger().debug("Unable to retrieve stock quote for: " + _symbol, e); - _bot.send(_sender, "An error has occurred: " + e.getMessage()); + bot.getLogger().debug("Unable to retrieve stock quote for: " + symbol, e); + bot.send(sender, "An error has occurred: " + e.getMessage()); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/Twitter.java index 74123fb..6742092 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/Twitter.java @@ -53,37 +53,37 @@ public class Twitter implements Runnable /** * The bot. */ - private final Mobibot _bot; + private final Mobibot bot; /** * The Twitter consumer secret. */ - private final String _consumerSecret; + private final String consumerSecret; /** * The Twitter consumer key. */ - private final String _consumerKey; + private final String consumerKey; /** * The Twitter message. */ - private final String _message; + private final String message; /** * The Twitter access token. */ - private final String _accessToken; + private final String accessToken; /** * The Twitter access token secret. */ - private final String _accessTokenSecret; + private final String accessTokenSecret; /** * The nick of the person who sent the message. */ - private final String _sender; + private final String sender; /** * Creates a new Twitter object. @@ -99,13 +99,13 @@ public class Twitter implements Runnable public Twitter(Mobibot bot, String sender, String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret, String message) { - _bot = bot; - _consumerKey = consumerKey; - _consumerSecret = consumerSecret; - _accessToken = accessToken; - _accessTokenSecret = accessTokenSecret; - _message = message; - _sender = sender; + this.bot = bot; + this.consumerKey = consumerKey; + this.consumerSecret = consumerSecret; + this.accessToken = accessToken; + this.accessTokenSecret = accessTokenSecret; + this.message = message; + this.sender = sender; } public final void run() @@ -113,22 +113,22 @@ public class Twitter implements Runnable try { final ConfigurationBuilder cb = new ConfigurationBuilder(); - cb.setDebugEnabled(true).setOAuthConsumerKey(_consumerKey).setOAuthConsumerSecret(_consumerSecret) - .setOAuthAccessToken(_accessToken).setOAuthAccessTokenSecret(_accessTokenSecret); + cb.setDebugEnabled(true).setOAuthConsumerKey(consumerKey).setOAuthConsumerSecret(consumerSecret) + .setOAuthAccessToken(accessToken).setOAuthAccessTokenSecret(accessTokenSecret); final TwitterFactory tf = new TwitterFactory(cb.build()); final twitter4j.Twitter twitter = tf.getInstance(); - final Status status = twitter.updateStatus(_message + " (" + _sender + ')'); + final Status status = twitter.updateStatus(message + " (" + sender + ')'); - _bot.send(_sender, + bot.send(sender, "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status .getId() ); } catch (Exception e) { - _bot.getLogger().warn("Unable to post to Twitter: " + _message, e); - _bot.send(_sender, "An error has occurred: " + e.getMessage()); + bot.getLogger().warn("Unable to post to Twitter: " + message, e); + bot.send(sender, "An error has occurred: " + e.getMessage()); } } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/Weather.java index 6a1325c..9b264ec 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/Weather.java @@ -67,22 +67,22 @@ public class Weather implements Runnable /** * The bot. */ - private final Mobibot _bot; + private final Mobibot bot; /** * The nick of the person who sent the message. */ - private final String _sender; + private final String sender; /** * The station ID. */ - private final String _station; + private final String station; /** * The private message flag. */ - private final boolean _isPrivate; + private final boolean isPrivate; /** * Creates a new Weather object. @@ -94,10 +94,10 @@ public class Weather implements Runnable */ public Weather(Mobibot bot, String sender, String station, boolean isPrivate) { - _bot = bot; - _sender = sender; - _station = station.toUpperCase(); - _isPrivate = isPrivate; + this.bot = bot; + this.sender = sender; + this.station = station.toUpperCase(); + this.isPrivate = isPrivate; } /** @@ -105,29 +105,28 @@ public class Weather implements Runnable */ public final void run() { - if (_station.length() == 4) + if (station.length() == 4) { - final Metar metar = net.sf.jweather.Weather.getMetar(_station); + final Metar metar = net.sf.jweather.Weather.getMetar(station); if (metar != null) { Float result; - _bot.send(_sender, "Station ID: " + metar.getStationID(), _isPrivate); + bot.send(sender, "Station ID: " + metar.getStationID(), isPrivate); - _bot.send(_sender, + bot.send(sender, "At: " + metar.getDateString() + " UTC (" + ( ((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + " minutes ago)", - _isPrivate + isPrivate ); result = metar.getWindSpeedInMPH(); if (result != null) { - _bot.send(_sender, - "Wind Speed: " + result + " mph, " + metar.getWindSpeedInKnots() + " knots", - _isPrivate); + bot.send(sender, + "Wind Speed: " + result + " mph, " + metar.getWindSpeedInKnots() + " knots", isPrivate); } result = metar.getVisibility(); @@ -136,11 +135,11 @@ public class Weather implements Runnable { if (!metar.getVisibilityLessThan()) { - _bot.send(_sender, "Visibility: " + NUMBER_FORMAT.format(result) + " mile(s)", _isPrivate); + bot.send(sender, "Visibility: " + NUMBER_FORMAT.format(result) + " mile(s)", isPrivate); } else { - _bot.send(_sender, "Visibility: < " + NUMBER_FORMAT.format(result) + " mile(s)", _isPrivate); + bot.send(sender, "Visibility: < " + NUMBER_FORMAT.format(result) + " mile(s)", isPrivate); } } @@ -148,26 +147,26 @@ public class Weather implements Runnable if (result != null) { - _bot.send(_sender, "Pressure: " + result + " in Hg", _isPrivate); + bot.send(sender, "Pressure: " + result + " in Hg", isPrivate); } result = metar.getTemperatureInCelsius(); if (result != null) { - _bot.send(_sender, - "Temperature: " + result + " C, " + metar.getTemperatureInFahrenheit() + " F", - _isPrivate); + bot.send(sender, + "Temperature: " + result + " C, " + metar.getTemperatureInFahrenheit() + " F", isPrivate); } if (metar.getWeatherConditions() != null) { final Iterator it = metar.getWeatherConditions().iterator(); + //noinspection WhileLoopReplaceableByForEach while (it.hasNext()) { final WeatherCondition weatherCondition = (WeatherCondition) it.next(); - _bot.send(_sender, weatherCondition.getNaturalLanguageString(), _isPrivate); + bot.send(sender, weatherCondition.getNaturalLanguageString(), isPrivate); } } @@ -175,10 +174,11 @@ public class Weather implements Runnable { final Iterator it = metar.getSkyConditions().iterator(); + //noinspection WhileLoopReplaceableByForEach while (it.hasNext()) { final SkyCondition skyCondition = (SkyCondition) it.next(); - _bot.send(_sender, skyCondition.getNaturalLanguageString(), _isPrivate); + bot.send(sender, skyCondition.getNaturalLanguageString(), isPrivate); } } @@ -186,12 +186,12 @@ public class Weather implements Runnable } else { - _bot.send(_sender, "Invalid Station ID. Please try again.", _isPrivate); + bot.send(sender, "Invalid Station ID. Please try again.", isPrivate); return; } } - _bot.helpResponse(_sender, Mobibot.WEATHER_CMD); + bot.helpResponse(sender, Mobibot.WEATHER_CMD); } } diff --git a/website/index.html b/website/index.html index 6c485cd..d8e459b 100644 --- a/website/index.html +++ b/website/index.html @@ -1,56 +1,114 @@ -mobibot - - + mobibot + + + - -
    -

    mobibot

    -

    The #mobitopia bot

    -

    The latest version of mobibot is always available via CVS.

    -

    About mobibot

    -

    mobibot is the #mobitopia IRC channel bot. It is built on Paul Mutton's PircBot Java-based Framework.

    -

    mobibot is making extensive use of various open source libraries, including:

    - -

    mobibot was written by Erik C. Thauvin as a replacement for the channel's original ChumpBot.

    -

    Features

    -

    mobibot's main functionality is to capture URLs posted on the channel. The URLs are automatically gathered into a publishable RSS feed.

    -

    Other features include:

    -
      -
    • Performing calculations
    • -
    • Converting between currencies
    • -
    • Rolling dice
    • -
    • Performing Google searches
    • -
    • Displaying the latest entries on Mobitopia
    • -
    • Performing DNS lookups
    • -
    • Recapping public channel messages
    • -
    • Retrieving stock quotes
    • -
    • Displaying the time in various time zones
    • -
    • Listing the users on the channel
    • -
    • Displaying weather information
    • -
    • Posting to Twitter
    • -
    -

    Some of the internal features include RSS feed backlogs, rolling logs, debugging toggle and much more.

    -

    If you have any feature suggestions, please post them to the mobibot wiki.

    -

    Using mobibot

    -

    To use mobibot, simply join #mobitopia on irc.freenode.net and type:

    -

    mobibot: help

    -

    mobibot will reply with a listing of the commands currently supported.

    -

    Licenses

    -

    There are various open source licenses attached to mobibot. Please refer to the licenses directory in the CVS tree for more details.

    -
    + +
    +

    mobibot

    + +

    The #mobitopia bot

    + +

    The latest version of mobibot is always available via + GitHub.

    + +

    About mobibot

    + +

    mobibot is the + #mobitopia IRC channel bot. It is built on Paul Mutton's + PircBot Java-based Framework.

    + +

    mobibot is making extensive use of various open source libraries, including:

    + +

    mobibot was written by + Erik C. Thauvin as a replacement for the channel's original + ChumpBot.

    + +

    Features

    + +

    mobibot's main functionality is to + capture URLs posted on the channel. The URLs are automatically gathered into a publishable + RSS feed.

    + +

    Other features include:

    +
      +
    • Displaying the latest entries on Mobitopia
    • +
    • Performing calculations +
      mobibot: calc (floor(sqrt(3))+3.14)*3^2
      +
    • +
    • Converting between currencies +
      mobibot: currency 17.54 USD to EUR
      +
    • +
    • Performing Google searches +
      mobibot: google mobitopia on irc
      +
    • +
    • Displaying weather information +
      mobibot: weather KSFO
      +
    • +
    • Performing DNS lookups +
      mobibot: lookup www.apple.com
      +
    • +
    • Retrieving stock quotes +
      mobibot: stock GOOG
      +
    • +
    • Displaying the time in various time zones +
      mobibot: time UK
      +
    • +
    • Recapping public channel messages
    • +
    • Listing the users on the channel
    • +
    • Random quotes from I Heart Quotes
    • +
    • Rolling dice and playing war
    • +
    • Posting to Twitter
    • +
    +

    Some of the internal features include RSS feed backlogs, rolling logs, debugging toggle and much more.

    + +

    If you have any feature suggestions, please post them to the + mobibot wiki.

    + +

    Using mobibot

    + +

    To use mobibot, simply join #mobitopia on + irc.freenode.net and type:

    + +

    mobibot: help

    + +

    mobibot will reply with a listing of the commands currently supported.

    + +

    Licenses

    + +

    There are various open source licenses attached to mobibot. Please refer to the + licenses directory in the source tree for more details. +

    +
    diff --git a/website/simple.css b/website/simple.css index 4092d3f..40178ff 100644 --- a/website/simple.css +++ b/website/simple.css @@ -3,7 +3,7 @@ body { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px; color: #000000; - margin: 0px; + margin: 0; } h1 { @@ -24,6 +24,7 @@ code { font-size: 12px; color: #000066; background-color: #ffeedd; + margin-left: 20px } a:hover { @@ -34,8 +35,8 @@ a:hover { #content { float: none; position: relative; - margin: 0px 10px 10px 10px; - padding: 0px 10px 10px 10px; + margin: 0 10px 10px 10px; + padding: 0 10px 10px 10px; } #content p { From e31a22af114ef1f7b1fadbcff6d036e1714e8bf9 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 28 Apr 2014 19:41:31 -0700 Subject: [PATCH 008/842] Implemented Entries manager. Implemented Messages manager. Enabled caching of the currency exchange rates. Moved commands, utils, etc. to separate classes. Added more weather data. --- build.gradle | 8 +- buildnum.properties | 2 +- mobibot.ipr | 13 +- mobibot.iws | 1001 +++++--- properties/mobibot.properties | 3 + .../net/thauvin/erik/mobibot/Commands.java | 256 ++ .../erik/mobibot/CurrencyConverter.java | 208 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 61 +- .../java/net/thauvin/erik/mobibot/Dice.java | 97 + .../net/thauvin/erik/mobibot/EntriesMgr.java | 384 +++ .../thauvin/erik/mobibot/EntryComment.java | 9 +- .../net/thauvin/erik/mobibot/EntryLink.java | 130 +- .../net/thauvin/erik/mobibot/FeedReader.java | 7 +- .../thauvin/erik/mobibot/GoogleSearch.java | 12 +- .../java/net/thauvin/erik/mobibot/Lookup.java | 162 ++ .../net/thauvin/erik/mobibot/Mobibot.java | 2254 ++++++----------- .../java/net/thauvin/erik/mobibot/Quote.java | 17 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 136 +- .../net/thauvin/erik/mobibot/StockQuote.java | 7 +- .../net/thauvin/erik/mobibot/SwingWorker.java | 246 +- .../net/thauvin/erik/mobibot/TellMessage.java | 146 ++ .../thauvin/erik/mobibot/TellMessagesMgr.java | 156 ++ .../net/thauvin/erik/mobibot/Twitter.java | 11 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 1 - .../java/net/thauvin/erik/mobibot/Utils.java | 309 +++ .../java/net/thauvin/erik/mobibot/War.java | 110 + .../net/thauvin/erik/mobibot/Weather.java | 40 +- .../net/thauvin/erik/mobibot/WorldTime.java | 190 ++ 28 files changed, 3778 insertions(+), 2198 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/Commands.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/Dice.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/Lookup.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/TellMessage.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/Utils.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/War.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/WorldTime.java diff --git a/build.gradle b/build.gradle index 2bb6d78..2e93f3a 100644 --- a/build.gradle +++ b/build.gradle @@ -61,13 +61,12 @@ compileJava { } } - //options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } jar { - manifest.attributes( - 'Main-Class': mainClassName, - 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) + manifest.attributes('Main-Class': mainClassName, + 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) version = null } @@ -106,5 +105,4 @@ task deploy(dependsOn: ['build', 'copyToDeploy', 'copyToDeployLib']) { task release(dependsOn: ['deploy', 'wrapper']) { group = "Publishing" description = "Releases new version." - isRelease = true } diff --git a/buildnum.properties b/buildnum.properties index a9ea87f..8ff75c6 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Sun Apr 20 23:26:28 PDT 2014 +#Fri Apr 25 18:08:16 PDT 2014 build.num.last=0 diff --git a/mobibot.ipr b/mobibot.ipr index 11c9875..c5eb45b 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -70,7 +70,18 @@ - + + + + + + + diff --git a/mobibot.iws b/mobibot.iws index b569a16..591e057 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -1,7 +1,7 @@ - - - - + + + + + + + + + - - + + + - - - + @@ -158,11 +163,11 @@ - - + + - - + + @@ -171,28 +176,91 @@ - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -250,6 +318,20 @@ + + + + + + + + + @@ -684,10 +781,10 @@ - @@ -696,10 +793,28 @@ + + Abstraction issues + + + Declaration redundancy + + + JUnit issues + + + JavaScript + + + JavaScript function metricsJavaScript + + + Performance issues + - Abstraction issues + WeakerAccess @@ -1364,6 +1479,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1413,6 +1568,32 @@ + + + + + + + + + + + @@ -1458,6 +1639,34 @@ + + + + + + + + + + + + + + + + @@ -1488,46 +1697,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1573,10 +1742,10 @@ - + - + @@ -1606,11 +1775,21 @@ + + + + + + + + + + @@ -1627,7 +1806,7 @@ - + + + + + + + + + + @@ -1987,18 +2185,20 @@ - + + - - + + - + + @@ -2020,6 +2220,11 @@ + + + - @@ -2126,59 +2349,111 @@ - - + + - + - - + + - - + + - - - - + + + + - - - - + + + + + + + + + + + + + + + - + - - - - - + + - - - - - - + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2413,13 +2604,6 @@ - - - - - - - @@ -2430,13 +2614,6 @@ - - - - - - - @@ -2444,20 +2621,6 @@ - - - - - - - - - - - - - - @@ -2486,13 +2649,6 @@ - - - - - - - @@ -2500,20 +2656,6 @@ - - - - - - - - - - - - - - @@ -2521,55 +2663,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2577,13 +2670,6 @@ - - - - - - - @@ -2601,34 +2687,232 @@ - - - - - - - - - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2691,7 +2975,18 @@ - + + + Mobibot + + + + + No facets are configured @@ -2752,7 +3047,21 @@ - + + + Source + + + + + + + + diff --git a/properties/mobibot.properties b/properties/mobibot.properties index d3ae51c..3acc8ab 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -15,6 +15,9 @@ weblog=http://www.mobitopia.org/ feed=http://www.mobitopia.org/rss.xml backlogs=http://www.mobitopia.org/mobibot/logs +tell-max-days=5 +tell-max-size=50 + #delicious-user= #delicious-pwd= diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java new file mode 100644 index 0000000..47dbedf --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -0,0 +1,256 @@ +/* + * @(#)Commands.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot; + +/** + * The commands, keywords and arguments. + * + * @author Erik C. Thauvin + * @created 2014-04-26 + * @since 1.0 + */ +public class Commands +{ + /** + * The currency command. + */ + public static final String CURRENCY_CMD = "currency"; + + /** + * The rates keyword. + */ + public static final String CURRENCY_RATES_KEYWORD = "rates"; + + /** + * The weather command. + */ + public static final String WEATHER_CMD = "weather"; + + /** + * Debug command line argument. + */ + public static final String DEBUG_ARG = "debug"; + + /** + * Help command line argument. + */ + public static final String HELP_ARG = "help"; + + /** + * Properties command line argument. + */ + public static final String PROPS_ARG = "properties"; + + /** + * Properties version line argument. + */ + public static final String VERSION_ARG = "version"; + + /** + * The add (back)log command. + */ + public static final String ADDLOG_CMD = "addlog"; + + /** + * The debug command. + */ + public static final String DEBUG_CMD = "debug"; + + /** + * The dices command. + */ + public static final String DICE_CMD = "dice"; + + /** + * The say command. + */ + public static final String SAY_CMD = "say"; + + /** + * The die command. + */ + public static final String DIE_CMD = "die"; + + /** + * The cycle command. + */ + public static final String CYCLE_CMD = "cycle"; + + /** + * The msg command. + */ + public static final String MSG_CMD = "msg"; + + /** + * The ignore command. + */ + public static final String IGNORE_CMD = "ignore"; + + /** + * The ignore me keyword. + */ + public static final String IGNORE_ME_KEYWORD = "me"; + + /** + * The help command. + */ + public static final String HELP_CMD = "help"; + + /** + * The help on posting keyword. + */ + public static final String HELP_POSTING_KEYWORD = "posting"; + + /** + * The help on tags keyword. + */ + public static final String HELP_TAGS_KEYWORD = "tags"; + + /** + * The Google command. + */ + public static final String GOOGLE_CMD = "google"; + + /** + * The Twitter command. + */ + public static final String TWITTER_CMD = "twitter"; + + /** + * The math command. + */ + public static final String CALC_CMD = "calc"; + + /** + * The me command. + */ + public static final String ME_CMD = "me"; + + /** + * The nick command. + */ + public static final String NICK_CMD = "nick"; + + /** + * The link command. + */ + public static final String LINK_CMD = "L"; + + /** + * The lookup command. + */ + public static final String LOOKUP_CMD = "lookup"; + + /** + * The ping command. + */ + public static final String PING_CMD = "ping"; + + /** + * The pong command. + */ + public static final String PONG_CMD = "pong"; + + /** + * The quote command. + */ + public static final String QUOTE_CMD = "quote"; + + /** + * The recap command. + */ + public static final String RECAP_CMD = "recap"; + + /** + * The stock command. + */ + public static final String STOCK_CMD = "stock"; + + /** + * The time command. + */ + public static final String TIME_CMD = "time"; + + /** + * The tell command. + */ + public static final String TELL_CMD = "tell"; + + /** + * The {@link #TELL_CMD} delete command. + */ + public static final String TELL_DEL_CMD = "del"; + + /** + * The {@link #TELL_CMD} all command. + */ + public static final String TELL_ALL_CMD = "all"; + + /** + * The war command. + */ + public static final String WAR_CMD = "war"; + + /** + * The users command. + */ + public static final String USERS_CMD = "users"; + + /** + * The info command. + */ + public static final String INFO_CMD = "info"; + + /** + * The version command. + */ + public static final String VERSION_CMD = "version"; + + /** + * The view command. + */ + public static final String VIEW_CMD = "view"; + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + */ + private Commands() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 8bcb522..4822fb0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -1,7 +1,7 @@ /* * @(#)CurrencyConverter.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import org.jdom.Document; @@ -51,7 +49,6 @@ import java.util.*; * Converts various currencies. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Feb 11, 2004 * @since 1.0 */ @@ -67,11 +64,6 @@ public class CurrencyConverter implements Runnable */ private static final Map EXCHANGE_RATES = new TreeMap(); - /** - * The rates keyword. - */ - private static final String RATES_KEYWORD = "rates"; - /** * The bot. */ @@ -80,12 +72,12 @@ public class CurrencyConverter implements Runnable /** * The actual currency query. */ - private final String query; + private String query; /** * The nick of the person who sent the message. */ - private final String sender; + private String sender; /** * The last exchange rates table publication date. @@ -96,17 +88,24 @@ public class CurrencyConverter implements Runnable * Creates a new CurrencyConverter object. * * @param bot The bot. - * @param sender The nick of the person who sent the message. - * @param query The currency query. - * @param date The current date. */ - public CurrencyConverter(Mobibot bot, String sender, String query, String date) + public CurrencyConverter(Mobibot bot) { this.bot = bot; - this.sender = sender; - this.query = query.toLowerCase(); + } - if (!pubDate.equals(date)) + /** + * Sets the query. + * + * @param sender The nick of the person who sent the message. + * @param query The currency query. + */ + public void setQuery(String sender, String query) + { + this.query = query; + this.sender = sender; + + if (!pubDate.equals(Utils.today())) { EXCHANGE_RATES.clear(); } @@ -115,110 +114,115 @@ public class CurrencyConverter implements Runnable // Converts specified currencies. public final void run() { - if (EXCHANGE_RATES.isEmpty()) + if (Utils.isValidString(sender) && Utils.isValidString(query)) { - try + if (EXCHANGE_RATES.isEmpty()) { - final SAXBuilder builder = new SAXBuilder(); - builder.setIgnoringElementContentWhitespace(true); - - final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); - final Element root = doc.getRootElement(); - final Namespace ns = root.getNamespace(""); - final Element cubeRoot = root.getChild("Cube", ns); - final Element cubeTime = cubeRoot.getChild("Cube", ns); - - pubDate = cubeTime.getAttribute("time").getValue(); - - final List cubes = cubeTime.getChildren(); - Element cube; - - for (final Object rawCube : cubes) + try { - cube = (Element) rawCube; - EXCHANGE_RATES.put(cube.getAttribute("currency").getValue(), cube.getAttribute("rate").getValue()); - } + final SAXBuilder builder = new SAXBuilder(); + builder.setIgnoringElementContentWhitespace(true); - EXCHANGE_RATES.put("EUR", "1"); - } - catch (JDOMException e) - { - bot.getLogger().debug("Unable to parse the exchange rates table.", e); - bot.send(sender, "An error has occurred while parsing the exchange rates table."); - } - catch (IOException e) - { - bot.getLogger().debug("Unable to fetch the exchange rates table.", e); - bot.send(sender, "An error has occurred while fetching the exchange rates table: " + e.getMessage()); - } - } + final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); + final Element root = doc.getRootElement(); + final Namespace ns = root.getNamespace(""); + final Element cubeRoot = root.getChild("Cube", ns); + final Element cubeTime = cubeRoot.getChild("Cube", ns); - if (!EXCHANGE_RATES.isEmpty()) - { - if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-z]{3}+ to [a-z]{3}+")) - { - final String[] cmds = query.split(" "); + pubDate = cubeTime.getAttribute("time").getValue(); - if (cmds.length == 4) - { - if (cmds[3].equals(cmds[1])) + final List cubes = cubeTime.getChildren(); + Element cube; + + for (final Object rawCube : cubes) { - bot.send(sender, "You're kidding, right?"); + cube = (Element) rawCube; + EXCHANGE_RATES + .put(cube.getAttribute("currency").getValue(), cube.getAttribute("rate").getValue()); } - else - { - try - { - final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); - final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); - final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); - bot.send(bot.getChannel(), - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' + - cmds[1].toUpperCase() + " = " + - NumberFormat.getCurrencyInstance(Locale.US).format((amt * to) / from).substring(1) - + ' ' + cmds[3].toUpperCase() - ); - } - catch (NullPointerException ignored) + EXCHANGE_RATES.put("EUR", "1"); + } + catch (JDOMException e) + { + bot.getLogger().debug("Unable to parse the exchange rates table.", e); + bot.send(sender, "An error has occurred while parsing the exchange rates table."); + } + catch (IOException e) + { + bot.getLogger().debug("Unable to fetch the exchange rates table.", e); + bot.send(sender, + "An error has occurred while fetching the exchange rates table: " + e.getMessage()); + } + } + + if (!EXCHANGE_RATES.isEmpty()) + { + if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) + { + final String[] cmds = query.split(" "); + + if (cmds.length == 4) + { + if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) { - bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + bot.send(sender, "You're kidding, right?"); + } + else + { + try + { + final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); + final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); + final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); + + bot.send(bot.getChannel(), + NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' + + cmds[1].toUpperCase() + " = " + + NumberFormat.getCurrencyInstance(Locale.US).format((amt * to) / from) + .substring(1) + ' ' + cmds[3].toUpperCase() + ); + } + catch (NullPointerException ignored) + { + bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + } } } } - } - else if (query.equals(RATES_KEYWORD)) - { - bot.send(sender, "Last Update: " + pubDate); - - final Iterator it = EXCHANGE_RATES.keySet().iterator(); - String rate; - - final StringBuilder buff = new StringBuilder(0); - - while (it.hasNext()) + else if (query.equals(Commands.CURRENCY_RATES_KEYWORD)) { - rate = it.next(); - if (buff.length() > 0) + bot.send(sender, "Last Update: " + pubDate); + + final Iterator it = EXCHANGE_RATES.keySet().iterator(); + String rate; + + final StringBuilder buff = new StringBuilder(0); + + while (it.hasNext()) { - buff.append(", "); + rate = it.next(); + if (buff.length() > 0) + { + buff.append(", "); + } + buff.append(rate).append(": ").append(EXCHANGE_RATES.get(rate)); } - buff.append(rate).append(": ").append(EXCHANGE_RATES.get(rate)); + + bot.send(sender, buff.toString()); + + } + else + { + bot.helpResponse(sender, Commands.CURRENCY_CMD + ' ' + query); + bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); } - - bot.send(sender, buff.toString()); - } else { - bot.helpResponse(sender, Mobibot.CURRENCY_CMD + ' ' + query); - bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + bot.getLogger().debug("The exchange rate table is empty."); + bot.send(sender, "Sorry, but the exchange rate table is empty."); } } - else - { - bot.getLogger().debug("The exchange rate table is empty."); - bot.send(sender, "Sorry, but the exchange rate table is empty."); - } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 036f2e8..8f60136 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -1,7 +1,7 @@ /* * @(#)DeliciousPoster.java * - * Copyright (c) 2005, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import del.icio.us.Delicious; @@ -42,11 +40,10 @@ import del.icio.us.Delicious; * The class to handle posts to del.icio.us. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Mar 5, 2005 - * @noinspection UnnecessaryBoxing * @since 1.0 */ +@SuppressWarnings("UnnecessaryBoxing") public class DeliciousPoster { private final Delicious delicious; @@ -78,16 +75,28 @@ public class DeliciousPoster public Object construct() { return Boolean.valueOf(delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate())); + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate())); } }; worker.start(); } + /** + * Returns he del.icio.us extended attribution line. + * + * @param entry The entry. + * + * @return The extended attribution line. + */ + private String postedBy(EntryLink entry) + { + return "Posted by " + entry.getNick() + " on " + entry.getChannel() + " (" + ircServer + ')'; + } + /** * Deletes a post to del.icio.us. * @@ -125,36 +134,24 @@ public class DeliciousPoster delicious.deletePost(oldUrl); return Boolean.valueOf(delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate())); + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate())); } else { return Boolean.valueOf(delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate(), - true, - true)); + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate(), + true, + true)); } } }; worker.start(); } - - /** - * Returns he del.icio.us extended attribution line. - * - * @param entry The entry. - * - * @return The extended attribution line. - */ - private String postedBy(EntryLink entry) - { - return "Posted by " + entry.getNick() + " on " + entry.getChannel() + " (" + ircServer + ')'; - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/Dice.java new file mode 100644 index 0000000..f5f63d2 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Dice.java @@ -0,0 +1,97 @@ +/* + * @(#)Dice.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot; + +import java.util.Random; + +/** + * The {@link net.thauvin.erik.mobibot.Commands#DICE_CMD} command + * + * @author Erik C. Thauvin + * @created 2014-04-28 + * @since 1.0 + */ +public class Dice +{ + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + */ + private Dice() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Rolls the dice + * + * @param bot The bot. + * @param sender The sender's nickname. + */ + public static void roll(Mobibot bot, String sender) + { + final Random r = new Random(); + + int i = r.nextInt(6) + 1; + int y = r.nextInt(6) + 1; + final int playerTotal = i + y; + + bot.send(bot.getChannel(), + sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils + .bold(playerTotal) + ); + + i = r.nextInt(6) + 1; + y = r.nextInt(6) + 1; + final int total = i + y; + + bot.action( + "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); + + if (playerTotal < total) + { + bot.action("wins."); + } + else if (playerTotal > total) + { + bot.action("lost."); + } + else + { + bot.action("tied."); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java new file mode 100644 index 0000000..a3add76 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -0,0 +1,384 @@ +/* + * @(#)EntriesMgr.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot; + +import com.sun.syndication.feed.synd.*; +import com.sun.syndication.io.FeedException; +import com.sun.syndication.io.SyndFeedInput; +import com.sun.syndication.io.SyndFeedOutput; + +import java.io.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +/** + * Manages the feed entries. + * + * @author Erik C. Thauvin + * @created 2014-04-28 + * @since 1.0 + */ +public class EntriesMgr +{ + /** + * The name of the file containing the current entries. + */ + public static final String CURRENT_XML = "current.xml"; + + /** + * The name of the file containing the backlog entries. + */ + public static final String NAV_XML = "nav.xml"; + + /** + * The maximum number of backlogs to keep. + */ + private static final int MAX_BACKLOGS = 10; + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + */ + private EntriesMgr() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Loads the current entries. + * + * @param file The file containing the current entries. + * @param channel The channel + * @param entries The entries. + * + * @return The feed's last published date. + * + * @throws java.io.FileNotFoundException If the file was not found. + * @throws com.sun.syndication.io.FeedException If an error occurred while reading the feed. + */ + @SuppressWarnings("unchecked") + public static String loadEntries(String file, String channel, List entries) + throws FileNotFoundException, FeedException + { + entries.clear(); + + final SyndFeedInput input = new SyndFeedInput(); + + String today; + InputStreamReader reader = null; + + try + { + reader = new InputStreamReader(new FileInputStream(new File(file))); + + final SyndFeed feed = input.build(reader); + + today = Utils.ISO_SDF.format(feed.getPublishedDate()); + + final List items = feed.getEntries(); + SyndEntry item; + SyndContent description; + String[] comments; + String author; + EntryLink entry; + + for (int i = items.size() - 1; i >= 0; i--) + { + item = (SyndEntryImpl) items.get(i); + author = item.getAuthor() + .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); + entry = new EntryLink(item.getLink(), + item.getTitle(), + author, + channel, + item.getPublishedDate(), + item.getCategories()); + description = item.getDescription(); + comments = description.getValue().split("
    "); + + int split; + for (final String comment : comments) + { + split = comment.indexOf(": "); + + if (split != -1) + { + entry.addComment(comment.substring(split + 2).trim(), comment.substring(0, split).trim()); + } + } + + entries.add(entry); + } + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException ignore) + { + ; // Do nothing + } + } + } + + return today; + } + + /** + * Loads the backlogs. + * + * @param file The file containing the backlogs. + * @param history The history list. + * + * @throws FileNotFoundException If the file was not found. + * @throws FeedException If an error occurred while reading the feed. + */ + public static void loadBacklogs(String file, List history) + throws FileNotFoundException, FeedException + { + history.clear(); + + final SyndFeedInput input = new SyndFeedInput(); + + InputStreamReader reader = null; + + try + { + reader = new InputStreamReader(new FileInputStream(new File(file))); + + final SyndFeed feed = input.build(reader); + + final List items = feed.getEntries(); + SyndEntry item; + + for (int i = items.size() - 1; i >= 0; i--) + { + item = (SyndEntryImpl) items.get(i); + history.add(item.getTitle()); + } + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException ignore) + { + ; // Do nothing + } + } + } + } + + /** + * Saves the entries. + * + * @param isDayBackup Set the true if the daily backup file should also be created. + */ + public static void saveEntries(Mobibot bot, List entries, List history, boolean isDayBackup) + { + if (bot.getLogger().isDebugEnabled()) + { + bot.getLogger().debug("Saving..."); + } + + if (Utils.isValidString(bot.getLogsDir()) && Utils.isValidString(bot.getWeblogUrl())) + { + FileWriter fw = null; + + try + { + fw = new FileWriter(new File(bot.getLogsDir() + CURRENT_XML)); + + SyndFeed rss = new SyndFeedImpl(); + rss.setFeedType("rss_2.0"); + rss.setTitle(bot.getChannel() + " IRC Links"); + rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); + rss.setLink(bot.getWeblogUrl()); + rss.setPublishedDate(Calendar.getInstance().getTime()); + rss.setLanguage("en"); + + EntryLink entry; + StringBuffer buff; + EntryComment comment; + final List items = new ArrayList(0); + SyndEntry item; + SyndContent description; + + for (int i = (entries.size() - 1); i >= 0; --i) + { + entry = entries.get(i); + + buff = new StringBuffer( + "Posted by " + entry.getNick() + " on " + entry.getChannel() + "" + ); + + if (entry.getCommentsCount() > 0) + { + buff.append("

    "); + + final EntryComment[] comments = entry.getComments(); + + for (int j = 0; j < comments.length; j++) + { + comment = comments[j]; + + if (j > 0) + { + buff.append("
    "); + } + + buff.append(comment.getNick()).append(": ").append(comment.getComment()); + } + } + + item = new SyndEntryImpl(); + item.setLink(entry.getLink()); + description = new SyndContentImpl(); + description.setValue(buff.toString()); + item.setDescription(description); + item.setTitle(entry.getTitle()); + item.setPublishedDate(entry.getDate()); + item.setAuthor( + bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); + item.setCategories(entry.getTags()); + + items.add(item); + } + + rss.setEntries(items); + + if (bot.getLogger().isDebugEnabled()) + { + bot.getLogger().debug("Writing the entries feed."); + } + + final SyndFeedOutput output = new SyndFeedOutput(); + output.output(rss, fw); + fw.close(); + + fw = new FileWriter(new File(bot.getLogsDir() + bot.getToday() + ".xml")); + output.output(rss, fw); + + if (isDayBackup) + { + if (Utils.isValidString(bot.getBacklogsUrl())) + { + if (history.indexOf(bot.getToday()) == -1) + { + history.add(bot.getToday()); + + while (history.size() > MAX_BACKLOGS) + { + history.remove(0); + } + } + + fw.close(); + fw = new FileWriter(new File(bot.getLogsDir() + NAV_XML)); + rss = new SyndFeedImpl(); + rss.setFeedType("rss_2.0"); + rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); + rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " + bot.getChannel()); + rss.setLink(bot.getBacklogsUrl()); + rss.setPublishedDate(Calendar.getInstance().getTime()); + + String date; + items.clear(); + + for (int i = (history.size() - 1); i >= 0; --i) + { + date = history.get(i); + + item = new SyndEntryImpl(); + item.setLink(bot.getBacklogsUrl() + date + ".xml"); + item.setTitle(date); + description = new SyndContentImpl(); + description.setValue("Links for " + date); + item.setDescription(description); + + items.add(item); + } + + rss.setEntries(items); + + if (bot.getLogger().isDebugEnabled()) + { + bot.getLogger().debug("Writing the backlog feed."); + } + + output.output(rss, fw); + } + else + { + bot.getLogger().warn("Unable to generate the backlogs feed. No property configured."); + } + } + } + catch (Exception e) + { + bot.getLogger().warn("Unable to generate the feed.", e); + } + finally + { + try + { + if (fw != null) + { + fw.close(); + } + } + catch (Exception ignore) + { + ; // Do nothing + } + } + } + else + { + bot.getLogger().warn("Unable to generate the feed. At least one of the required property is missing."); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index c071e74..4c12789 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -1,7 +1,7 @@ /* * @(#)EntryComment.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import java.io.Serializable; @@ -44,7 +42,6 @@ import java.util.Date; * The class used to store comments associated to a specific entry. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Jan 31, 2004 * @since 1.0 */ @@ -80,6 +77,7 @@ public class EntryComment implements Serializable /** * Creates a new comment. + * * @noinspection UnusedDeclaration */ protected EntryComment() @@ -101,6 +99,7 @@ public class EntryComment implements Serializable * Sets the comment. * * @param comment The actual comment. + * * @noinspection UnusedDeclaration */ public final void setComment(String comment) diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 5afd312..2b737cf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -1,7 +1,7 @@ /* * @(#)EntryLink.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import com.sun.syndication.feed.synd.SyndCategoryImpl; @@ -48,7 +46,6 @@ import java.util.List; * The class used to store link entries. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Jan 31, 2004 * @since 1.0 */ @@ -106,6 +103,61 @@ public class EntryLink implements Serializable setTags(tags); } + /** + * Sets the tags. + * + * @param tags The space-delimited tags. + */ + public final synchronized void setTags(String tags) + { + if (tags != null) + { + final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); + + SyndCategoryImpl tag; + String part; + char mod; + + for (final String rawPart : parts) + { + part = rawPart.trim(); + + if (part.length() >= 2) + { + tag = new SyndCategoryImpl(); + tag.setName(part.substring(1).toLowerCase()); + + mod = part.charAt(0); + + if (mod == '-') + { + // Don't remove the channel tag, if any. + if (!tag.getName().equals(channel.substring(1))) + { + this.tags.remove(tag); + } + } + else if (mod == '+') + { + if (!this.tags.contains(tag)) + { + this.tags.add(tag); + } + } + else + { + tag.setName(part.trim().toLowerCase()); + + if (!this.tags.contains(tag)) + { + this.tags.add(tag); + } + } + } + } + } + } + /** * Creates a new entry. * @@ -129,6 +181,7 @@ public class EntryLink implements Serializable /** * Creates a new EntryLink object. + * * @noinspection UnusedDeclaration */ protected EntryLink() @@ -178,6 +231,7 @@ public class EntryLink implements Serializable * Sets the channel. * * @param channel The channel. + * * @noinspection UnusedDeclaration */ public final synchronized void setChannel(String channel) @@ -279,6 +333,7 @@ public class EntryLink implements Serializable * Set the comment's author login. * * @param login The new login. + * * @noinspection UnusedDeclaration */ public final synchronized void setLogin(String login) @@ -319,56 +374,11 @@ public class EntryLink implements Serializable /** * Sets the tags. * - * @param tags The space-delimited tags. + * @param tags The tags. */ - public final synchronized void setTags(String tags) + final synchronized void setTags(List tags) { - if (tags != null) - { - final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); - - SyndCategoryImpl tag; - String part; - char mod; - - for (final String rawPart : parts) - { - part = rawPart.trim(); - - if (part.length() >= 2) - { - tag = new SyndCategoryImpl(); - tag.setName(part.substring(1).toLowerCase()); - - mod = part.charAt(0); - - if (mod == '-') - { - // Don't remove the channel tag, if any. - if (!tag.getName().equals(channel.substring(1))) - { - this.tags.remove(tag); - } - } - else if (mod == '+') - { - if (!this.tags.contains(tag)) - { - this.tags.add(tag); - } - } - else - { - tag.setName(part.trim().toLowerCase()); - - if (!this.tags.contains(tag)) - { - this.tags.add(tag); - } - } - } - } - } + this.tags.addAll(tags); } /** @@ -426,16 +436,6 @@ public class EntryLink implements Serializable } } - /** - * Sets the tags. - * - * @param tags The tags. - */ - public final synchronized void setTags(List tags) - { - this.tags.addAll(tags); - } - /** * Returns a string representation of the object. * @@ -444,8 +444,8 @@ public class EntryLink implements Serializable public final String toString() { - return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " - + date + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' + return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date + + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index bfd3197..adbcdda 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -1,7 +1,7 @@ /* * @(#)FeedReader.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import com.sun.syndication.feed.synd.SyndEntry; @@ -50,7 +48,6 @@ import java.util.List; * Reads a RSS feed. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Feb 1, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java index ad73726..190a232 100644 --- a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java @@ -1,7 +1,7 @@ /* * @(#)GoogleSearch.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,12 +30,9 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot; import org.json.JSONArray; import org.json.JSONObject; @@ -50,7 +47,6 @@ import java.net.URLEncoder; * Performs a Google search or spell checking query. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Feb 7, 2004 * @since 1.0 */ @@ -95,7 +91,6 @@ public class GoogleSearch implements Runnable */ public final void run() { - try { final String query = URLEncoder.encode(this.query, "UTF-8"); @@ -119,12 +114,11 @@ public class GoogleSearch implements Runnable for (int i = 0; i < ja.length(); i++) { final JSONObject j = ja.getJSONObject(i); - bot.send(sender, Mobibot.unescapeXml(j.getString("titleNoFormatting"))); + bot.send(sender, Utils.unescapeXml(j.getString("titleNoFormatting"))); bot.send(sender, TAB_INDENT + j.getString("url")); } reader.close(); - } catch (Exception e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/Lookup.java new file mode 100644 index 0000000..43884ec --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Lookup.java @@ -0,0 +1,162 @@ +/* + * @(#)Lookup.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot; + +import org.apache.commons.net.WhoisClient; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Performs a DNS lookup query. + * + * @author Erik C. Thauvin + * @created 2014-04-26 + * @since 1.0 + */ +public class Lookup +{ + + /** + * The whois host. + */ + @SuppressWarnings("WeakerAccess") + public static final String WHOIS_HOST = "whois.arin.net"; + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + */ + private Lookup() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Performs a DNS lookup on the specified query. + * + * @param query The IP address or hostname. + * + * @return The lookup query result string. + * + * @throws java.net.UnknownHostException If the host is unknown. + */ + public static String lookup(String query) + throws UnknownHostException + { + final StringBuilder buffer = new StringBuilder(""); + + final InetAddress[] results = InetAddress.getAllByName(query); + String hostInfo; + + for (final InetAddress result : results) + { + if (result.getHostAddress().equals(query)) + { + hostInfo = result.getHostName(); + + if (hostInfo.equals(query)) + { + throw new UnknownHostException(); + } + } + else + { + hostInfo = result.getHostAddress(); + } + + if (buffer.length() > 0) + { + buffer.append(", "); + } + + buffer.append(hostInfo); + } + + return buffer.toString(); + } + + /** + * Performs a whois IP query. + * + * @param query The IP address. + * + * @return The IP whois data, if any. + * + * @throws java.io.IOException If a connection error occurs. + */ + public static String[] whois(String query) + throws IOException + { + return whois(query, WHOIS_HOST); + } + + /** + * Performs a whois IP query. + * + * @param query The IP address. + * @param host The whois host. + * + * @return The IP whois data, if any. + * + * @throws java.io.IOException If a connection error occurs. + */ + @SuppressWarnings("WeakerAccess, SameParameterValue") + public static String[] whois(String query, String host) + throws IOException + { + final WhoisClient whois = new WhoisClient(); + String[] lines; + + try + { + whois.setDefaultTimeout(Mobibot.CONNECT_TIMEOUT); + whois.connect(host); + whois.setSoTimeout(Mobibot.CONNECT_TIMEOUT); + whois.setSoLinger(false, 0); + + lines = whois.query('-' + query).split("\n"); + } + finally + { + whois.disconnect(); + } + + return lines; + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 4a8a14d..97405a5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1,7 +1,7 @@ /* * @(#)Mobibot.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,23 +30,16 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ package net.thauvin.erik.mobibot; -import com.sun.syndication.feed.synd.*; import com.sun.syndication.fetcher.impl.FeedFetcherCache; import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache; import com.sun.syndication.io.FeedException; -import com.sun.syndication.io.SyndFeedInput; -import com.sun.syndication.io.SyndFeedOutput; import de.congrace.exp4j.Calculable; import de.congrace.exp4j.ExpressionBuilder; import org.apache.commons.cli.*; import org.apache.commons.logging.impl.Log4JLogger; -import org.apache.commons.net.WhoisClient; import org.apache.log4j.Level; import org.jibble.pircbot.Colors; import org.jibble.pircbot.PircBot; @@ -55,18 +48,14 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.*; -import java.net.InetAddress; import java.net.UnknownHostException; -import java.nio.channels.FileChannel; import java.text.DecimalFormat; -import java.text.SimpleDateFormat; import java.util.*; /** * Implements the #mobitopia bot. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Jan 31, 2004 * @since 1.0 */ @@ -78,32 +67,14 @@ public class Mobibot extends PircBot public static final int CONNECT_TIMEOUT = 5000; /** - * The currency command. + * The serialized object file extension. */ - public static final String CURRENCY_CMD = "currency"; + private static final String SER_EXT = ".ser"; /** - * The weather command. + * Shall we play a game? */ - public static final String WEATHER_CMD = "weather"; - - /** - * The HH:MM timestamp simple date format. - */ - private static final SimpleDateFormat HHMM_SDF = new SimpleDateFormat("HH:mm z"); - - /** - * Initialize {@link #HHMM_SDF timestamp} - */ - static - { - HHMM_SDF.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - /** - * The ISO (YYYY-MM-DD) simple date format. - */ - private static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); + private static final String shall_we_play_a_game = "Shall we play a game?"; /** * The info strings. @@ -117,7 +88,7 @@ public class Mobibot extends PircBot * The version strings. */ private static final String[] VERSION_STRS = { - "Version: " + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + " (" + ISO_SDF + "Version: " + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + " (" + Utils.ISO_SDF .format(ReleaseInfo.getBuildDate()) + ')', "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', @@ -127,26 +98,6 @@ public class Mobibot extends PircBot + System.getProperty("java.vm.info") + ')' }; - /** - * Debug command line argument. - */ - private static final String DEBUG_ARG = "debug"; - - /** - * Help command line argument. - */ - private static final String HELP_ARG = "help"; - - /** - * Properties command line argument. - */ - private static final String PROPS_ARG = "properties"; - - /** - * Properties version line argument. - */ - private static final String VERSION_ARG = "version"; - /** * The maximum number of times the bot will try to reconnect, if disconnected. */ @@ -162,156 +113,16 @@ public class Mobibot extends PircBot */ private static final int MAX_RECAP = 10; - /** - * The recap array. - */ - private static final List RECAP_ARRAY = new ArrayList(MAX_RECAP); - - /** - * The maximum number of backlogs to keep. - */ - private static final int MAX_BACKLOGS = 10; - /** * The double tab indent (8 spaces). */ private static final String DOUBLE_INDENT = " "; - /** - * The add (back)log command. - */ - private static final String ADDLOG_CMD = "addlog"; - - /** - * The debug command. - */ - private static final String DEBUG_CMD = "debug"; - - /** - * The dices command. - */ - private static final String DICE_CMD = "dice"; - - /** - * The say command. - */ - private static final String SAY_CMD = "say"; - - /** - * The die command. - */ - private static final String DIE_CMD = "die"; - - /** - * The cycle command. - */ - private static final String CYCLE_CMD = "cycle"; - - /** - * The msg command. - */ - private static final String MSG_CMD = "msg"; - - /** - * The ignore command. - */ - private static final String IGNORE_CMD = "ignore"; - - /** - * The ignore me keyword. - */ - private static final String IGNORE_ME_KEYWORD = "me"; - - /** - * The help command. - */ - private static final String HELP_CMD = "help"; - - /** - * The help on posting keyword. - */ - private static final String HELP_POSTING_KEYWORD = "posting"; - - /** - * The help on tags keyword. - */ - private static final String HELP_TAGS_KEYWORD = "tags"; - - /** - * The Google command. - */ - private static final String GOOGLE_CMD = "google"; - - /** - * The Twitter command. - */ - private static final String TWITTER_CMD = "twitter"; - - /** - * The math command. - */ - private static final String CALC_CMD = "calc"; - - /** - * The me command. - */ - private static final String ME_CMD = "me"; - - /** - * The nick command. - */ - private static final String NICK_CMD = "nick"; - - /** - * The link command. - */ - private static final String LINK_CMD = "L"; - /** * The link match string. */ private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - /** - * The lookup command. - */ - private static final String LOOKUP_CMD = "lookup"; - - /** - * The ping command. - */ - private static final String PING_CMD = "ping"; - - /** - * The pong command. - */ - private static final String PONG_CMD = "pong"; - - /** - * The quote command. - */ - private static final String QUOTE_CMD = "quote"; - - /** - * The recap command. - */ - private static final String RECAP_CMD = "recap"; - - /** - * The stock command. - */ - private static final String STOCK_CMD = "stock"; - - /** - * The time command. - */ - private static final String TIME_CMD = "time"; - - /** - * The war command. - */ - private static final String WAR_CMD = "war"; - /** * The empty title string. */ @@ -322,53 +133,6 @@ public class Mobibot extends PircBot */ private static final String TAGS_MARKER = "tags:"; - /** - * The countries supported by the {@link #TIME_CMD time} command. - */ - private static final Map COUNTRIES_MAP = new TreeMap(); - - /** - * The deck of card for the {@link #WAR_CMD war} command. - */ - private static final String[] WAR_DECK = - new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; - - /** - * The suits for the deck of card for the {@link #WAR_CMD war} command. - */ - private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; - - /** - * The date/time format for the {@link #TIME_CMD time} command. - */ - private static final SimpleDateFormat TIME_SDF = - new SimpleDateFormat("'The time is 'HH:mm' on 'EEE, d MMM yyyy' in '"); - - /** - * The beats (Internet Time) keyword. - */ - private static final String BEATS_KEYWORD = ".beats"; - - /** - * The users command. - */ - private static final String USERS_CMD = "users"; - - /** - * The info command. - */ - private static final String INFO_CMD = "info"; - - /** - * The version command. - */ - private static final String VERSION_CMD = "version"; - - /** - * The view command. - */ - private static final String VIEW_CMD = "view"; - /** * The start time. */ @@ -379,25 +143,41 @@ public class Mobibot extends PircBot */ private static final int DEFAULT_PORT = 6667; - /** - * The whois host. - */ - private static final String WHOIS_HOST = "whois.arin.net"; - /** * The number of milliseconds to delay between consecutive messages. */ private static final long MESSAGE_DELAY = 1000L; /** - * The name of the file containing the current entries. + * The default maximum number of days to keep {@link Commands#TELL_CMD} messages. */ - private static final String CURRENT_XML = "current.xml"; + + private static final int DEFAULT_TELL_MAX_DAYS = 7; /** - * The name of the file containing the backlog entries. + * The number of days message are kept. */ - private static final String NAV_XML = "nav.xml"; + private int tellMaxDays = DEFAULT_TELL_MAX_DAYS; + + /** + * The default {@link Commands#TELL_CMD) message max queue size. + */ + private static final int DEFAULT_TELL_MAX_SIZE = 50; + + /** + * The maximum number of {@link Commands#TELL_CMD} messages allowed. + */ + private int tellMaxSize = DEFAULT_TELL_MAX_SIZE; + + /** + * The recap array. + */ + private final List recap = new ArrayList(0); + + /** + * The {@link Commands#TELL_CMD} messages queue. + */ + private final List tellMessages = new ArrayList(0); /** * The main channel. @@ -434,6 +214,11 @@ public class Mobibot extends PircBot */ private final String ircServer; + /** + * The currency converter. + */ + private final CurrencyConverter currencyConverter; + /** * The logger. */ @@ -449,10 +234,20 @@ public class Mobibot extends PircBot */ private final String logsDir; + /** + * The serialized object file. + */ + private final String serializedObject; + + /** + * Time command. + */ + private final WorldTime worldTime = new WorldTime(); + /** * The backlogs URL. */ - private String backlogsURL = ""; + private String backLogsUrl = ""; /** * The default tags/categories. @@ -507,96 +302,52 @@ public class Mobibot extends PircBot /** * Today's date. */ - private String today = today(); + private String today = Utils.today(); /** * The weblog URL. */ - private String weblogURL = ""; - - /** - * Initialize the countries. - */ - static - { - COUNTRIES_MAP.put("AU", "Australia/Sydney"); - COUNTRIES_MAP.put("BE", "Europe/Brussels"); - COUNTRIES_MAP.put("CA", "America/Montreal"); - COUNTRIES_MAP.put("CH", "Europe/Zurich"); - COUNTRIES_MAP.put("CN", "Asia/Shanghai"); - COUNTRIES_MAP.put("DE", "Europe/Berlin"); - COUNTRIES_MAP.put("DK", "Europe/Copenhagen"); - COUNTRIES_MAP.put("ES", "Europe/Madrid"); - COUNTRIES_MAP.put("FI", "Europe/Helsinki"); - COUNTRIES_MAP.put("FR", "Europe/Paris"); - COUNTRIES_MAP.put("GB", "Europe/London"); - COUNTRIES_MAP.put("HK", "Asia/Hong_Kong"); - COUNTRIES_MAP.put("IE", "Europe/Dublin"); - COUNTRIES_MAP.put("IL", "Israel"); - COUNTRIES_MAP.put("IN", "Asia/Calcutta"); - COUNTRIES_MAP.put("IS", "Iceland"); - COUNTRIES_MAP.put("IT", "Europe/Rome"); - COUNTRIES_MAP.put("JP", "Asia/Tokyo"); - COUNTRIES_MAP.put("MX", "Mexico/Mexico_City"); - COUNTRIES_MAP.put("NL", "Europe/Amsterdam"); - COUNTRIES_MAP.put("NO", "Europe/Oslo"); - COUNTRIES_MAP.put("NZ", "Pacific/Auckland"); - COUNTRIES_MAP.put("PK", "Asia/Karachi"); - COUNTRIES_MAP.put("RU", "Europe/Moscow"); - COUNTRIES_MAP.put("SE", "Europe/Stockholm"); - COUNTRIES_MAP.put("SG", "Asia/Singapore"); - COUNTRIES_MAP.put("SU", "Europe/Moscow"); - COUNTRIES_MAP.put("TH", "Asia/Bangkok"); - COUNTRIES_MAP.put("TW", "Asia/Taipei"); - COUNTRIES_MAP.put("UK", "Europe/London"); - COUNTRIES_MAP.put("US", "America/New_York"); - COUNTRIES_MAP.put("EST", "America/New_York"); - COUNTRIES_MAP.put("CST", "America/Chicago"); - COUNTRIES_MAP.put("MST", "America/Denver"); - COUNTRIES_MAP.put("PST", "America/Los_Angeles"); - COUNTRIES_MAP.put("EDT", "America/New_York"); - COUNTRIES_MAP.put("CDT", "America/Chicago"); - COUNTRIES_MAP.put("MDT", "America/Denver"); - COUNTRIES_MAP.put("PDT", "America/Los_Angeles"); - COUNTRIES_MAP.put("CET", "CET"); - COUNTRIES_MAP.put("GMT", "GMT"); - COUNTRIES_MAP.put("HST", "HST"); - COUNTRIES_MAP.put("UTC", "UTC"); - COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); - COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); - } + private String weblogUrl = ""; /** * Creates a new Mobibot object. * * @param server The server. * @param port The port. + * @param nickname The nickname. * @param channel The channel. * @param logsDir The logs directory. */ - public Mobibot(String server, int port, String channel, String logsDir) + public Mobibot(String server, int port, String nickname, String channel, String logsDir) { System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); + setName(nickname); + ircServer = server; ircPort = port; this.channel = channel; this.logsDir = logsDir; + this.serializedObject = logsDir + getName() + SER_EXT; // Set the logger logger = new Log4JLogger(Mobibot.class.getPackage().getName()); loggerLevel = logger.getLogger().getLevel(); + // Initialization + Utils.UTC_SDF.setTimeZone(TimeZone.getTimeZone("UTC")); + currencyConverter = new CurrencyConverter(this); + // Load the current entries, if any. try { - loadEntries(this.logsDir + CURRENT_XML); + today = EntriesMgr.loadEntries(this.logsDir + EntriesMgr.CURRENT_XML, this.channel, entries); - if (!today().equals(today)) + if (!Utils.today().equals(today)) { entries.clear(); - today = today(); + today = Utils.today(); } } catch (FileNotFoundException ignore) @@ -605,13 +356,13 @@ public class Mobibot extends PircBot } catch (FeedException e) { - logger.error("An error occurred while parsing the '" + CURRENT_XML + "' file.", e); + logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); } // Load the backlogs, if any. try { - loadBacklogs(this.logsDir + NAV_XML); + EntriesMgr.loadBacklogs(this.logsDir + EntriesMgr.NAV_XML, history); } catch (FileNotFoundException ignore) { @@ -619,42 +370,30 @@ public class Mobibot extends PircBot } catch (FeedException e) { - logger.error("An error occurred while parsing the '" + NAV_XML + "' file.", e); + logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); } } - /** - * Returns true if the given string is valid. - * - * @param s The string to validate. - * - * @return true if the string is non-empty and not null, false otherwise. - */ - public static boolean isValidString(String s) - { - return (s != null) && (s.trim().length() > 0); - } - /** * The Truth Is Out There... * * @param args The command line arguments. * - * @noinspection UseOfSystemOutOrSystemErr, ACCESS_STATIC_VIA_INSTANCE + * @noinspection UseOfSystemOutOrSystemErr, ACCESS_STATIC_VIA_INSTANCE, unchecked */ public static void main(String[] args) { // Setup the command line options final Options options = new Options(); - options.addOption(HELP_ARG.substring(0, 1), HELP_ARG, false, "print this help message"); - options.addOption(DEBUG_ARG.substring(0, 1), - DEBUG_ARG, + options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); + options.addOption(Commands.DEBUG_ARG.substring(0, 1), + Commands.DEBUG_ARG, false, "print debug & logging data directly to the console"); //noinspection AccessStaticViaInstance options.addOption(OptionBuilder.withArgName("file").hasArg().withDescription("use alternate properties file") - .withLongOpt(PROPS_ARG).create(PROPS_ARG.substring(0, 1))); - options.addOption(VERSION_ARG.substring(0, 1), VERSION_ARG, false, "print version info"); + .withLongOpt(Commands.PROPS_ARG).create(Commands.PROPS_ARG.substring(0, 1))); + options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); // Parse the command line final CommandLineParser parser = new PosixParser(); @@ -671,12 +410,12 @@ public class Mobibot extends PircBot System.exit(1); } - if (line.hasOption(HELP_ARG.charAt(0))) + if (line.hasOption(Commands.HELP_ARG.charAt(0))) { // Output the usage new HelpFormatter().printHelp(Mobibot.class.getName(), options); } - else if (line.hasOption(VERSION_ARG.charAt(0))) + else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) { for (final String s : INFO_STRS) { @@ -690,7 +429,8 @@ public class Mobibot extends PircBot try { - fis = new FileInputStream(new File(line.getOptionValue(PROPS_ARG.charAt(0), "./mobibot.properties"))); + fis = new FileInputStream(new File(line.getOptionValue(Commands.PROPS_ARG.charAt(0), + "./mobibot.properties"))); // Load the properties files p.load(fis); @@ -725,11 +465,11 @@ public class Mobibot extends PircBot // Get the main properties final String channel = p.getProperty("channel"); final String server = p.getProperty("server"); - final int port = getPort(p.getProperty("port", String.valueOf(DEFAULT_PORT))); + final int port = Utils.getIntProperty(p.getProperty("port"), DEFAULT_PORT); final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase()); - final String logsDir = ensureDir(p.getProperty("logs", "."), false); + final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); - if (!line.hasOption(DEBUG_ARG.charAt(0))) + if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { // Redirect the stdout and stderr PrintStream stdout = null; @@ -737,7 +477,7 @@ public class Mobibot extends PircBot try { stdout = new PrintStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + today() + ".log", true)); + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); } catch (IOException e) { @@ -767,7 +507,7 @@ public class Mobibot extends PircBot final String login = p.getProperty("login", nickname); final String weblogURL = p.getProperty("weblog", ""); final String feedURL = p.getProperty("feed", ""); - final String backlogsURL = ensureDir(p.getProperty("backlogs", weblogURL), true); + final String backlogsURL = Utils.ensureDir(p.getProperty("backlogs", weblogURL), true); final String ignoredNicks = p.getProperty("ignore", ""); final String identNick = p.getProperty("ident-nick", ""); final String identMsg = p.getProperty("ident-msg", ""); @@ -784,13 +524,16 @@ public class Mobibot extends PircBot final String ttoken = p.getProperty("twitter-token", ""); final String ttokenSecret = p.getProperty("twitter-tokenSecret", ""); + // Get the tell command settings + final int tellMaxDays = Utils.getIntProperty(p.getProperty("tell-max-days"), DEFAULT_TELL_MAX_DAYS); + final int tellMaxSize = Utils.getIntProperty(p.getProperty("tell-max-size"), DEFAULT_TELL_MAX_SIZE); + // Create the bot - final Mobibot bot = new Mobibot(server, port, channel, logsDir); + final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir); // Initialize the bot bot.setVerbose(true); bot.setAutoNickChange(true); - bot.setName(nickname); bot.setLogin(login); bot.setVersion(weblogURL); bot.setMessageDelay(MESSAGE_DELAY); @@ -803,18 +546,18 @@ public class Mobibot extends PircBot bot.setIdentMsg(identMsg); // Set the URLs - bot.setWeblogURL(weblogURL); + bot.setWeblogUrl(weblogURL); bot.setFeedURL(feedURL); - bot.setBacklogsURL(backlogsURL); + bot.setBacklogsUrl(backlogsURL); - if (isValidString(dname) && isValidString(dpwd)) + if (Utils.isValidString(dname) && Utils.isValidString(dpwd)) { // Set the del.icio.us authentication bot.setDeliciousAuth(dname, dpwd); } - if (isValidString(tconsumerKey) && isValidString(tconsumerSecret) && isValidString(ttoken) && isValidString( - ttokenSecret)) + if (Utils.isValidString(tconsumerKey) && Utils.isValidString(tconsumerSecret) && Utils.isValidString(ttoken) + && Utils.isValidString(ttokenSecret)) { // Set the Twitter authentication bot.setTwitterAuth(tconsumerKey, tconsumerSecret, ttoken, ttokenSecret); @@ -826,6 +569,9 @@ public class Mobibot extends PircBot // Set the ignored nicks bot.setIgnoredNicks(ignoredNicks); + // Set the tell command + bot.setTell(tellMaxDays, tellMaxSize); + // Save the entries bot.saveEntries(true); @@ -862,62 +608,186 @@ public class Mobibot extends PircBot bot.setVersion(INFO_STRS[0]); // Identify with NickServ - if (isValidString(ident)) + if (Utils.isValidString(ident)) { bot.identify(ident); } // Identify with a specified nick - if (isValidString(identNick) && isValidString(identMsg)) + if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) { bot.sendMessage(identNick, identMsg); } + // Load the messages queue + bot.tellMessages.addAll(TellMessagesMgr.load(bot.getSerializedObject(), bot.getLogger())); + bot.cleanTellMessages(); + bot.joinChannel(channel); } } /** - * Converts XML/XHTML entities to plain text. + * Sets the ident password. * - * @param str The string to unescape. - * - * @return The unescaped string. + * @param pwd The password. */ - public static String unescapeXml(String str) + private void setIdent(String pwd) { - String s = str.replaceAll("&", "&"); - s = s.replaceAll("<", "<"); - s = s.replaceAll(">", ">"); - s = s.replaceAll(""", "\""); - s = s.replaceAll("'", "'"); - s = s.replaceAll("'", "'"); - - return s; + ident = pwd; } /** - * Makes the given int bold. + * Sets the ident nickname. * - * @param i The int. - * - * @return The bold string. + * @param nick The nickname. */ - private static String bold(int i) + private void setIdentNick(String nick) { - return Colors.BOLD + i + Colors.BOLD; + identNick = nick; } /** - * Makes the given string bold. + * Sets the ident message. * - * @param s The string. - * - * @return The bold string. + * @param msg The message. */ - private static String bold(String s) + private void setIdentMsg(String msg) { - return Colors.BOLD + s + Colors.BOLD; + identMsg = msg; + } + + /** + * Sets the feed URL. + * + * @param feedURL The feed URL. + */ + private void setFeedURL(String feedURL) + { + this.feedURL = feedURL; + } + + /** + * Sets the del.icio.us authentication. + * + * @param username The del.icio.us username. + * @param password The del.icio.us password. + */ + private void setDeliciousAuth(String username, String password) + { + delicious = new DeliciousPoster(username, password, ircServer); + } + + /** + * Sets the Twitter consumerSecret and password.. + * + * @param consumerKey The Twitter consumer key. + * @param consumerSecret The Twitter consumer secret. + * @param token The Twitter token. + * @param tokenSecret The Twitter token secret. + */ + private void setTwitterAuth(String consumerKey, String consumerSecret, String token, String tokenSecret) + { + twitterConsumerKey = consumerKey; + twitterConsumerSecret = consumerSecret; + twitterToken = token; + twitterTokenSecret = tokenSecret; + } + + /** + * Sets the default tags/categories. + * + * @param tags The tags. + */ + private void setTags(String tags) + { + defaultTags = tags; + } + + /** + * Sets the Ignored nicks. + * + * @param nicks The nicks to ignore + */ + private void setIgnoredNicks(String nicks) + { + if (Utils.isValidString(nicks)) + { + final StringTokenizer st = new StringTokenizer(nicks, ","); + + while (st.hasMoreTokens()) + { + ignoredNicks.add(st.nextToken().trim().toLowerCase()); + } + } + } + + /** + * Set the {@link Commands#TELL_CMD} parameters + * + * @param tellMaxDays The max number of days to hold messages for. + * @param tellMaxSize The maximmm number of messages to hold + */ + private void setTell(int tellMaxDays, int tellMaxSize) + { + this.tellMaxDays = tellMaxDays; + this.tellMaxSize = tellMaxSize; + } + + /** + * Saves the entries. + * + * @param isDayBackup Set the true if the daily backup file should also be created. + */ + private void saveEntries(boolean isDayBackup) + { + EntriesMgr.saveEntries(this, entries, history, isDayBackup); + } + + /** + * Sleeps for the specified number of seconds. + * + * @param secs The number of seconds to sleep for. + */ + private static void sleep(int secs) + { + try + { + Thread.sleep((long) (secs * 1000)); + } + catch (InterruptedException ignore) + { + ; // Do nothing + } + } + + /** + * Reruns the serialized object file. + * + * @return The file location. + */ + @SuppressWarnings("WeakerAccess") + public String getSerializedObject() + { + return serializedObject; + } + + /** + * Returns the bot's logger. + * + * @return The bot's logger. + */ + public final Log4JLogger getLogger() + { + return logger; + } + + /** + * Cleans the {@link #tellMessages} messages queue. + */ + private void cleanTellMessages() + { + TellMessagesMgr.cleanTellMessages(tellMessages, tellMaxDays); } /** @@ -931,8 +801,8 @@ public class Mobibot extends PircBot */ private static String buildComment(int entryIndex, int commentIndex, EntryComment comment) { - return (LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " + comment - .getComment()); + return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " + + comment.getComment()); } /** @@ -961,7 +831,7 @@ public class Mobibot extends PircBot */ private static String buildLink(int index, EntryLink entry, boolean isView) { - final StringBuilder buff = new StringBuilder(LINK_CMD + (index + 1) + ": "); + final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); buff.append('[').append(entry.getNick()).append(']'); @@ -974,7 +844,7 @@ public class Mobibot extends PircBot if (NO_TITLE.equals(entry.getTitle())) { - buff.append(bold(entry.getTitle())); + buff.append(Utils.bold(entry.getTitle())); } else { @@ -996,290 +866,77 @@ public class Mobibot extends PircBot */ private static String buildTags(int entryIndex, EntryLink entry) { - return (LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); + return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); } /** - * Copies a file. + * Get today's date for the feed. * - * @param in The source file. - * @param out The destination file. - * - * @throws IOException If the file could not be copied. - * @noinspection UnusedDeclaration + * @return Today's date. */ - private static void copyFile(File in, File out) - throws IOException + public synchronized String getToday() { - FileChannel inChannel = null; - FileChannel outChannel = null; - FileInputStream input = null; - FileOutputStream output = null; - - try - { - input = new FileInputStream(in); - output = new FileOutputStream(out); - - inChannel = input.getChannel(); - outChannel = output.getChannel(); - - inChannel.transferTo(0L, inChannel.size(), outChannel); - } - finally - { - try - { - if (inChannel != null) - { - inChannel.close(); - } - - if (input != null) - { - input.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - - try - { - if (outChannel != null) - { - outChannel.close(); - } - - if (output != null) - { - output.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - } + return today; } /** - * Ensures that the given location (File/URL) has a trailing slash (/) to indicate a directory. + * Returns the backlogs URL. * - * @param location The File or URL location. - * @param isUrl Set to true if the location is a URL - * - * @return The location ending with a slash. + * @return The backlogs URL. */ - private static String ensureDir(String location, boolean isUrl) + public final String getBacklogsUrl() { - if (isUrl) - { - if (location.charAt(location.length() - 1) == '/') - { - return location; - } - else - { - return location + '/'; - } - } - else - { - if (location.charAt(location.length() - 1) == File.separatorChar) - { - return location; - } - else - { - return location + File.separatorChar; - } - } + return backLogsUrl; } /** - * Returns the port. + * Sets the backlogs URL. * - * @param property The port property value. - * - * @return The port or default value if invalid. + * @param backLogsUrl The backlogs URL. */ - private static int getPort(String property) + private void setBacklogsUrl(String backLogsUrl) { - int port; - - try - { - port = Integer.parseInt(property); - } - catch (NumberFormatException ignore) - { - port = Mobibot.DEFAULT_PORT; - } - - return port; + this.backLogsUrl = backLogsUrl; } /** - * Returns the current Internet (beat) Time. + * Returns the weblog URL. * - * @return The Internet Time string. + * @return The weblog URL. */ - private static String internetTime() + public final String getWeblogUrl() { - final Calendar gc = Calendar.getInstance(); - - final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); - int hh = gc.get(Calendar.HOUR_OF_DAY); - final int mm = gc.get(Calendar.MINUTE); - final int ss = gc.get(Calendar.SECOND); - - hh -= offset; // GMT - hh += 1; // BMT - - long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); - - if (beats >= 1000) - { - beats -= (long) 1000; - } - else if (beats < 0) - { - beats += (long) 1000; - } - - if (beats < 10) - { - return ("@00" + String.valueOf(beats)); - } - else if (beats < 100) - { - return ("@0" + String.valueOf(beats)); - } - - return ('@' + String.valueOf(beats)); + return weblogUrl; } /** - * Performs a DNS lookup on the specified query. + * Sets the weblog URL. * - * @param query The IP address or hostname. - * - * @return The lookup query result string. - * - * @throws UnknownHostException If the host is unknown. + * @param weblogUrl The weblog URL. */ - private static String lookup(String query) - throws UnknownHostException + private void setWeblogUrl(String weblogUrl) { - final StringBuilder buffer = new StringBuilder(""); - - final InetAddress[] results = InetAddress.getAllByName(query); - String hostInfo; - - for (final InetAddress result : results) - { - if (result.getHostAddress().equals(query)) - { - hostInfo = result.getHostName(); - - if (hostInfo.equals(query)) - { - throw new UnknownHostException(); - } - } - else - { - hostInfo = result.getHostAddress(); - } - - if (buffer.length() > 0) - { - buffer.append(", "); - } - - buffer.append(hostInfo); - } - - return buffer.toString(); + this.weblogUrl = weblogUrl; } /** - * Stores the last 10 public messages and actions. + * Returns the log directory. * - * @param sender The nick of the person who sent the private message. - * @param message The actual message sent. - * @param isAction Set to true if the message is an action. + * @return the log directory. */ - private static void recap(String sender, String message, boolean isAction) + public final String getLogsDir() { - RECAP_ARRAY.add(HHMM_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") - + message); - - if (RECAP_ARRAY.size() > MAX_RECAP) - { - RECAP_ARRAY.remove(0); - } + return logsDir; } /** - * Sleeps for the specified number of seconds. + * Returns the irc server. * - * @param secs The number of seconds to sleep for. + * @return The irc server. */ - private static void sleep(int secs) + public final String getIrcServer() { - try - { - Thread.sleep((long) (secs * 1000)); - } - catch (InterruptedException ignore) - { - ; // Do nothing - } - } - - /** - * Returns today's date. - * - * @return Today's date in {@link #ISO_SDF ISO} format. - */ - private static String today() - { - return ISO_SDF.format(Calendar.getInstance().getTime()); - } - - /** - * Performs a whois IP query. - * - * @param query The IP address. - * - * @return The IP whois data, if any. - * - * @throws IOException If a connection error occurs. - */ - private static String[] whois(String query) - throws IOException - { - final WhoisClient whois = new WhoisClient(); - String[] lines; - - try - { - whois.setDefaultTimeout(CONNECT_TIMEOUT); - whois.connect(WHOIS_HOST); - whois.setSoTimeout(CONNECT_TIMEOUT); - whois.setSoLinger(false, 0); - - lines = whois.query('-' + query).split("\n"); - } - finally - { - whois.disconnect(); - } - - return lines; + return ircServer; } /** @@ -1287,7 +944,7 @@ public class Mobibot extends PircBot * * @param action The action. */ - public final void action(String action) + final void action(String action) { action(getChannel(), action); } @@ -1298,24 +955,14 @@ public class Mobibot extends PircBot * @param channel The channel. * @param action The action. */ - public final void action(String channel, String action) + final void action(String channel, String action) { - if (isValidString(channel) && isValidString(action)) + if (Utils.isValidString(channel) && Utils.isValidString(action)) { sendAction(channel, action); } } - /** - * Returns the current channel. - * - * @return The current channel. - */ - public final String getChannel() - { - return channel; - } - /** * Returns the {@link FeedFetcherCache feed info cache}. * @@ -1326,16 +973,6 @@ public class Mobibot extends PircBot return feedInfoCache; } - /** - * Returns the bot's logger. - * - * @return The bot's logger. - */ - public final Log4JLogger getLogger() - { - return logger; - } - /** * Responds with the bot's help. * @@ -1346,238 +983,247 @@ public class Mobibot extends PircBot { final String lcTopic = topic.toLowerCase(); - if (lcTopic.endsWith(HELP_POSTING_KEYWORD)) + if (lcTopic.endsWith(Commands.HELP_POSTING_KEYWORD)) { - send(sender, bold("Post a URL, by saying it on a line on its own:")); - send(sender, DOUBLE_INDENT + bold(" [] [" + TAGS_MARKER + "<+tag> [...]]")); - send(sender, "I will reply with a label, for example: " + bold(LINK_CMD + '1')); + send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); + send(sender, DOUBLE_INDENT + Utils.bold("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); + send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + '1')); send(sender, "To add a title, use a its label and a pipe:"); - send(sender, DOUBLE_INDENT + bold(LINK_CMD + "1:|This is the title")); + send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1:|This is the title")); send(sender, "To add a comment: "); - send(sender, DOUBLE_INDENT + bold(LINK_CMD + "1:This is a comment")); - send(sender, "I will reply with a label, for example: " + bold(LINK_CMD + "1.1")); + send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1:This is a comment")); + send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + "1.1")); send(sender, "To edit a comment, use its label: "); - send(sender, DOUBLE_INDENT + bold(LINK_CMD + "1.1:This is an edited comment")); + send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1.1:This is an edited comment")); send(sender, "To delete a comment, use its label and a minus sign: "); - send(sender, DOUBLE_INDENT + bold(LINK_CMD + "1.1:-")); + send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1.1:-")); send(sender, "You can also view a posting by saying its label."); } - else if (lcTopic.endsWith(HELP_TAGS_KEYWORD)) + else if (lcTopic.endsWith(Commands.HELP_TAGS_KEYWORD)) { - send(sender, bold("To categorize or tag a URL, use its label and a T:")); - send(sender, DOUBLE_INDENT + bold(LINK_CMD + "1T:<+tag|-tag> [...]")); + send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:")); + send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); } - else if (lcTopic.endsWith(VIEW_CMD)) + else if (lcTopic.endsWith(Commands.VIEW_CMD)) { send(sender, "To list or search the current URL posts:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + VIEW_CMD) + " [<start>] [<query>]"); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); } else if (lcTopic.endsWith(getChannel().substring(1).toLowerCase())) { send(sender, "To list the last 5 posts from the channel's weblog:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + getChannel().substring(1))); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + getChannel().substring(1))); } - else if (lcTopic.endsWith(GOOGLE_CMD)) + else if (lcTopic.endsWith(Commands.GOOGLE_CMD)) { send(sender, "To search Google:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + GOOGLE_CMD + " <query>")); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.GOOGLE_CMD + " <query>")); } - else if (lcTopic.endsWith(TWITTER_CMD) && isTwitterEnabled()) + else if (lcTopic.endsWith(Commands.TWITTER_CMD) && isTwitterEnabled()) { send(sender, "To post to Twitter:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + TWITTER_CMD + " <message>")); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TWITTER_CMD + " <message>")); } - else if (lcTopic.endsWith(RECAP_CMD)) + else if (lcTopic.endsWith(Commands.RECAP_CMD)) { send(sender, "To list the last 10 public channel messages:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + RECAP_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.RECAP_CMD)); } - else if (lcTopic.endsWith(CALC_CMD)) + else if (lcTopic.endsWith(Commands.CALC_CMD)) { send(sender, "To solve a mathematical calculation:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + CALC_CMD + " <calculation>")); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CALC_CMD + " <calculation>")); } - else if (lcTopic.endsWith(LOOKUP_CMD)) + else if (lcTopic.endsWith(Commands.LOOKUP_CMD)) { send(sender, "To perform a DNS lookup query:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>")); + send(sender, + DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.LOOKUP_CMD + " <ip address or hostname>")); } - else if (lcTopic.endsWith(TIME_CMD)) + else if (lcTopic.endsWith(Commands.TIME_CMD)) { send(sender, "To display a country's current date/time:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + TIME_CMD) + " [<country code>]"); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TIME_CMD) + " [<country code>]"); send(sender, "For a listing of the supported countries:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + TIME_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TIME_CMD)); } - else if (lcTopic.endsWith(QUOTE_CMD)) + else if (lcTopic.endsWith(Commands.QUOTE_CMD)) { send(sender, "To retrieve a random quote:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + QUOTE_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.QUOTE_CMD)); } - else if (lcTopic.endsWith(STOCK_CMD)) + else if (lcTopic.endsWith(Commands.STOCK_CMD)) { send(sender, "To retrieve a stock quote:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + STOCK_CMD + " <symbol[.country code]>")); + send(sender, + DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.STOCK_CMD + " <symbol[.country code]>")); } - else if (lcTopic.endsWith(DICE_CMD)) + else if (lcTopic.endsWith(Commands.DICE_CMD)) { send(sender, "To roll the dice:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + DICE_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.DICE_CMD)); } - else if (lcTopic.endsWith(WAR_CMD)) + else if (lcTopic.endsWith(Commands.WAR_CMD)) { send(sender, "To play war:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + WAR_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.WAR_CMD)); } - else if (lcTopic.endsWith(WEATHER_CMD)) + else if (lcTopic.endsWith(Commands.WEATHER_CMD)) { send(sender, "To display weather information:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + WEATHER_CMD + " <station id>")); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.WEATHER_CMD + " <station id>")); send(sender, "For a listing of the ICAO station IDs, please visit: " + Weather.STATIONS_URL); } - else if (lcTopic.endsWith(USERS_CMD)) + else if (lcTopic.endsWith(Commands.USERS_CMD)) { send(sender, "To list the users present on the channel:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + USERS_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.USERS_CMD)); } - else if (lcTopic.endsWith(INFO_CMD)) + else if (lcTopic.endsWith(Commands.INFO_CMD)) { send(sender, "To view information about the bot:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + INFO_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.INFO_CMD)); } - else if (lcTopic.endsWith(CYCLE_CMD)) + else if (lcTopic.endsWith(Commands.CYCLE_CMD)) { if (isOp(sender)) { send(sender, "To have the bot leave the channel and come back:"); - send(sender, DOUBLE_INDENT + bold("/msg " + getNick() + ' ' + CYCLE_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); } } - else if (lcTopic.endsWith(ME_CMD)) + else if (lcTopic.endsWith(Commands.ME_CMD)) { if (isOp(sender)) { send(sender, "To have the bot perform an action:"); - send(sender, DOUBLE_INDENT + bold("/msg " + getNick() + ' ' + ME_CMD + " <action>")); + send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); } } - else if (lcTopic.endsWith(SAY_CMD)) + else if (lcTopic.endsWith(Commands.SAY_CMD)) { if (isOp(sender)) { send(sender, "To have the bot say something on the channel:"); - send(sender, DOUBLE_INDENT + bold("/msg " + getNick() + ' ' + SAY_CMD + " <text>")); + send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); } } - else if (lcTopic.endsWith(VERSION_CMD)) + else if (lcTopic.endsWith(Commands.VERSION_CMD)) { if (isOp(sender)) { send(sender, "To view the version data (bot, java, etc.):"); - send(sender, DOUBLE_INDENT + bold("/msg " + getNick() + ' ' + VERSION_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); } } - else if (lcTopic.endsWith(MSG_CMD)) + else if (lcTopic.endsWith(Commands.MSG_CMD)) { if (isOp(sender)) { send(sender, "To have the bot send a private message to someone:"); - send(sender, DOUBLE_INDENT + bold("/msg " + getNick() + ' ' + MSG_CMD + " <nick> <text>")); + send(sender, + DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); } } - else if (lcTopic.startsWith(CURRENCY_CMD)) + else if (lcTopic.startsWith(Commands.CURRENCY_CMD)) { send(sender, "To convert from one currency to another:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD + " [100 USD to EUR]")); - if (lcTopic.endsWith(CURRENCY_CMD)) + if (lcTopic.endsWith(Commands.CURRENCY_CMD)) { send(sender, "For a listing of supported currencies:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + CURRENCY_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD)); } } - else if (lcTopic.startsWith(IGNORE_CMD)) + else if (lcTopic.startsWith(Commands.IGNORE_CMD)) { send(sender, "To check your ignore status:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + IGNORE_CMD)); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.IGNORE_CMD)); send(sender, "To toggle your ignore status:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + IGNORE_CMD + ' ' + IGNORE_ME_KEYWORD)); + send(sender, + DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); + + } + else if (lcTopic.startsWith(Commands.TELL_CMD)) + { + send(sender, "To send a message to someone when they join the channel:"); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TELL_CMD + " <nick> <message>")); + + send(sender, "To view queued and sent messages:"); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.VIEW_CMD)); + + send(sender, "Messages are kept for about " + Utils.bold(tellMaxDays) + " days."); } else { - send(sender, bold("Type a URL on " + getChannel() + " to post it.")); + send(sender, Utils.bold("Type a URL on " + getChannel() + " to post it.")); send(sender, "For more information on specific command, type:"); - send(sender, DOUBLE_INDENT + bold(getNick() + ": " + HELP_CMD + " <command>")); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.HELP_CMD + " <command>")); send(sender, "The commands are:"); - final String[] cmds = { - CALC_CMD, - CURRENCY_CMD, - DICE_CMD, - GOOGLE_CMD, - IGNORE_CMD, - INFO_CMD, - LOOKUP_CMD, - getChannel().substring(1), - HELP_POSTING_KEYWORD, - QUOTE_CMD, - RECAP_CMD, - STOCK_CMD, - HELP_TAGS_KEYWORD, - TIME_CMD, - TWITTER_CMD, - USERS_CMD, - VIEW_CMD, - WAR_CMD, - WEATHER_CMD - }; + final List<String> cmds = new ArrayList<String>(); + cmds.add(Commands.CALC_CMD); + cmds.add(Commands.CURRENCY_CMD); + cmds.add(Commands.DICE_CMD); + cmds.add(Commands.GOOGLE_CMD); + cmds.add(Commands.IGNORE_CMD); + cmds.add(Commands.INFO_CMD); + cmds.add(Commands.LOOKUP_CMD); + cmds.add(getChannel().substring(1)); + cmds.add(Commands.HELP_POSTING_KEYWORD); + cmds.add(Commands.QUOTE_CMD); + cmds.add(Commands.RECAP_CMD); + cmds.add(Commands.STOCK_CMD); + cmds.add(Commands.HELP_TAGS_KEYWORD); + cmds.add(Commands.TIME_CMD); + cmds.add(Commands.USERS_CMD); + cmds.add(Commands.VIEW_CMD); + cmds.add(Commands.WAR_CMD); + cmds.add(Commands.WEATHER_CMD); - Arrays.sort(cmds); + if (isTellEnabled()) + { + cmds.add(Commands.TELL_CMD); + } + + if (isTwitterEnabled()) + { + cmds.add(Commands.TWITTER_CMD); + } + + Collections.sort(cmds); final StringBuilder sb = new StringBuilder(0); - boolean isValidCmd = true; - for (int i = 0, cmdCount = 1; i < cmds.length; i++, cmdCount++) + for (int i = 0, cmdCount = 1; i < cmds.size(); i++, cmdCount++) { - if (cmds[i].equals(TWITTER_CMD)) + if (sb.length() > 0) { - isValidCmd = isTwitterEnabled(); + sb.append(" "); } - if (isValidCmd) - { - if (sb.length() > 0) - { - sb.append(" "); - } - - sb.append(cmds[i]); - } - else - { - cmdCount--; - } + sb.append(cmds.get(i)); // 5 commands per line or last command - if (sb.length() > 0 && (cmdCount == 5 || i == (cmds.length - 1))) + if (sb.length() > 0 && (cmdCount == 5 || i == (cmds.size() - 1))) { - send(sender, DOUBLE_INDENT + bold(sb.toString())); + send(sender, DOUBLE_INDENT + Utils.bold(sb.toString())); sb.setLength(0); cmdCount = 0; } - - isValidCmd = true; } if (isOp(sender)) { send(sender, "The op commands are:"); send(sender, - DOUBLE_INDENT + bold( - CYCLE_CMD + " " + ME_CMD + " " + MSG_CMD + " " + SAY_CMD + " " + VERSION_CMD) + DOUBLE_INDENT + Utils + .bold(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + Commands.VERSION_CMD) ); } } @@ -1594,65 +1240,12 @@ public class Mobibot extends PircBot send(sender, message, false); } - /** - * Sends a private message or notice. - * - * @param sender The nick of the person who sent the message. - * @param message The actual message. - * @param isPrivate Set to true if the response should be a private message, otherwise a notice is sent. - */ - public final void send(String sender, String message, boolean isPrivate) - { - if (isValidString(message) && isValidString(sender)) - { - if (isPrivate) - { - if (logger.isDebugEnabled()) - { - logger.debug("Sending message to " + sender + ": " + message); - } - - sendMessage(sender, message); - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("Sending notice to " + sender + ": " + message); - } - - sendNotice(sender, message); - } - } - } - - /** - * This method is called whenever an ACTION is sent from a user. - * - * @param sender The nick of the person who sent the action. - * @param login The login of the person who sent the action. - * @param hostname The hostname of the person who sent the action. - * @param target The target of the action, be it a channel or our nick. - * @param action The action carried out by the user. - */ - protected final void onAction(String sender, String login, String hostname, String target, String action) - { - if (target.equals(getChannel())) - { - recap(sender, action, true); - } - } - - /** - * This method carries out the actions to be performed when the PircBot gets disconnected. - * - * @noinspection UseOfSystemOutOrSystemErr - */ + @Override protected final void onDisconnect() { - if (isValidString(weblogURL)) + if (Utils.isValidString(weblogUrl)) { - setVersion(weblogURL); + setVersion(weblogUrl); } sleep(5); @@ -1694,12 +1287,12 @@ public class Mobibot extends PircBot setVersion(INFO_STRS[0]); - if (isValidString(ident)) + if (Utils.isValidString(ident)) { identify(ident); } - if (isValidString(identNick) && isValidString(identMsg)) + if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) { sendMessage(identNick, identMsg); } @@ -1708,14 +1301,16 @@ public class Mobibot extends PircBot } /** - * This method is called whenever a message is sent to a channel. + * Returns the current channel. * - * @param channel The channel to which the message was sent. - * @param sender The nick of the person who sent the message. - * @param login The login of the person who sent the message. - * @param hostname The hostname of the person who sent the message. - * @param message The actual message sent. + * @return The current channel. */ + public final String getChannel() + { + return channel; + } + + @Override protected final void onMessage(String channel, String sender, String login, String hostname, String message) { if (logger.isDebugEnabled()) @@ -1740,13 +1335,13 @@ public class Mobibot extends PircBot if (dupIndex == -1) { - if (!today().equals(getToday())) + if (!Utils.today().equals(getToday())) { isBackup = true; saveEntries(true); entries.clear(); - setToday(today()); + today = Utils.today(); } final StringBuilder tags = new StringBuilder(defaultTags); @@ -1762,7 +1357,7 @@ public class Mobibot extends PircBot } else { - if (isValidString(data[0])) + if (Utils.isValidString(data[0])) { title = data[0].trim(); } @@ -1778,7 +1373,7 @@ public class Mobibot extends PircBot final Document html = Jsoup.connect(link).get(); final String htmlTitle = html.title(); - if (isValidString(htmlTitle)) + if (Utils.isValidString(htmlTitle)) { title = htmlTitle; } @@ -1805,7 +1400,9 @@ public class Mobibot extends PircBot if (NO_TITLE.equals(entry.getTitle())) { send(sender, "Please specify a title, by typing:", true); - send(sender, DOUBLE_INDENT + bold(LINK_CMD + (index + 1) + ":|This is the title"), true); + send(sender, + DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + (index + 1) + ":|This is the title"), + true); } } else @@ -1829,11 +1426,11 @@ public class Mobibot extends PircBot args = cmds[1].trim(); } - if (cmd.startsWith(HELP_CMD)) + if (cmd.startsWith(Commands.HELP_CMD)) { helpResponse(sender, args); } - else if (cmd.equals(PING_CMD)) + else if (cmd.equals(Commands.PING_CMD)) { final String[] pings = { "is barely alive.", @@ -1854,125 +1451,72 @@ public class Mobibot extends PircBot action(channel, pings[r.nextInt(pings.length)]); } - else if (cmd.equals(PONG_CMD)) + else if (cmd.equals(Commands.PONG_CMD)) { - send(channel, PING_CMD, true); + send(channel, Commands.PING_CMD, true); } - else if (cmd.equals(RECAP_CMD)) + else if (cmd.equals(Commands.RECAP_CMD)) { recapResponse(sender, false); } - else if (cmd.equals(USERS_CMD)) + else if (cmd.equals(Commands.USERS_CMD)) { usersResponse(sender, false); } - else if (cmd.equals(INFO_CMD)) + else if (cmd.equals(Commands.INFO_CMD)) { infoResponse(sender, false); } - else if (cmd.equals(VERSION_CMD)) + else if (cmd.equals(Commands.VERSION_CMD)) { versionResponse(sender, false); } - else if (cmd.equals(DICE_CMD)) + else if (cmd.equals(Commands.DICE_CMD)) { - final Random r = new Random(); + send(getChannel(), shall_we_play_a_game); - int i = r.nextInt(6) + 1; - int y = r.nextInt(6) + 1; - final int playerTotal = i + y; - - send(getChannel(), - sender + " rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " - + bold(playerTotal)); - - i = r.nextInt(6) + 1; - y = r.nextInt(6) + 1; - final int total = i + y; - - action("rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " + bold(total)); - - if (playerTotal < total) - { - action("wins."); - } - else if (playerTotal > total) - { - action("lost."); - } - else - { - action("tied."); - } + Dice.roll(this, sender); } - else if (cmd.equals(WAR_CMD)) + else if (cmd.equals(Commands.WAR_CMD)) { - final Random r = new Random(); + send(getChannel(), shall_we_play_a_game); - int i; - int y; - - while (true) - { - i = r.nextInt(WAR_DECK.length); - y = r.nextInt(WAR_DECK.length); - - send(getChannel(), - sender + " drew the " + bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - action("drew the " + bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - - if (i != y) - { - break; - } - } - - if (i < y) - { - action("lost."); - } - else if (i > y) - { - action("wins."); - } - else - { - action("tied."); - } + War.play(this, sender); } else if (cmd.equalsIgnoreCase(getChannel().substring(1))) { feedResponse(sender); } - else if (cmd.startsWith(CURRENCY_CMD)) + else if (cmd.startsWith(Commands.CURRENCY_CMD)) { - new Thread(new CurrencyConverter(this, sender, args, today())).start(); + currencyConverter.setQuery(sender, args); + new Thread(currencyConverter).start(); } - else if (cmd.startsWith(LOOKUP_CMD)) + else if (cmd.startsWith(Commands.LOOKUP_CMD)) { lookupResponse(sender, args); } - else if (cmd.startsWith(VIEW_CMD)) + else if (cmd.startsWith(Commands.VIEW_CMD)) { viewResponse(sender, args, false); } - else if (cmd.startsWith(GOOGLE_CMD)) + else if (cmd.startsWith(Commands.GOOGLE_CMD)) { googleResponse(sender, args); } - else if (cmd.startsWith(TWITTER_CMD)) + else if (cmd.startsWith(Commands.TWITTER_CMD) && isTwitterEnabled()) { twitterResponse(sender, args); } - else if (cmd.startsWith(STOCK_CMD)) + else if (cmd.startsWith(Commands.STOCK_CMD)) { stockResponse(sender, args); } - else if (cmd.startsWith(QUOTE_CMD)) + else if (cmd.startsWith(Commands.QUOTE_CMD)) { new Thread(new Quote(this, sender)).start(); } - else if (cmd.startsWith(CALC_CMD)) + else if (cmd.startsWith(Commands.CALC_CMD)) { if (cmds.length > 1) { @@ -1993,23 +1537,27 @@ public class Mobibot extends PircBot } else { - helpResponse(sender, CALC_CMD); + helpResponse(sender, Commands.CALC_CMD); } } - else if (cmd.startsWith(TIME_CMD)) + else if (cmd.startsWith(Commands.TIME_CMD)) { - timeResponse(sender, args, false); + worldTime.timeResponse(this, sender, args, false); } - else if (cmd.startsWith(WEATHER_CMD)) + else if (cmd.startsWith(Commands.TELL_CMD) && isTellEnabled()) + { + tellResponse(sender, args); + } + else if (cmd.startsWith(Commands.WEATHER_CMD)) { weatherResponse(sender, args, false); } - else if (cmd.startsWith(IGNORE_CMD)) + else if (cmd.startsWith(Commands.IGNORE_CMD)) { if (!isOp(sender)) { final String nick = sender.toLowerCase(); - final boolean isMe = args.toLowerCase().startsWith(IGNORE_ME_KEYWORD); + final boolean isMe = args.toLowerCase().startsWith(Commands.IGNORE_ME_KEYWORD); if (ignoredNicks.contains(nick)) { @@ -2046,7 +1594,7 @@ public class Mobibot extends PircBot for (String nick : nicks) { - if (IGNORE_ME_KEYWORD.equals(nick)) + if (Commands.IGNORE_ME_KEYWORD.equals(nick)) { nick = sender.toLowerCase(); } @@ -2066,7 +1614,7 @@ public class Mobibot extends PircBot } } } - else if (message.matches(LINK_CMD + "[0-9]+:.*")) + else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { isCommand = true; @@ -2111,7 +1659,7 @@ public class Mobibot extends PircBot } entries.remove(index); - send(getChannel(), "Entry " + LINK_CMD + (index + 1) + " removed."); + send(getChannel(), "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); saveEntries(false); } else @@ -2192,7 +1740,7 @@ public class Mobibot extends PircBot } } } - else if (message.matches(LINK_CMD + "[0-9]+T:.*")) + else if (message.matches(Commands.LINK_CMD + "[0-9]+T:.*")) { isCommand = true; @@ -2237,7 +1785,7 @@ public class Mobibot extends PircBot } } } - else if (message.matches(LINK_CMD + "[0-9]+\\.[0-9]+:.*")) + else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { isCommand = true; @@ -2261,7 +1809,8 @@ public class Mobibot extends PircBot else if ("-".equals(cmd)) { entry.deleteComment(cindex); - send(getChannel(), "Comment " + LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); + send(getChannel(), + "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); saveEntries(false); } else if (cmd.charAt(0) == '?') @@ -2299,16 +1848,7 @@ public class Mobibot extends PircBot } } - /** - * This method is called whenever a private message is sent to the bot. - * - * @param sender The nick of the person who sent the private message. - * @param login The login of the person who sent the private message. - * @param hostname The hostname of the person who sent the private message. - * @param message The actual message sent. - * - * @noinspection UseOfSystemOutOrSystemErr - */ + @Override protected final void onPrivateMessage(String sender, String login, String hostname, String message) { if (logger.isDebugEnabled()) @@ -2325,7 +1865,7 @@ public class Mobibot extends PircBot args = cmds[1].trim(); } - if (cmd.startsWith(HELP_CMD)) + if (cmd.startsWith(Commands.HELP_CMD)) { helpResponse(sender, args); } @@ -2337,7 +1877,7 @@ public class Mobibot extends PircBot System.exit(0); } } - else if (cmd.equals(DIE_CMD)) + else if (cmd.equals(Commands.DIE_CMD)) { if (isOp(sender)) { @@ -2348,23 +1888,23 @@ public class Mobibot extends PircBot System.exit(0); } } - else if (cmd.equals(CYCLE_CMD)) + else if (cmd.equals(Commands.CYCLE_CMD)) { send(getChannel(), sender + " has just asked me to leave. I'll be back!"); sleep(0); partChannel(getChannel()); - sleep(5); + sleep(10); joinChannel(getChannel()); } - else if (cmd.equals(RECAP_CMD)) + else if (cmd.equals(Commands.RECAP_CMD)) { recapResponse(sender, true); } - else if (cmd.equals(USERS_CMD)) + else if (cmd.equals(Commands.USERS_CMD)) { usersResponse(sender, true); } - else if (cmd.startsWith(ADDLOG_CMD) && (cmds.length > 1)) + else if (cmd.startsWith(Commands.ADDLOG_CMD) && (cmds.length > 1)) { if (isOp(sender)) { @@ -2372,7 +1912,7 @@ public class Mobibot extends PircBot send(sender, history.toString(), true); } } - else if (cmd.startsWith(ME_CMD)) + else if (cmd.startsWith(Commands.ME_CMD)) { if (isOp(sender)) { @@ -2382,18 +1922,18 @@ public class Mobibot extends PircBot } else { - helpResponse(sender, ME_CMD); + helpResponse(sender, Commands.ME_CMD); } } } - else if (cmd.startsWith(NICK_CMD) && (cmds.length > 1)) + else if (cmd.startsWith(Commands.NICK_CMD) && (cmds.length > 1)) { if (isOp(sender)) { changeNick(args); } } - else if (cmd.startsWith(SAY_CMD)) + else if (cmd.startsWith(Commands.SAY_CMD)) { if (isOp(sender)) { @@ -2403,11 +1943,11 @@ public class Mobibot extends PircBot } else { - helpResponse(sender, SAY_CMD); + helpResponse(sender, Commands.SAY_CMD); } } } - else if (cmd.startsWith(MSG_CMD)) + else if (cmd.startsWith(Commands.MSG_CMD)) { if (isOp(sender)) { @@ -2417,41 +1957,44 @@ public class Mobibot extends PircBot if (args.length() > 2) { - System.out.println(msg[0] + ' ' + msg[1]); send(msg[0], msg[1], true); } else { - helpResponse(sender, MSG_CMD); + helpResponse(sender, Commands.MSG_CMD); } } else { - helpResponse(sender, MSG_CMD); + helpResponse(sender, Commands.MSG_CMD); } } } - else if (cmd.startsWith(VIEW_CMD)) + else if (cmd.startsWith(Commands.VIEW_CMD)) { viewResponse(sender, args, true); } - else if (cmd.startsWith(TIME_CMD)) + else if (cmd.startsWith(Commands.TIME_CMD)) { - timeResponse(sender, args, true); + worldTime.timeResponse(this, sender, args, true); } - else if (cmd.startsWith(WEATHER_CMD)) + else if (cmd.startsWith(Commands.TELL_CMD) && isTellEnabled()) + { + tellResponse(sender, args); + } + else if (cmd.startsWith(Commands.WEATHER_CMD)) { weatherResponse(sender, args, true); } - else if (cmd.equals(INFO_CMD)) + else if (cmd.equals(Commands.INFO_CMD)) { infoResponse(sender, true); } - else if (cmd.equals(VERSION_CMD)) + else if (cmd.equals(Commands.VERSION_CMD)) { versionResponse(sender, true); } - else if (cmd.equals(DEBUG_CMD)) + else if (cmd.equals(Commands.DEBUG_CMD)) { if (isOp(sender)) { @@ -2473,14 +2016,331 @@ public class Mobibot extends PircBot } } + @Override + protected final void onAction(String sender, String login, String hostname, String target, String action) + { + if (target.equals(getChannel())) + { + recap(sender, action, true); + } + } + /** - * Responds the title and links from the RSS feed. + * Stores the last 10 public messages and actions. + * + * @param sender The nick of the person who sent the private message. + * @param message The actual message sent. + * @param isAction Set to true if the message is an action. + */ + private void recap(String sender, String message, boolean isAction) + { + recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") + + message + ); + + if (recap.size() > MAX_RECAP) + { + recap.remove(0); + } + } + + @Override + protected void onJoin(String channel, String sender, String login, String hostname) + { + tellSendMessages(sender); + } + + @Override + protected void onNickChange(String oldNick, String login, String hostname, String newNick) + { + tellSendMessages(newNick); + } + + /** + * Checks and sends {@link Commands#TELL_CMD} messages. + * + * @param nickname The user's nickname. + */ + private void tellSendMessages(String nickname) + { + if (!nickname.equals(getNick()) && isTellEnabled()) + { + synchronized (tellMessages) + { + for (final TellMessage message : tellMessages) + { + if (message.isMatch(nickname)) + { + if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) + { + if (message.getSender().equals(nickname)) + { + send(nickname, + Utils.bold("You") + " wanted me to remind you: " + Colors.REVERSE + message + .getMessage() + Colors.REVERSE, true + ); + } + else + { + send(nickname, + message.getSender() + " wanted me to tell you: " + Colors.REVERSE + message + .getMessage() + Colors.REVERSE, + true + ); + } + + message.setReceived(); + + saveTellMessages(); + + } + else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message + .isNotified()) + { + send(nickname, + "Your message " + Colors.REVERSE + "[ID " + message.getId() + ']' + Colors.REVERSE + + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF + .format(message.getReceived()), + true + ); + + message.setNotified(); + + saveTellMessages(); + } + } + } + } + } + } + + /** + * Returns <code>true</code> if {@link Commands#TELL_CMD} is enabled. + * + * @return <code>true</code> or <code>false</code> + */ + private boolean isTellEnabled() + { + return tellMaxSize > 0 && tellMaxDays > 0; + } + + /** + * Sends a private message or notice. + * + * @param sender The nick of the person who sent the message. + * @param message The actual message. + * @param isPrivate Set to true if the response should be a private message, otherwise a notice is sent. + */ + public final void send(String sender, String message, boolean isPrivate) + { + if (Utils.isValidString(message) && Utils.isValidString(sender)) + { + if (isPrivate) + { + if (logger.isDebugEnabled()) + { + logger.debug("Sending message to " + sender + ": " + message); + } + + sendMessage(sender, message); + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Sending notice to " + sender + ": " + message); + } + + sendNotice(sender, message); + } + } + } + + /** + * Saves the {@link #tellMessages} messages queue. + */ + private void saveTellMessages() + { + TellMessagesMgr.save(getSerializedObject(), tellMessages, logger); + } + + /** + * Processes the {@link Commands#TELL_CMD} commands. + * + * @param sender The sender's nick. + * @param cmds The commands string. + */ + private void tellResponse(String sender, String cmds) + { + if (!Utils.isValidString(cmds)) + { + helpResponse(sender, Commands.TELL_CMD); + } + else if (cmds.startsWith(Commands.VIEW_CMD)) + { + if (isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + Commands.TELL_ALL_CMD)) + { + if (tellMessages.size() > 0) + { + synchronized (tellMessages) + { + for (final TellMessage message : tellMessages) + { + send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + + ']', + true + ); + } + } + } + else + { + send(sender, "There are no messages in the queue.", true); + } + } + else + { + boolean hasMessage = false; + + synchronized (tellMessages) + { + for (final TellMessage message : tellMessages) + { + if (message.isMatch(sender)) + { + if (!hasMessage) + { + hasMessage = true; + send(sender, "Here are your messages: ", true); + } + + if (message.isReceived()) + { + send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId() + + ", DELIVERED]", + true + ); + + } + else + { + send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() + + ", QUEUED]", + true + ); + } + + send(sender, DOUBLE_INDENT + message.getMessage(), true); + } + } + } + + if (!hasMessage) + { + send(sender, "You have no messages in the queue.", true); + } + else + { + send(sender, "To delete a message that you sent:"); + send(sender, + DOUBLE_INDENT + Utils + .bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id>") + ); + send(sender, "Messages are kept for about " + Utils.bold(tellMaxDays) + " days."); + } + } + } + else if (cmds.startsWith(Commands.TELL_DEL_CMD + ' ')) + { + final String[] split = cmds.split(" "); + + if (split.length == 2) + { + final String id = split[1]; + boolean deleted = false; + boolean found = false; + + synchronized (tellMessages) + { + for (final TellMessage message : tellMessages) + { + found = message.isMatchId(id); + + if (found && (message.getSender().equalsIgnoreCase(sender) || isOp(sender))) + { + tellMessages.remove(message); + + saveTellMessages(); + send(sender, "Your message was deleted from the queue.", true); + deleted = true; + break; + } + } + } + + if (!deleted) + { + if (found) + { + send(sender, "Only messages that you sent can be deleted.", true); + } + else + { + send(sender, "The specified message [ID " + id + "] could not be found.", true); + } + } + } + else + { + helpResponse(sender, Commands.TELL_CMD); + } + } + else + { + final String[] split = cmds.split(" ", 2); + + if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" "))) + { + if (tellMessages.size() < tellMaxSize) + { + final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); + + tellMessages.add(message); + + saveTellMessages(); + + send(sender, + "Message [ID " + message.getId() + "] was queued for " + Utils.bold(message.getRecipient()), + true); + } + else + { + send(sender, "Sorry, the messages queue is currently full.", true); + } + } + else + { + helpResponse(sender, Commands.TELL_CMD); + } + } + + cleanTellMessages(); + } + + /** + * Responds with the title and links from the RSS feed. * * @param sender The nick of the person who sent the private message. */ private void feedResponse(String sender) { - if (isValidString(feedURL)) + if (Utils.isValidString(feedURL)) { new Thread(new FeedReader(this, sender, feedURL)).start(); } @@ -2543,26 +2403,6 @@ public class Mobibot extends PircBot return buff.toString(); } - /** - * Get today's date. - * - * @return Today's date. - */ - private synchronized String getToday() - { - return today; - } - - /** - * Set today's date. - * - * @param today Today's date. - */ - private synchronized void setToday(String today) - { - this.today = today; - } - /** * Responds with the Google search results for the specified query. * @@ -2571,16 +2411,14 @@ public class Mobibot extends PircBot */ private void googleResponse(String sender, String query) { - if (query.length() > 0) { new Thread(new GoogleSearch(this, sender, query)).start(); } else { - helpResponse(sender, GOOGLE_CMD); + helpResponse(sender, Commands.GOOGLE_CMD); } - } /** @@ -2590,8 +2428,8 @@ public class Mobibot extends PircBot */ private boolean isTwitterEnabled() { - return isValidString(twitterConsumerKey) && isValidString(twitterConsumerSecret) && isValidString(twitterToken) - && isValidString(twitterTokenSecret); + return Utils.isValidString(twitterConsumerKey) && Utils.isValidString(twitterConsumerSecret) && Utils + .isValidString(twitterToken) && Utils.isValidString(twitterTokenSecret); } /** @@ -2616,7 +2454,7 @@ public class Mobibot extends PircBot } else { - helpResponse(sender, TWITTER_CMD); + helpResponse(sender, Commands.TWITTER_CMD); } } else @@ -2649,7 +2487,7 @@ public class Mobibot extends PircBot final long minutes = timeInSeconds / 60L; send(sender, "Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size() - + ']', + + (isTellEnabled() && isOp(sender) ? ", Messages: " + tellMessages.size() : "") + ']', isPrivate ); } @@ -2680,7 +2518,7 @@ public class Mobibot extends PircBot */ private boolean isIgnoredNick(String nick) { - return isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); + return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); } @@ -2697,7 +2535,6 @@ public class Mobibot extends PircBot for (final User user : users) { - if (user.getNick().equals(sender)) { return user.isOp(); @@ -2707,132 +2544,6 @@ public class Mobibot extends PircBot return false; } - /** - * Loads the backlogs. - * - * @param file The file containing the backlogs. - * - * @throws FileNotFoundException If the file was not found. - * @throws FeedException If an error occurred while reading the feed. - */ - private void loadBacklogs(String file) - throws FileNotFoundException, FeedException - { - history.clear(); - - final SyndFeedInput input = new SyndFeedInput(); - - InputStreamReader reader = null; - - try - { - reader = new InputStreamReader(new FileInputStream(new File(file))); - - final SyndFeed feed = input.build(reader); - - final List items = feed.getEntries(); - SyndEntry item; - - for (int i = items.size() - 1; i >= 0; i--) - { - item = (SyndEntryImpl) items.get(i); - history.add(item.getTitle()); - } - } - finally - { - if (reader != null) - { - try - { - reader.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - } - - } - - /** - * Loads the current entries. - * - * @param file The file containing the current entries. - * - * @throws FileNotFoundException If the file was not found. - * @throws FeedException If an error occurred while reading the feed. - */ - @SuppressWarnings("unchecked") - private void loadEntries(String file) - throws FileNotFoundException, FeedException - { - entries.clear(); - - final SyndFeedInput input = new SyndFeedInput(); - - InputStreamReader reader = null; - - try - { - reader = new InputStreamReader(new FileInputStream(new File(file))); - - final SyndFeed feed = input.build(reader); - - setToday(ISO_SDF.format(feed.getPublishedDate())); - - final List items = feed.getEntries(); - SyndEntry item; - SyndContent description; - String[] comments; - String author; - EntryLink entry; - - for (int i = items.size() - 1; i >= 0; i--) - { - item = (SyndEntryImpl) items.get(i); - author = item.getAuthor() - .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); - entry = new EntryLink(item.getLink(), - item.getTitle(), - author, - getChannel(), - item.getPublishedDate(), - item.getCategories()); - description = item.getDescription(); - comments = description.getValue().split("<br/>"); - - int split; - for (final String comment : comments) - { - split = comment.indexOf(": "); - - if (split != -1) - { - entry.addComment(comment.substring(split + 2).trim(), comment.substring(0, split).trim()); - } - } - - entries.add(entry); - } - } - finally - { - if (reader != null) - { - try - { - reader.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - } - } - /** * Responds with the results of a DNS query. * @@ -2845,7 +2556,7 @@ public class Mobibot extends PircBot { try { - send(getChannel(), lookup(query)); + send(getChannel(), Lookup.lookup(query)); } catch (UnknownHostException ignore) { @@ -2854,7 +2565,7 @@ public class Mobibot extends PircBot { try { - final String[] lines = whois(query); + final String[] lines = Lookup.whois(query); if ((lines != null) && (lines.length > 0)) { @@ -2893,7 +2604,7 @@ public class Mobibot extends PircBot } else { - helpResponse(sender, LOOKUP_CMD); + helpResponse(sender, Commands.LOOKUP_CMD); } } @@ -2905,297 +2616,12 @@ public class Mobibot extends PircBot */ private void recapResponse(String sender, boolean isPrivate) { - for (final String recap : RECAP_ARRAY) + for (final String recap : this.recap) { send(sender, recap, isPrivate); } } - /** - * Saves the entries. - * - * @param isDayBackup Set the true if the daily backup file should also be created. - */ - private void saveEntries(boolean isDayBackup) - { - if (logger.isDebugEnabled()) - { - logger.debug("Saving..."); - } - - if (isValidString(logsDir) && isValidString(weblogURL)) - { - FileWriter fw = null; - - try - { - fw = new FileWriter(new File(logsDir + CURRENT_XML)); - - SyndFeed rss = new SyndFeedImpl(); - rss.setFeedType("rss_2.0"); - rss.setTitle(getChannel() + " IRC Links"); - rss.setDescription("Links from " + ircServer + " on " + getChannel()); - rss.setLink(weblogURL); - rss.setPublishedDate(Calendar.getInstance().getTime()); - rss.setLanguage("en"); - - EntryLink entry; - StringBuffer buff; - EntryComment comment; - final List<SyndEntry> items = new ArrayList<SyndEntry>(0); - SyndEntry item; - SyndContent description; - - for (int i = (entries.size() - 1); i >= 0; --i) - { - entry = entries.get(i); - - buff = new StringBuffer( - "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + ircServer + '/' + entry - .getChannel() + "\"><b>" + entry.getChannel() + "</b></a>" - ); - - if (entry.getCommentsCount() > 0) - { - buff.append(" <br/><br/>"); - - final EntryComment[] comments = entry.getComments(); - - for (int j = 0; j < comments.length; j++) - { - comment = comments[j]; - - if (j > 0) - { - buff.append(" <br/>"); - } - - buff.append(comment.getNick()).append(": ").append(comment.getComment()); - } - } - - item = new SyndEntryImpl(); - item.setLink(entry.getLink()); - description = new SyndContentImpl(); - description.setValue(buff.toString()); - item.setDescription(description); - item.setTitle(entry.getTitle()); - item.setPublishedDate(entry.getDate()); - item.setAuthor(getChannel().substring(1) + '@' + ircServer + " (" + entry.getNick() + ')'); - item.setCategories(entry.getTags()); - - items.add(item); - } - - rss.setEntries(items); - - if (logger.isDebugEnabled()) - { - logger.debug("Writing the entries feed."); - } - - final SyndFeedOutput output = new SyndFeedOutput(); - output.output(rss, fw); - fw.close(); - - fw = new FileWriter(new File(logsDir + getToday() + ".xml")); - output.output(rss, fw); - - if (isDayBackup) - { - if (isValidString(backlogsURL)) - { - if (history.indexOf(getToday()) == -1) - { - history.add(getToday()); - - while (history.size() > MAX_BACKLOGS) - { - history.remove(0); - } - } - - fw.close(); - fw = new FileWriter(new File(logsDir + NAV_XML)); - rss = new SyndFeedImpl(); - rss.setFeedType("rss_2.0"); - rss.setTitle(getChannel() + " IRC Links Backlogs"); - rss.setDescription("Backlogs of Links from " + ircServer + " on " + getChannel()); - rss.setLink(backlogsURL); - rss.setPublishedDate(Calendar.getInstance().getTime()); - - String date; - items.clear(); - - for (int i = (history.size() - 1); i >= 0; --i) - { - date = history.get(i); - - item = new SyndEntryImpl(); - item.setLink(backlogsURL + date + ".xml"); - item.setTitle(date); - description = new SyndContentImpl(); - description.setValue("Links for " + date); - item.setDescription(description); - - items.add(item); - } - - rss.setEntries(items); - - if (logger.isDebugEnabled()) - { - logger.debug("Writing the backlog feed."); - } - - output.output(rss, fw); - } - else - { - logger.warn("Unable to generate the backlogs feed. No property configured."); - } - } - } - catch (Exception e) - { - logger.warn("Unable to generate the feed.", e); - } - finally - { - try - { - if (fw != null) - { - fw.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - } - } - else - { - logger.warn("Unable to generate the feed. At least one of the required property is missing."); - } - } - - /** - * Sets the backlogs URL. - * - * @param backlogsURL The backlogs URL. - */ - private void setBacklogsURL(String backlogsURL) - { - this.backlogsURL = backlogsURL; - } - - /** - * Sets the del.icio.us authentication. - * - * @param username The del.icio.us username. - * @param password The del.icio.us password. - */ - private void setDeliciousAuth(String username, String password) - { - delicious = new DeliciousPoster(username, password, ircServer); - } - - /** - * Sets the feed URL. - * - * @param feedURL The feed URL. - */ - private void setFeedURL(String feedURL) - { - this.feedURL = feedURL; - } - - /** - * Sets the Twitter consumerSecret and password.. - * - * @param consumerKey The Twitter consumer key. - * @param consumerSecret The Twitter consumer secret. - * @param token The Twitter token. - * @param tokenSecret The Twitter token secret. - */ - private void setTwitterAuth(String consumerKey, String consumerSecret, String token, String tokenSecret) - { - twitterConsumerKey = consumerKey; - twitterConsumerSecret = consumerSecret; - twitterToken = token; - twitterTokenSecret = tokenSecret; - } - - /** - * Sets the ident password. - * - * @param pwd The password. - */ - private void setIdent(String pwd) - { - ident = pwd; - } - - /** - * Sets the ident message. - * - * @param msg The message. - */ - private void setIdentMsg(String msg) - { - identMsg = msg; - } - - /** - * Sets the ident nickname. - * - * @param nick The nickname. - */ - private void setIdentNick(String nick) - { - identNick = nick; - } - - /** - * Sets the Ignored nicks. - * - * @param nicks The nicks to ignore - */ - private void setIgnoredNicks(String nicks) - { - if (isValidString(nicks)) - { - final StringTokenizer st = new StringTokenizer(nicks, ","); - - while (st.hasMoreTokens()) - { - ignoredNicks.add(st.nextToken().trim().toLowerCase()); - } - } - } - - /** - * Sets the default tags/categories. - * - * @param tags The tags. - */ - private void setTags(String tags) - { - defaultTags = tags; - } - - /** - * Sets the weblog URL. - * - * @param weblogURL The weblog URL. - */ - private void setWeblogURL(String weblogURL) - { - this.weblogURL = weblogURL; - } - /** * Responds with the specified stock quote. * @@ -3210,56 +2636,7 @@ public class Mobibot extends PircBot } else { - helpResponse(sender, STOCK_CMD); - } - } - - /** - * Responds with the current time. - * - * @param sender The nick of the person who sent the message. - * @param args The time command arguments. - * @param isPrivate Set to true is the response should be send as a private message. - */ - private void timeResponse(String sender, String args, boolean isPrivate) - { - boolean isInvalidTz = false; - final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); - final String response; - - if (tz != null) - { - if (tz.equals(BEATS_KEYWORD)) - { - response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); - } - else - { - TIME_SDF.setTimeZone(TimeZone.getTimeZone(tz)); - response = TIME_SDF.format(Calendar.getInstance().getTime()) + tz.substring(tz.indexOf('/') + 1) - .replace('_', ' '); - } - } - else - { - isInvalidTz = true; - response = "The supported time zones/countries are: " + COUNTRIES_MAP.keySet().toString(); - } - - if (isPrivate) - { - send(sender, response, true); - } - else - { - if (isInvalidTz) - { - send(sender, response); - } - else - { - send(getChannel(), response); - } + helpResponse(sender, Commands.STOCK_CMD); } } @@ -3366,8 +2743,9 @@ public class Mobibot extends PircBot if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + bold( - getNick() + ": " + VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), isPrivate + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), + isPrivate ); break; @@ -3382,7 +2760,7 @@ public class Mobibot extends PircBot if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + bold(getNick() + ": " + VIEW_CMD + ' ' + (i + 1)), + "To view more, try: " + Utils.bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), isPrivate); break; diff --git a/src/main/java/net/thauvin/erik/mobibot/Quote.java b/src/main/java/net/thauvin/erik/mobibot/Quote.java index a839584..721a384 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Quote.java +++ b/src/main/java/net/thauvin/erik/mobibot/Quote.java @@ -1,7 +1,7 @@ /* * @(#)Quote.java * - * Copyright (c) 2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import org.jibble.pircbot.Colors; @@ -53,6 +51,13 @@ import java.net.URLConnection; */ public class Quote implements Runnable { + + /** + * The I Heart Quotes URL. + */ + private static final String QUOTE_URL = + "http://www.iheartquotes.com/api/v1/random?format=json&max_lines=1&source=esr+humorix_misc+humorix_stories+joel_on_software+macintosh+math+mav_flame+osp_rules+paul_graham+prog_style+subversion"; + /** * The bot. */ @@ -82,7 +87,7 @@ public class Quote implements Runnable { try { - final URL url = new URL("http://www.iheartquotes.com/api/v1/random?format=json&max_lines=1"); + final URL url = new URL(QUOTE_URL); final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); @@ -96,7 +101,7 @@ public class Quote implements Runnable final JSONObject json = new JSONObject(sb.toString()); - bot.send(bot.getChannel(), Colors.BLUE + json.getString("quote") + Colors.BLUE); + bot.send(bot.getChannel(), Colors.CYAN + json.getString("quote") + Colors.CYAN); reader.close(); } diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java index d8ebe4e..7caf1ec 100644 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,59 +1,115 @@ +/* + * @(#)ReleaseInfo.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Sun Apr 20 23:26:28 PDT 2014 */ +/* Creation date Fri Apr 25 18:08:16 PDT 2014 */ package net.thauvin.erik.mobibot; import java.util.Date; /** * This class provides information gathered from the build environment. - * + * * @author JReleaseInfo AntTask */ -public class ReleaseInfo { +public class ReleaseInfo +{ - /** - * Disables the default constructor. - * @throws UnsupportedOperationException if the constructor is called. - */ - private ReleaseInfo() throws UnsupportedOperationException { - throw new UnsupportedOperationException("Illegal constructor call."); - } + /** + * buildDate (set during build process to 1398474496363L). + */ + private static final Date buildDate = new Date(1398474496363L); + /** + * project (set during build process to "mobibot"). + */ + private static final String project = "mobibot"; - /** buildDate (set during build process to 1398061588708L). */ - private static final Date buildDate = new Date(1398061588708L); + /** + * version (set during build process to "0.6"). + */ + private static final String version = "0.6"; - /** - * Get buildDate (set during build process to Sun Apr 20 23:26:28 PDT 2014). - * @return Date buildDate - */ - public static Date getBuildDate() { return buildDate; } + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if the constructor is called. + */ + private ReleaseInfo() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + /** + * Get buildDate (set during build process to Fri Apr 25 18:08:16 PDT 2014). + * + * @return Date buildDate + */ + public static Date getBuildDate() + { + return buildDate; + } - /** project (set during build process to "mobibot"). */ - private static final String project = "mobibot"; + /** + * Get project (set during build process to "mobibot"). + * + * @return String project + */ + public static String getProject() + { + return project; + } - /** - * Get project (set during build process to "mobibot"). - * @return String project - */ - public static String getProject() { return project; } + /** + * Get version (set during build process to "0.6"). + * + * @return String version + */ + public static String getVersion() + { + return version; + } - - /** version (set during build process to "0.6"). */ - private static final String version = "0.6"; - - /** - * Get version (set during build process to "0.6"). - * @return String version - */ - public static String getVersion() { return version; } - - - /** - * Get buildNumber (set during build process to 0). - * @return int buildNumber - */ - public static int getBuildNumber() { return 0; } + /** + * Get buildNumber (set during build process to 0). + * + * @return int buildNumber + */ + public static int getBuildNumber() + { + return 0; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java index f453f3c..492c1f5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java @@ -1,7 +1,7 @@ /* * @(#)StockQuote.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import com.Ostermiller.util.CSVParser; @@ -46,7 +44,6 @@ import java.io.IOException; * Retrieves a stock quote from Yahoo!. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Feb 7, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java index 6c49325..2ff6379 100644 --- a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java +++ b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java @@ -1,133 +1,159 @@ package net.thauvin.erik.mobibot; -import javax.swing.SwingUtilities; +import javax.swing.*; /** - * This is the 3rd version of SwingWorker (also known as - * SwingWorker 3), an abstract class that you subclass to - * perform GUI-related work in a dedicated thread. For - * instructions on and examples of using this class, see: - * + * This is the 3rd version of SwingWorker (also known as SwingWorker 3), an abstract class that you subclass to perform + * GUI-related work in a dedicated thread. For instructions on and examples of using this class, see: + * <p/> * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html - * - * Note that the API changed slightly in the 3rd version: - * You must now invoke start() on the SwingWorker after - * creating it. + * <p/> + * Note that the API changed slightly in the 3rd version: You must now invoke start() on the SwingWorker after creating + * it. * * @noinspection ALL */ -public abstract class SwingWorker { - private Object value; // see getValue(), setValue() +public abstract class SwingWorker +{ + private Object value; // see getValue(), setValue() - /** - * Class to maintain reference to current worker thread - * under separate synchronization control. - */ - private static class ThreadVar { - private Thread thread; - ThreadVar(Thread t) { thread = t; } - synchronized Thread get() { return thread; } - synchronized void clear() { thread = null; } - } + private ThreadVar threadVar; - private ThreadVar threadVar; + /** + * Start a thread that will call the <code>construct</code> method and then exit. + */ + public SwingWorker() + { + final Runnable doFinished = new Runnable() + { + public void run() + { + finished(); + } + }; - /** - * Get the value produced by the worker thread, or null if it - * hasn't been constructed yet. - */ - protected synchronized Object getValue() { - return value; - } + Runnable doConstruct = new Runnable() + { + public void run() + { + try + { + setValue(construct()); + } + finally + { + threadVar.clear(); + } - /** - * Set the value produced by worker thread - */ - private synchronized void setValue(Object x) { - value = x; - } + SwingUtilities.invokeLater(doFinished); + } + }; - /** - * Compute the value to be returned by the <code>get</code> method. - */ - public abstract Object construct(); + Thread t = new Thread(doConstruct); + threadVar = new ThreadVar(t); + } - /** - * Called on the event dispatching thread (not on the worker thread) - * after the <code>construct</code> method has returned. - */ - public void finished() { - } + /** + * Called on the event dispatching thread (not on the worker thread) after the <code>construct</code> method has + * returned. + */ + public void finished() + { + } - /** - * A new method that interrupts the worker thread. Call this method - * to force the worker to stop what it's doing. - */ - public void interrupt() { - Thread t = threadVar.get(); - if (t != null) { - t.interrupt(); - } - threadVar.clear(); - } + /** + * Compute the value to be returned by the <code>get</code> method. + */ + public abstract Object construct(); - /** - * Return the value created by the <code>construct</code> method. - * Returns null if either the constructing thread or the current - * thread was interrupted before a value was produced. - * - * @return the value created by the <code>construct</code> method - */ - public Object get() { - while (true) { - Thread t = threadVar.get(); - if (t == null) { - return getValue(); - } - try { - t.join(); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); // propagate - return null; - } - } - } + /** + * A new method that interrupts the worker thread. Call this method to force the worker to stop what it's doing. + */ + public void interrupt() + { + Thread t = threadVar.get(); + if (t != null) + { + t.interrupt(); + } + threadVar.clear(); + } + /** + * Return the value created by the <code>construct</code> method. Returns null if either the constructing thread or + * the current thread was interrupted before a value was produced. + * + * @return the value created by the <code>construct</code> method + */ + public Object get() + { + while (true) + { + Thread t = threadVar.get(); + if (t == null) + { + return getValue(); + } + try + { + t.join(); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); // propagate + return null; + } + } + } - /** - * Start a thread that will call the <code>construct</code> method - * and then exit. - */ - public SwingWorker() { - final Runnable doFinished = new Runnable() { - public void run() { finished(); } - }; + /** + * Get the value produced by the worker thread, or null if it hasn't been constructed yet. + */ + protected synchronized Object getValue() + { + return value; + } - Runnable doConstruct = new Runnable() { - public void run() { - try { - setValue(construct()); - } - finally { - threadVar.clear(); - } + /** + * Set the value produced by worker thread + */ + private synchronized void setValue(Object x) + { + value = x; + } - SwingUtilities.invokeLater(doFinished); - } - }; + /** + * Start the worker thread. + */ + public void start() + { + Thread t = threadVar.get(); + if (t != null) + { + t.start(); + } + } - Thread t = new Thread(doConstruct); - threadVar = new ThreadVar(t); - } + /** + * Class to maintain reference to current worker thread under separate synchronization control. + */ + private static class ThreadVar + { + private Thread thread; - /** - * Start the worker thread. - */ - public void start() { - Thread t = threadVar.get(); - if (t != null) { - t.start(); - } - } + ThreadVar(Thread t) + { + thread = t; + } + + synchronized Thread get() + { + return thread; + } + + synchronized void clear() + { + thread = null; + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java new file mode 100644 index 0000000..1872208 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -0,0 +1,146 @@ +/* + * @(#)TellMessage.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; + +/** + * The <code>TellMessage</code> class. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2014-04-24 + * @since 1.0 + */ +public class TellMessage implements Serializable +{ + private static final long serialVersionUID = 1L; + + private final String sender; + + private final String recipient; + + private final String message; + + private final String id; + + final private Date queued; + + private Date received; + + private boolean isReceived; + + private boolean isNotified; + + /** + * Create a new message. + * + * @param sender The sender's nick. + * @param recipient The recipient's nick. + * @param message The message. + */ + public TellMessage(String sender, String recipient, String message) + { + this.sender = sender; + this.recipient = recipient; + this.message = message; + + this.queued = Calendar.getInstance().getTime(); + this.id = Utils.TIMESTAMP_SDF.format(this.queued); + + } + + public String getSender() + { + return sender; + } + + public String getRecipient() + { + return recipient; + } + + public String getMessage() + { + return message; + } + + public Date getQueued() + { + return queued; + } + + public Date getReceived() + { + return received; + } + + public void setReceived() + { + this.received = Calendar.getInstance().getTime(); + this.isReceived = true; + } + + public boolean isNotified() + { + return this.isNotified; + } + + public void setNotified() + { + this.isNotified = true; + } + + public String getId() + { + return this.id; + } + + public boolean isReceived() + { + return this.isReceived; + } + + public boolean isMatchId(String id) + { + return this.id.equals(id); + } + + public boolean isMatch(String nick) + { + return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java new file mode 100644 index 0000000..c54f636 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -0,0 +1,156 @@ +/* + * @(#)TellMessagesMgr.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot; + +import org.apache.commons.logging.impl.Log4JLogger; + +import java.io.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * Managers the {@link Commands#TELL_CMD} messages. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2014-04-26 + * @since 1.0 + */ +public class TellMessagesMgr +{ + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + */ + private TellMessagesMgr() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Loads the messages. + * + * @param file The serialized objects file. + * @param logger The logger. + * + * @return The {@link net.thauvin.erik.mobibot.TellMessage} array. + */ + @SuppressWarnings("unchecked") + public static List<TellMessage> load(String file, Log4JLogger logger) + { + try + { + final ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file))); + + try + { + + return ((List<TellMessage>) input.readObject()); + } + finally + { + input.close(); + } + } + catch (FileNotFoundException ignore) + { + ; // Do nothing. + } + catch (IOException e) + { + logger.error("An IO error occurred loading the messages queue.", e); + } + catch (Exception e) + { + logger.getLogger().error("An error occurred loading the messages queue.", e); + } + + return (List<TellMessage>) new ArrayList(); + } + + /** + * Saves the messages. + * + * @param file The serialized objects file. + * @param messages The {@link net.thauvin.erik.mobibot.TellMessage} array. + * @param logger The logger. + */ + public static void save(String file, List<TellMessage> messages, Log4JLogger logger) + { + try + { + final ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file))); + + try + { + output.writeObject(messages); + } + finally + { + output.close(); + } + } + catch (IOException e) + { + logger.error("Unable to save messages queue.", e); + } + } + + /** + * Cleans the messages queue. + */ + public static void cleanTellMessages(List<TellMessage> tellMessages, int tellMaxDays) + { + final Calendar maxDate = Calendar.getInstance(); + final Date today = new Date(); + + synchronized (tellMessages) + { + for (final TellMessage message : tellMessages) + { + maxDate.setTime(message.getQueued()); + maxDate.add(Calendar.DATE, tellMaxDays); + + if (maxDate.getTime().before(today)) + { + tellMessages.remove(message); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/Twitter.java index 6742092..23fd404 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/Twitter.java @@ -1,7 +1,7 @@ /* * @(#)Twitter.java * - * Copyright (C) 2007 Erik C. Thauvin + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import twitter4j.Status; @@ -44,7 +42,6 @@ import twitter4j.conf.ConfigurationBuilder; * Inserts presence information into Twitter. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> - * @version $Revision$, $Date$ * @created Sept 10, 2008 * @since 1.0 */ @@ -121,8 +118,8 @@ public class Twitter implements Runnable final Status status = twitter.updateStatus(message + " (" + sender + ')'); bot.send(sender, - "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status - .getId() + "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status + .getId() ); } catch (Exception e) diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 06b8589..1bcb5ba 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -20,7 +20,6 @@ import java.io.InputStreamReader; * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @author <a href="http://twitter4j.org/en/code-examples.html#oauth">http://twitter4j.org/en/code-examples.html#oauth</a> - * @version $Revision$, $Date$ * @created Sep 13, 2010 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java new file mode 100644 index 0000000..3ff10ab --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -0,0 +1,309 @@ +/* + * @(#)Utils.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot; + +import org.jibble.pircbot.Colors; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +/** + * Miscellaneous utilities class. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2014-04-26 + * @since 1.0 + */ +public class Utils +{ + /** + * The timestamp simple date format. + */ + public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); + + /** + * The UTC (yyyy-MM-dd HH:mm) simple date format. + */ + static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + /** + * The ISO (YYYY-MM-DD) simple date format. + */ + static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + */ + private Utils() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Converts XML/XHTML entities to plain text. + * + * @param str The string to unescape. + * + * @return The unescaped string. + */ + public static String unescapeXml(String str) + { + String s = str.replaceAll("&", "&"); + s = s.replaceAll("<", "<"); + s = s.replaceAll(">", ">"); + s = s.replaceAll(""", "\""); + s = s.replaceAll("'", "'"); + s = s.replaceAll("'", "'"); + + return s; + } + + /** + * Copies a file. + * + * @param in The source file. + * @param out The destination file. + * + * @throws java.io.IOException If the file could not be copied. + * @noinspection UnusedDeclaration + */ + public static void copyFile(File in, File out) + throws IOException + { + FileChannel inChannel = null; + FileChannel outChannel = null; + FileInputStream input = null; + FileOutputStream output = null; + + try + { + input = new FileInputStream(in); + output = new FileOutputStream(out); + + inChannel = input.getChannel(); + outChannel = output.getChannel(); + + inChannel.transferTo(0L, inChannel.size(), outChannel); + } + finally + { + try + { + if (inChannel != null) + { + inChannel.close(); + } + + if (input != null) + { + input.close(); + } + } + catch (Exception ignore) + { + ; // Do nothing + } + + try + { + if (outChannel != null) + { + outChannel.close(); + } + + if (output != null) + { + output.close(); + } + } + catch (Exception ignore) + { + ; // Do nothing + } + } + } + + /** + * Returns the current Internet (beat) Time. + * + * @return The Internet Time string. + */ + public static String internetTime() + { + final Calendar gc = Calendar.getInstance(); + + final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); + int hh = gc.get(Calendar.HOUR_OF_DAY); + final int mm = gc.get(Calendar.MINUTE); + final int ss = gc.get(Calendar.SECOND); + + hh -= offset; // GMT + hh += 1; // BMT + + long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); + + if (beats >= 1000) + { + beats -= (long) 1000; + } + else if (beats < 0) + { + beats += (long) 1000; + } + + if (beats < 10) + { + return ("@00" + String.valueOf(beats)); + } + else if (beats < 100) + { + return ("@0" + String.valueOf(beats)); + } + + return ('@' + String.valueOf(beats)); + } + + /** + * Returns a property as an int. + * + * @param property The port property value. + * @param def The default property value. + * + * @return The port or default value if invalid. + */ + public static int getIntProperty(String property, int def) + { + int prop; + + try + { + prop = Integer.parseInt(property); + } + catch (NumberFormatException ignore) + { + prop = def; + } + + return prop; + } + + /** + * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. + * + * @param location The File or URL location. + * @param isUrl Set to true if the location is a URL + * + * @return The location ending with a slash. + */ + public static String ensureDir(String location, boolean isUrl) + { + if (isUrl) + { + if (location.charAt(location.length() - 1) == '/') + { + return location; + } + else + { + return location + '/'; + } + } + else + { + if (location.charAt(location.length() - 1) == File.separatorChar) + { + return location; + } + else + { + return location + File.separatorChar; + } + } + } + + /** + * Returns true if the given string is valid. + * + * @param s The string to validate. + * + * @return true if the string is non-empty and not null, false otherwise. + */ + public static boolean isValidString(String s) + { + return (s != null) && (s.trim().length() > 0); + } + + /** + * Makes the given int bold. + * + * @param i The int. + * + * @return The bold string. + */ + public static String bold(int i) + { + return Colors.BOLD + i + Colors.BOLD; + } + + /** + * Returns today's date. + * + * @return Today's date in {@link #ISO_SDF ISO} format. + */ + public static String today() + { + return ISO_SDF.format(Calendar.getInstance().getTime()); + } + + /** + * Makes the given string bold. + * + * @param s The string. + * + * @return The bold string. + */ + public static String bold(String s) + { + return Colors.BOLD + s + Colors.BOLD; + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/War.java new file mode 100644 index 0000000..6430374 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/War.java @@ -0,0 +1,110 @@ +/* + * @(#)War.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot; + +import java.util.Random; + +/** + * The <code>War</code> class. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2014-04-28 + * @since 1.0 + */ +public class War +{ + /** + * The deck of card for the {@link net.thauvin.erik.mobibot.Commands#WAR_CMD war} command. + */ + private static final String[] WAR_DECK = + new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; + + /** + * The suits for the deck of card for the {@link Commands#WAR_CMD war} command. + */ + private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + */ + private War() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Plays war. + * + * @param bot The bot. + * @param sender The sender's nickname. + */ + public static void play(Mobibot bot, String sender) + { + final Random r = new Random(); + + int i; + int y; + + while (true) + { + i = r.nextInt(WAR_DECK.length); + y = r.nextInt(WAR_DECK.length); + + bot.send(bot.getChannel(), + sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + + if (i != y) + { + break; + } + } + + if (i < y) + { + bot.action("lost."); + } + else if (i > y) + { + bot.action("wins."); + } + else + { + bot.action("tied."); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/Weather.java index 9b264ec..8f8442d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/Weather.java @@ -1,7 +1,7 @@ /* * @(#)Weather.java * - * Copyright (c) 2004, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,8 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * */ + package net.thauvin.erik.mobibot; import net.sf.jweather.metar.Metar; @@ -48,7 +46,6 @@ import java.util.Iterator; * Fetches the weather data from a specific station ID. * * @author Erik C. Thauvin - * @version $Revision$, $Date$ * @created Feb 7, 2004 * @since 1.0 */ @@ -116,9 +113,9 @@ public class Weather implements Runnable bot.send(sender, "Station ID: " + metar.getStationID(), isPrivate); bot.send(sender, - "At: " + metar.getDateString() + " UTC (" + ( - ((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + " minutes ago)", - isPrivate + "At: " + Utils.UTC_SDF.format(metar.getDate()) + " UTC (" + ( + ((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + " minutes ago)", + isPrivate ); result = metar.getWindSpeedInMPH(); @@ -126,28 +123,30 @@ public class Weather implements Runnable if (result != null) { bot.send(sender, - "Wind Speed: " + result + " mph, " + metar.getWindSpeedInKnots() + " knots", isPrivate); + "Wind Speed: " + result + " mph, " + metar.getWindSpeedInKnots() + " knots, " + metar + .getWindSpeedInMPS() + " m/s", + isPrivate + ); } result = metar.getVisibility(); if (result != null) { - if (!metar.getVisibilityLessThan()) - { - bot.send(sender, "Visibility: " + NUMBER_FORMAT.format(result) + " mile(s)", isPrivate); - } - else - { - bot.send(sender, "Visibility: < " + NUMBER_FORMAT.format(result) + " mile(s)", isPrivate); - } + bot.send(sender, + "Visibility: " + (metar.getVisibilityLessThan() ? "< " : "") + NUMBER_FORMAT.format(result) + + " mi, " + metar.getVisibilityInKilometers() + " km", + isPrivate + ); } result = metar.getPressure(); if (result != null) { - bot.send(sender, "Pressure: " + result + " in Hg", isPrivate); + bot.send(sender, + "Pressure: " + result + " Hg, " + metar.getPressureInHectoPascals() + " hPa", + isPrivate); } result = metar.getTemperatureInCelsius(); @@ -155,7 +154,8 @@ public class Weather implements Runnable if (result != null) { bot.send(sender, - "Temperature: " + result + " C, " + metar.getTemperatureInFahrenheit() + " F", isPrivate); + "Temperature: " + result + " \u00B0C, " + metar.getTemperatureInFahrenheit() + " \u00B0F", + isPrivate); } if (metar.getWeatherConditions() != null) @@ -192,6 +192,6 @@ public class Weather implements Runnable } } - bot.helpResponse(sender, Mobibot.WEATHER_CMD); + bot.helpResponse(sender, Commands.WEATHER_CMD); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java new file mode 100644 index 0000000..e22514f --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java @@ -0,0 +1,190 @@ +/* + * @(#)Time.java + * + * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Map; +import java.util.TimeZone; +import java.util.TreeMap; + +/** + * Processes the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD} command. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2014-04-27 + * @since 1.0 + */ +public class WorldTime +{ + /** + * The countries supported by the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. + */ + private static final Map<String, String> COUNTRIES_MAP = new TreeMap<String, String>(); + + /** + * The beats (Internet Time) keyword. + */ + private static final String BEATS_KEYWORD = ".beats"; + + /** + * The date/time format for the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. + */ + private static final SimpleDateFormat TIME_SDF = + new SimpleDateFormat("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"); + + /** + * Creates a new time object. + */ + public WorldTime() + { + // Initialize the countries map + COUNTRIES_MAP.put("AU", "Australia/Sydney"); + COUNTRIES_MAP.put("BE", "Europe/Brussels"); + COUNTRIES_MAP.put("CA", "America/Montreal"); + COUNTRIES_MAP.put("CDT", "America/Chicago"); + COUNTRIES_MAP.put("CET", "CET"); + COUNTRIES_MAP.put("CH", "Europe/Zurich"); + COUNTRIES_MAP.put("CN", "Asia/Shanghai"); + COUNTRIES_MAP.put("CST", "America/Chicago"); + COUNTRIES_MAP.put("CU", "Cuba"); + COUNTRIES_MAP.put("DE", "Europe/Berlin"); + COUNTRIES_MAP.put("DK", "Europe/Copenhagen"); + COUNTRIES_MAP.put("EDT", "America/New_York"); + COUNTRIES_MAP.put("EG", "Africa/Cairo"); + COUNTRIES_MAP.put("ER", "Africa/Asmara"); + COUNTRIES_MAP.put("ES", "Europe/Madrid"); + COUNTRIES_MAP.put("EST", "America/New_York"); + COUNTRIES_MAP.put("FI", "Europe/Helsinki"); + COUNTRIES_MAP.put("FR", "Europe/Paris"); + COUNTRIES_MAP.put("GB", "Europe/London"); + COUNTRIES_MAP.put("GMT", "GMT"); + COUNTRIES_MAP.put("HK", "Asia/Hong_Kong"); + COUNTRIES_MAP.put("HST", "HST"); + COUNTRIES_MAP.put("IE", "Europe/Dublin"); + COUNTRIES_MAP.put("IL", "Asia/Tel_Aviv"); + COUNTRIES_MAP.put("IN", "Asia/Calcutta"); + COUNTRIES_MAP.put("IR", "Asia/Tehran"); + COUNTRIES_MAP.put("IS", "Atlantic/Reykjavik"); + COUNTRIES_MAP.put("IT", "Europe/Rome"); + COUNTRIES_MAP.put("JM", "Jamaica"); + COUNTRIES_MAP.put("JP", "Asia/Tokyo"); + COUNTRIES_MAP.put("LY", "Africa/Tripoli"); + COUNTRIES_MAP.put("MDT", "America/Denver"); + COUNTRIES_MAP.put("MH", "Kwajalein"); + COUNTRIES_MAP.put("MST", "America/Denver"); + COUNTRIES_MAP.put("MX", "America/Mexico_City"); + COUNTRIES_MAP.put("NL", "Europe/Amsterdam"); + COUNTRIES_MAP.put("NO", "Europe/Oslo"); + COUNTRIES_MAP.put("NP", "Asia/Katmandu"); + COUNTRIES_MAP.put("NZ", "Pacific/Auckland"); + COUNTRIES_MAP.put("PDT", "America/Los_Angeles"); + COUNTRIES_MAP.put("PK", "Asia/Karachi"); + COUNTRIES_MAP.put("PL", "Europe/Warsaw"); + COUNTRIES_MAP.put("PST", "America/Los_Angeles"); + COUNTRIES_MAP.put("PT", "Europe/Lisbon"); + COUNTRIES_MAP.put("RU", "Europe/Moscow"); + COUNTRIES_MAP.put("SE", "Europe/Stockholm"); + COUNTRIES_MAP.put("SG", "Asia/Singapore"); + COUNTRIES_MAP.put("SU", "Europe/Moscow"); + COUNTRIES_MAP.put("TH", "Asia/Bangkok"); + COUNTRIES_MAP.put("TM", "Asia/Ashgabat"); + COUNTRIES_MAP.put("TR", "Europe/Istanbul"); + COUNTRIES_MAP.put("TW", "Asia/Taipei"); + COUNTRIES_MAP.put("UK", "Europe/London"); + COUNTRIES_MAP.put("US", "America/New_York"); + COUNTRIES_MAP.put("UTC", "UTC"); + COUNTRIES_MAP.put("VA", "Europe/Vatican"); + COUNTRIES_MAP.put("VN", "Asia/Ho_Chi_Minh"); + COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); + COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); + + for (final String tz : TimeZone.getAvailableIDs()) + { + if (!tz.contains("/") && tz.length() == 3 & !COUNTRIES_MAP.containsKey(tz)) + { + COUNTRIES_MAP.put(tz, tz); + } + } + } + + /** + * Responds with the current time. + * + * @param sender The nick of the person who sent the message. + * @param args The time command arguments. + * @param isPrivate Set to true is the response should be send as a private message. + */ + public final void timeResponse(Mobibot bot, String sender, String args, boolean isPrivate) + { + boolean isInvalidTz = false; + final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); + final String response; + + if (tz != null) + { + if (tz.equals(BEATS_KEYWORD)) + { + response = ("The current Internet Time is: " + Utils.internetTime() + ' ' + BEATS_KEYWORD); + } + else + { + TIME_SDF.setTimeZone(TimeZone.getTimeZone(tz)); + response = TIME_SDF.format(Calendar.getInstance().getTime()) + tz.substring(tz.indexOf('/') + 1) + .replace('_', ' '); + } + } + else + { + isInvalidTz = true; + response = "The supported time zones/countries are: " + COUNTRIES_MAP.keySet().toString(); + } + + if (isPrivate) + { + bot.send(sender, response, true); + } + else + { + if (isInvalidTz) + { + bot.send(sender, response); + } + else + { + bot.send(bot.getChannel(), response); + } + } + } +} \ No newline at end of file From 85ed19f259778f4dd31be1c0d2cc2ea8a354f246 Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Tue, 29 Apr 2014 06:44:21 -0700 Subject: [PATCH 009/842] Messages are now sent when people join, change nick or talk on the channel. Updated licenses. More code cleanup. --- build.gradle | 5 +- licenses/Apache LICENSE.txt | 2 +- licenses/JDOM License.txt | 7 +- licenses/JWeather License.txt | 642 +++++---- licenses/License.txt | 2 +- licenses/OstermillerUtil License.txt | 2 +- licenses/ROME License.txt | 5 +- licenses/Twitter4J LICENSE.txt | 199 ++- licenses/delicious-java License.txt | 2 +- licenses/jsoup License.txt | 2 +- mobibot.iws | 768 ++++++---- .../erik/mobibot/CurrencyConverter.java | 34 +- .../thauvin/erik/mobibot/EntryComment.java | 11 +- .../net/thauvin/erik/mobibot/EntryLink.java | 18 +- .../net/thauvin/erik/mobibot/Mobibot.java | 1257 +++++++++-------- .../net/thauvin/erik/mobibot/SwingWorker.java | 3 +- .../net/thauvin/erik/mobibot/TellMessage.java | 52 +- .../thauvin/erik/mobibot/TellMessagesMgr.java | 15 +- .../java/net/thauvin/erik/mobibot/Utils.java | 81 +- .../net/thauvin/erik/mobibot/Weather.java | 17 +- website/index.html | 5 +- 21 files changed, 1867 insertions(+), 1262 deletions(-) diff --git a/build.gradle b/build.gradle index 2e93f3a..5904064 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,18 @@ apply plugin: 'java' apply plugin: 'idea' +apply plugin: 'application' defaultTasks 'deploy' version = '0.6' def packageName = 'net.thauvin.erik.mobibot' -def mainClassName = packageName + '.Mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames +mainClassName = packageName + '.Mobibot' +[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' + repositories { mavenCentral() } diff --git a/licenses/Apache LICENSE.txt b/licenses/Apache LICENSE.txt index d645695..7a4a3ea 100644 --- a/licenses/Apache LICENSE.txt +++ b/licenses/Apache LICENSE.txt @@ -199,4 +199,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. \ No newline at end of file diff --git a/licenses/JDOM License.txt b/licenses/JDOM License.txt index 81afefe..abf4678 100644 --- a/licenses/JDOM License.txt +++ b/licenses/JDOM License.txt @@ -1,8 +1,6 @@ /*-- - $Id$ - - Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin. + Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -52,5 +50,4 @@ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information on the JDOM Project, please see <http://www.jdom.org/>. - */ - + */ \ No newline at end of file diff --git a/licenses/JWeather License.txt b/licenses/JWeather License.txt index 14db8fc..b1e3f5a 100644 --- a/licenses/JWeather License.txt +++ b/licenses/JWeather License.txt @@ -1,221 +1,397 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. The precise terms and conditions for copying, distribution and -modification follow. +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. - GNU GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". -Activities other than copying, distribution and modification are not + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - a) You must cause the modified files to carry prominent notices + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, +identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of +on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. +entire whole, and thus to each and every part regardless of who wrote +it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or -collective works based on the Program. +collective works based on the Library. -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. - 5. You are not required to accept this License, since you have not + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are +distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying -the Program or works based on it. +the Library or works based on it. - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to +You are not responsible for enforcing compliance by third parties with this License. - - 7. If, as a consequence of a court judgment or allegation of patent + + 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. +refrain entirely from distribution of the Library. -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is +integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that @@ -225,116 +401,104 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in + + 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. NO WARRANTY - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. END OF TERMS AND CONDITIONS - How to Apply These Terms to Your New Programs + How to Apply These Terms to Your New Libraries - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. - <one line to give the program's name and a brief idea of what it does.> + <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Also add information on how to contact you by electronic and paper mail. -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if +school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. - <signature of Ty Coon>, 1 April 1989 + <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. +That's all there is to it! + + diff --git a/licenses/License.txt b/licenses/License.txt index 07d745b..68001fc 100644 --- a/licenses/License.txt +++ b/licenses/License.txt @@ -26,4 +26,4 @@ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/licenses/OstermillerUtil License.txt b/licenses/OstermillerUtil License.txt index 47aec18..122e443 100644 --- a/licenses/OstermillerUtil License.txt +++ b/licenses/OstermillerUtil License.txt @@ -1,6 +1,6 @@ License (http://ostermiller.org/utils/) -OstermillerUtil Java Utilities Copyright (c) 2001-2007 by Stephen Ostermiller +OstermillerUtil Java Utilities Copyright (c) 2001-2011 by Stephen Ostermiller and other contributors The OstermillerUtils library is free software; you can redistribute it and/or diff --git a/licenses/ROME License.txt b/licenses/ROME License.txt index f43cdb1..98d59a8 100644 --- a/licenses/ROME License.txt +++ b/licenses/ROME License.txt @@ -1,4 +1,6 @@ Copyright 2004 Sun Microsystems, Inc. +Copyright 2011 The ROME Team + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10,5 +12,4 @@ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and -limitations under the License. - +limitations under the License. \ No newline at end of file diff --git a/licenses/Twitter4J LICENSE.txt b/licenses/Twitter4J LICENSE.txt index 6c19aab..6b5c2d5 100644 --- a/licenses/Twitter4J LICENSE.txt +++ b/licenses/Twitter4J LICENSE.txt @@ -1,26 +1,179 @@ -Twitter4J includes software from JSON.org to parse JSON response from the Twitter API. You can see the license term at http://www.JSON.org/license.html -Copyright (c) 2007-2010, Yusuke Yamamoto -All rights reserved. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Yusuke Yamamoto nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -THIS SOFTWARE IS PROVIDED BY Yusuke Yamamoto ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL Yusuke Yamamoto BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +Twitter4J SUBCOMPONENTS: + +Twitter4J includes software from JSON.org to parse JSON response from the Twitter API. You can see the license term at http://www.JSON.org/license.html \ No newline at end of file diff --git a/licenses/delicious-java License.txt b/licenses/delicious-java License.txt index 8249563..0ffb557 100644 --- a/licenses/delicious-java License.txt +++ b/licenses/delicious-java License.txt @@ -1,5 +1,5 @@ /** - * Copyright (c) 2004, David A. Czarnecki + * Copyright (c) 2004-2007, David A. Czarnecki * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/licenses/jsoup License.txt b/licenses/jsoup License.txt index fda732e..570a40d 100644 --- a/licenses/jsoup License.txt +++ b/licenses/jsoup License.txt @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2009, 2010, 2011, 2012 Jonathan Hedley <jonathan@hedley.net> +Copyright (c) 2009, 2010, 2011, 2012, 2013 Jonathan Hedley <jonathan@hedley.net> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/mobibot.iws b/mobibot.iws index 591e057..7492a3c 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -33,34 +33,27 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> - <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/Apache LICENSE.txt" afterPath="$PROJECT_DIR$/licenses/Apache LICENSE.txt" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/JDOM License.txt" afterPath="$PROJECT_DIR$/licenses/JDOM License.txt" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/JWeather License.txt" afterPath="$PROJECT_DIR$/licenses/JWeather License.txt" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/License.txt" afterPath="$PROJECT_DIR$/licenses/License.txt" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/OstermillerUtil License.txt" afterPath="$PROJECT_DIR$/licenses/OstermillerUtil License.txt" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/ROME License.txt" afterPath="$PROJECT_DIR$/licenses/ROME License.txt" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/Twitter4J LICENSE.txt" afterPath="$PROJECT_DIR$/licenses/Twitter4J LICENSE.txt" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/build.gradle" afterPath="$PROJECT_DIR$/build.gradle" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/buildnum.properties" afterPath="$PROJECT_DIR$/buildnum.properties" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.ipr" afterPath="$PROJECT_DIR$/mobibot.ipr" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/delicious-java License.txt" afterPath="$PROJECT_DIR$/licenses/delicious-java License.txt" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/website/index.html" afterPath="$PROJECT_DIR$/website/index.html" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/jsoup License.txt" afterPath="$PROJECT_DIR$/licenses/jsoup License.txt" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/properties/mobibot.properties" afterPath="$PROJECT_DIR$/properties/mobibot.properties" /> </list> <ignored path="$USER_HOME$/.griffon/" /> <ignored path="$USER_HOME$/.grails/" /> @@ -163,21 +156,125 @@ </component> <component name="FileEditorManager"> <leaf> - <file leaf-file-name="WorldTime.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <file leaf-file-name="Mobibot.java" pinned="false" current="true" current-in-tab="true"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2125" max-vertical-offset="3315"> - <caret line="125" column="66" selection-start-line="125" selection-start-column="52" selection-end-line="125" selection-end-column="52" /> + <state vertical-scroll-proportion="0.4461153" vertical-offset="45833" max-vertical-offset="47804"> + <caret line="2717" column="92" selection-start-line="2717" selection-start-column="51" selection-end-line="2717" selection-end-column="51" /> + <folding> + <element signature="e#0#67517#0" expanded="true" /> + <element signature="imports" expanded="true" /> + <element signature="e#5390#5398#0" expanded="true" /> + <element signature="e#5536#5549#0" expanded="true" /> + <element signature="e#5705#5716#0" expanded="true" /> + <element signature="e#5935#5943#0" expanded="true" /> + <element signature="e#6044#6052#0" expanded="true" /> + <element signature="e#7793#7826#0" expanded="true" /> + <element signature="e#16444#16445#0" expanded="true" /> + <element signature="e#16462#16463#0" expanded="true" /> + <element signature="e#16579#16580#0" expanded="true" /> + <element signature="e#16602#16603#0" expanded="true" /> + <element signature="e#16714#16715#0" expanded="true" /> + <element signature="e#16735#16736#0" expanded="true" /> + <element signature="e#16850#16851#0" expanded="true" /> + <element signature="e#16879#16880#0" expanded="true" /> + <element signature="e#17786#17787#0" expanded="true" /> + <element signature="e#17811#17812#0" expanded="true" /> + <element signature="e#18643#18644#0" expanded="true" /> + <element signature="e#18709#18710#0" expanded="true" /> + <element signature="e#19073#19106#0" expanded="true" /> + <element signature="e#19145#19146#0" expanded="true" /> + <element signature="e#19175#19176#0" expanded="true" /> + <element signature="e#19291#19292#0" expanded="true" /> + <element signature="e#19311#19312#0" expanded="true" /> + <element signature="e#19412#19413#0" expanded="true" /> + <element signature="e#19479#19480#0" expanded="true" /> + <element signature="e#19605#19606#0" expanded="true" /> + <element signature="e#19640#19641#0" expanded="true" /> + <element signature="e#21615#21616#0" expanded="true" /> + <element signature="e#21640#21641#0" expanded="true" /> + <element signature="e#21775#21776#0" expanded="true" /> + <element signature="e#21812#21813#0" expanded="true" /> + <element signature="e#21984#21985#0" expanded="true" /> + <element signature="e#22011#22012#0" expanded="true" /> + <element signature="e#22121#22122#0" expanded="true" /> + <element signature="e#22144#22145#0" expanded="true" /> + <element signature="e#22258#22259#0" expanded="true" /> + <element signature="e#22279#22280#0" expanded="true" /> + <element signature="e#22934#22935#0" expanded="true" /> + <element signature="e#22953#22954#0" expanded="true" /> + <element signature="e#23063#23064#0" expanded="true" /> + <element signature="e#23086#23087#0" expanded="true" /> + <element signature="e#23211#23212#0" expanded="true" /> + <element signature="e#23244#23245#0" expanded="true" /> + <element signature="e#31567#31575#0" expanded="true" /> + <element signature="e#54561#54562#0" expanded="true" /> + <element signature="e#54592#54593#0" expanded="true" /> + <element signature="e#54895#54896#0" expanded="true" /> + <element signature="e#54935#54936#0" expanded="true" /> + <element signature="e#56718#56719#0" expanded="true" /> + <element signature="e#56766#56767#0" expanded="true" /> + <element signature="e#57622#57623#0" expanded="true" /> + <element signature="e#57694#57695#0" expanded="true" /> + <element signature="e#57812#57813#0" expanded="true" /> + <element signature="e#57833#57834#0" expanded="true" /> + <element signature="e#58406#58407#0" expanded="true" /> + <element signature="e#58441#58442#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="Weather.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="889" max-vertical-offset="2669"> + <caret line="147" column="56" selection-start-line="147" selection-start-column="40" selection-end-line="147" selection-end-column="40" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="Mobibot.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <file leaf-file-name="buildnum.properties" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/buildnum.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="21157" max-vertical-offset="45492"> - <caret line="383" column="23" selection-start-line="383" selection-start-column="23" selection-end-line="383" selection-end-column="23" /> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> + <caret line="3" column="0" selection-start-line="3" selection-start-column="0" selection-end-line="3" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="EntryLink.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="5853" max-vertical-offset="6664"> + <caret line="323" column="42" selection-start-line="323" selection-start-column="42" selection-end-line="323" selection-end-column="42" /> + <folding> + <element signature="e#0#9451#0" expanded="true" /> + <element signature="imports" expanded="true" /> + <element signature="e#2175#2189#0" expanded="true" /> + <element signature="e#2277#2295#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="EntryComment.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="107" max-vertical-offset="1530"> + <caret line="110" column="42" selection-start-line="110" selection-start-column="42" selection-end-line="110" selection-end-column="42" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="TellMessagesMgr.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1071" max-vertical-offset="1972"> + <caret line="136" column="18" selection-start-line="136" selection-start-column="18" selection-end-line="136" selection-end-column="18" /> <folding /> </state> </provider> @@ -186,13 +283,38 @@ <file leaf-file-name="Utils.java" pinned="false" current="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1156" max-vertical-offset="5338"> - <caret line="68" column="34" selection-start-line="68" selection-start-column="34" selection-end-line="68" selection-end-column="34" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> + <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> + <folding> + <element signature="e#0#9144#0" expanded="true" /> + <element signature="imports" expanded="true" /> + </folding> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="TellMessage.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="594" max-vertical-offset="1428"> + <caret line="135" column="29" selection-start-line="135" selection-start-column="29" selection-end-line="135" selection-end-column="29" /> <folding /> </state> </provider> </entry> </file> + <file leaf-file-name="Commands.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="2890" max-vertical-offset="4369"> + <caret line="213" column="36" selection-start-line="213" selection-start-column="31" selection-end-line="213" selection-end-column="43" /> + <folding> + <element signature="e#0#5604#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + </file> <file leaf-file-name="EntriesMgr.java" pinned="false" current="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> <provider selected="true" editor-type-id="text-editor"> @@ -203,69 +325,6 @@ </provider> </entry> </file> - <file leaf-file-name="DeliciousPoster.java" pinned="false" current="true" current-in-tab="true"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.6626984" vertical-offset="556" max-vertical-offset="1989"> - <caret line="112" column="33" selection-start-line="112" selection-start-column="33" selection-end-line="112" selection-end-column="33" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Dice.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="782" max-vertical-offset="1734"> - <caret line="46" column="21" selection-start-line="46" selection-start-column="7" selection-end-line="46" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Lookup.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2414" max-vertical-offset="2839"> - <caret line="142" column="42" selection-start-line="142" selection-start-column="42" selection-end-line="142" selection-end-column="42" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="CurrencyConverter.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2460" max-vertical-offset="3927"> - <caret line="110" column="72" selection-start-line="110" selection-start-column="9" selection-end-line="110" selection-end-column="9" /> - <folding> - <element signature="e#0#6496#0" expanded="true" /> - <element signature="imports" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Commands.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="395" max-vertical-offset="4437"> - <caret line="39" column="50" selection-start-line="39" selection-start-column="50" selection-end-line="39" selection-end-column="50" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Quote.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1819" max-vertical-offset="2023"> - <caret line="107" column="30" selection-start-line="107" selection-start-column="27" selection-end-line="107" selection-end-column="27" /> - <folding /> - </state> - </provider> - </entry> - </file> </leaf> </component> <component name="FindManager"> @@ -304,6 +363,20 @@ </ExternalSystemSettings> </option> </ExternalTaskExecutionInfo> + <ExternalTaskExecutionInfo> + <option name="executorId" value="Run" /> + <option name="settings"> + <ExternalSystemSettings> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="taskNames"> + <list> + <option value="distZip" /> + </list> + </option> + </ExternalSystemSettings> + </option> + </ExternalTaskExecutionInfo> <ExternalTaskExecutionInfo> <option name="executorId" value="Run" /> <option name="settings"> @@ -392,6 +465,11 @@ <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="test" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="distTar" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Releases new version." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> @@ -432,6 +510,11 @@ <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="javadoc" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="distZip" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles a jar archive containing the main classes." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> @@ -447,6 +530,11 @@ <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="buildDependents" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="installApp" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles classes 'main'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> @@ -466,10 +554,20 @@ <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="processResources" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Creates OS specific scripts to run the project as a JVM application." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="startScripts" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="wrapper" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Runs this project as a JVM application" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="run" /> + </ExternalTaskPojo> </list> </value> </entry> @@ -534,6 +632,21 @@ <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> <option name="name" value="deploy" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> + <option name="name" value="distTar" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> + <option name="name" value="distZip" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> + <option name="name" value="installApp" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles a jar archive containing the main classes." /> <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> @@ -559,6 +672,16 @@ <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> <option name="name" value="release" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Runs this project as a JVM application" /> + <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> + <option name="name" value="run" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Creates OS specific scripts to run the project as a JVM application." /> + <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> + <option name="name" value="startScripts" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Runs the unit tests." /> <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> @@ -580,7 +703,7 @@ </option> <option name="modificationStamps"> <map> - <entry key="$PROJECT_DIR$" value="2796410469833" /> + <entry key="$PROJECT_DIR$" value="2796686495306" /> </map> </option> <option name="projectBuildClasspath"> @@ -698,31 +821,31 @@ <option value="$PROJECT_DIR$/build.gradle" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Entries.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/MiscUtils.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Time.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TimeCmd.java" /> <option value="$PROJECT_DIR$/mobibot.properties" /> <option value="$PROJECT_DIR$/deploy/mobibot.properties" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> </list> </option> </component> @@ -1479,46 +1602,6 @@ <sortByType /> </navigator> <panes> - <pane id="Scope"> - <subPane subId="Project Files"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - <subPane subId="All"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="src"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </pane> <pane id="ProjectPane"> <subPane> <PATH> @@ -1669,6 +1752,46 @@ </PATH> </subPane> </pane> + <pane id="Scope"> + <subPane subId="Project Files"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + <subPane subId="All"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="src"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + </pane> <pane id="PackagesPane"> <subPane> <PATH> @@ -1742,7 +1865,7 @@ <property name="vcs_file_view_flatWidth4" value="82" /> <property name="GoToFile.includeJavaFiles" value="false" /> <property name="RunManagerConfig.compileBeforeRunning" value="true" /> - <property name="options.lastSelected" value="reference.settings.project.maven.importing" /> + <property name="options.lastSelected" value="preferences.sourceCode.Java" /> <property name="project.structure.side.proportion" value="0.2" /> <property name="MemberChooser.copyJavadoc" value="false" /> <property name="project.structure.last.edited" value="Modules" /> @@ -1784,8 +1907,8 @@ <recent name="net.thauvin.erik.mobibot" /> </key> <key name="MoveMembersDialog.RECENTS_KEY"> - <recent name="net.thauvin.erik.mobibot.Commands" /> <recent name="net.thauvin.erik.mobibot.Utils" /> + <recent name="net.thauvin.erik.mobibot.Commands" /> <recent name="net.thauvin.erik.mobibot.War" /> <recent name="net.thauvin.erik.mobibot.Time" /> <recent name="net.thauvin.erik.mobibot.EntriesMgr" /> @@ -1806,7 +1929,7 @@ <option name="referencePos" value="0" /> <option name="showLabels" value="true" /> </component> - <component name="RunManager" selected="Gradle.mobibot [compileJava]"> + <component name="RunManager" selected="Gradle.mobibot [distZip]"> <configuration default="false" name="mobibot [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> <option name="externalProjectPath" value="$PROJECT_DIR$" /> @@ -1883,6 +2006,25 @@ <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> <method /> </configuration> + <configuration default="false" name="mobibot [distZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="distZip" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> + <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> + <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> + <method /> + </configuration> <configuration default="true" type="GroovyScriptRunConfiguration" factoryName="Groovy"> <module name="" /> <setting name="path" value="" /> @@ -2185,20 +2327,22 @@ <ConfigurationWrapper RunnerId="Run" /> <method /> </configuration> - <list size="6"> + <list size="7"> <item index="0" class="java.lang.String" itemvalue="Application.Mobibot" /> <item index="1" class="java.lang.String" itemvalue="Application.TwitterOAuth" /> <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [build]" /> <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> <item index="5" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> + <item index="6" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> </list> <recent_temporary> - <list size="4"> - <item index="0" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> - <item index="1" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> - <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> - <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [build]" /> + <list size="5"> + <item index="0" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> + <item index="1" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> + <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> + <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> + <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [build]" /> </list> </recent_temporary> </component> @@ -2322,11 +2466,15 @@ <workItem from="1398667437285" duration="14777000" /> <workItem from="1398725125314" duration="186000" /> <workItem from="1398736740069" duration="354000" /> + <workItem from="1398741101527" duration="153000" /> + <workItem from="1398750996638" duration="2877000" /> + <workItem from="1398764128641" duration="5827000" /> + <workItem from="1398771310468" duration="7179000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="196942000" /> + <option name="totallyTimeSpent" value="212978000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2357,6 +2505,7 @@ <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31728044" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> @@ -2365,18 +2514,17 @@ <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.12660669" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12660669" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.24037763" sideWeight="0.52858573" order="1" side_tool="true" content_ui="tabs" /> + <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.1844473" sideWeight="0.5288007" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.1844473" sideWeight="0.47141424" order="0" side_tool="false" content_ui="combo" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18696883" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> + <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.1844473" sideWeight="0.47119924" order="0" side_tool="false" content_ui="combo" /> + <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18602455" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31852552" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3210576" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> @@ -2400,7 +2548,7 @@ <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3295562" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.3241966" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32389048" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> </layout> <layout-to-restore> @@ -2605,14 +2753,14 @@ </provider> </entry> <entry file="file://$PROJECT_DIR$/README.txt"> - <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> - <state /> - </provider> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="-8.16" vertical-offset="0" max-vertical-offset="391"> <caret line="12" column="4" selection-start-line="12" selection-start-column="4" selection-end-line="12" selection-end-column="4" /> </state> </provider> + <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> + <state /> + </provider> </entry> <entry file="file://K:/Gradle/build.gradle"> <provider selected="true" editor-type-id="text-editor"> @@ -2671,14 +2819,14 @@ </provider> </entry> <entry file="file://$PROJECT_DIR$/licenses/License.txt"> - <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> - <state /> - </provider> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.035789475" vertical-offset="0" max-vertical-offset="950"> <caret line="2" column="23" selection-start-line="2" selection-start-column="23" selection-end-line="2" selection-end-column="23" /> </state> </provider> + <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> + <state /> + </provider> </entry> <entry file="file://$PROJECT_DIR$/website/simple.css"> <provider selected="true" editor-type-id="text-editor"> @@ -2701,13 +2849,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> - <caret line="3" column="0" selection-start-line="3" selection-start-column="0" selection-end-line="3" selection-end-column="0" /> - </state> - </provider> - </entry> <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/User.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.33333334" vertical-offset="984" max-vertical-offset="2448"> @@ -2743,24 +2884,42 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1615"> - <caret line="48" column="13" selection-start-line="48" selection-start-column="1" selection-end-line="48" selection-end-column="1" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1650" max-vertical-offset="2703"> + <caret line="125" column="66" selection-start-line="125" selection-start-column="52" selection-end-line="125" selection-end-column="52" /> + <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1190"> - <caret line="51" column="0" selection-start-line="51" selection-start-column="0" selection-end-line="51" selection-end-column="0" /> + <state vertical-scroll-proportion="0.0" vertical-offset="250" max-vertical-offset="3230"> + <caret line="96" column="0" selection-start-line="96" selection-start-column="0" selection-end-line="96" selection-end-column="0" /> + <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="187" max-vertical-offset="2380"> - <caret line="52" column="40" selection-start-line="52" selection-start-column="37" selection-end-line="52" selection-end-column="37" /> + <state vertical-scroll-proportion="0.0" vertical-offset="879" max-vertical-offset="1989"> + <caret line="106" column="58" selection-start-line="106" selection-start-column="44" selection-end-line="106" selection-end-column="44" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122"> + <caret line="44" column="13" selection-start-line="44" selection-start-column="13" selection-end-line="44" selection-end-column="13" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="221" max-vertical-offset="1564"> + <caret line="54" column="13" selection-start-line="54" selection-start-column="1" selection-end-line="54" selection-end-column="1" /> + <folding /> </state> </provider> </entry> @@ -2768,76 +2927,65 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="607" max-vertical-offset="1632"> <caret line="120" column="27" selection-start-line="120" selection-start-column="27" selection-end-line="120" selection-end-column="27" /> + <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.08052885" vertical-offset="630" max-vertical-offset="1462"> - <caret line="41" column="81" selection-start-line="41" selection-start-column="81" selection-end-line="41" selection-end-column="81" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1391" max-vertical-offset="2091"> + <caret line="142" column="42" selection-start-line="142" selection-start-column="42" selection-end-line="142" selection-end-column="42" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="204" max-vertical-offset="2108"> - <caret line="54" column="13" selection-start-line="54" selection-start-column="1" selection-end-line="54" selection-end-column="1" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="6233" max-vertical-offset="7769"> - <caret line="405" column="6" selection-start-line="405" selection-start-column="6" selection-end-line="405" selection-end-column="6" /> + <state vertical-scroll-proportion="0.020383693" vertical-offset="136" max-vertical-offset="2244"> + <caret line="45" column="129" selection-start-line="45" selection-start-column="26" selection-end-line="45" selection-end-column="26" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1734"> + <state vertical-scroll-proportion="0.0" vertical-offset="153" max-vertical-offset="1734"> <caret line="44" column="0" selection-start-line="44" selection-start-column="0" selection-end-line="44" selection-end-column="0" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="908" max-vertical-offset="2023"> - <caret line="136" column="88" selection-start-line="136" selection-start-column="88" selection-end-line="136" selection-end-column="88" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="17" max-vertical-offset="1343"> - <caret line="47" column="97" selection-start-line="47" selection-start-column="94" selection-end-line="47" selection-end-column="94" /> + <caret line="47" column="94" selection-start-line="47" selection-start-column="94" selection-end-line="47" selection-end-column="94" /> + <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="398" max-vertical-offset="1428"> - <caret line="49" column="30" selection-start-line="49" selection-start-column="30" selection-end-line="49" selection-end-column="30" /> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1360"> + <caret line="41" column="81" selection-start-line="41" selection-start-column="81" selection-end-line="41" selection-end-column="81" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1537" max-vertical-offset="3349"> - <caret line="135" column="39" selection-start-line="135" selection-start-column="36" selection-end-line="135" selection-end-column="36" /> + <state vertical-scroll-proportion="0.75152254" vertical-offset="556" max-vertical-offset="1377"> + <caret line="107" column="30" selection-start-line="107" selection-start-column="27" selection-end-line="107" selection-end-column="27" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2244"> - <caret line="45" column="129" selection-start-line="45" selection-start-column="26" selection-end-line="45" selection-end-column="26" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1097" max-vertical-offset="1785"> + <caret line="104" column="4" selection-start-line="104" selection-start-column="4" selection-end-line="104" selection-end-column="4" /> + <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="743" max-vertical-offset="2703"> - <caret line="125" column="66" selection-start-line="125" selection-start-column="52" selection-end-line="125" selection-end-column="52" /> + <state vertical-scroll-proportion="0.0" vertical-offset="612" max-vertical-offset="2788"> + <caret line="36" column="13" selection-start-line="36" selection-start-column="13" selection-end-line="36" selection-end-column="13" /> <folding /> </state> </provider> @@ -2850,73 +2998,147 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="395" max-vertical-offset="4437"> - <caret line="39" column="50" selection-start-line="39" selection-start-column="50" selection-end-line="39" selection-end-column="50" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122"> - <caret line="46" column="21" selection-start-line="46" selection-start-column="7" selection-end-line="46" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1391" max-vertical-offset="2091"> - <caret line="142" column="42" selection-start-line="142" selection-start-column="42" selection-end-line="142" selection-end-column="42" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1377"> - <caret line="107" column="30" selection-start-line="107" selection-start-column="27" selection-end-line="107" selection-end-column="27" /> - <folding /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="3534" max-vertical-offset="5066"> - <caret line="68" column="34" selection-start-line="68" selection-start-column="34" selection-end-line="68" selection-end-column="34" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2460" max-vertical-offset="3927"> - <caret line="110" column="72" selection-start-line="110" selection-start-column="9" selection-end-line="110" selection-end-column="9" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> + <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> <folding> - <element signature="e#0#6496#0" expanded="true" /> + <element signature="e#0#9144#0" expanded="true" /> <element signature="imports" expanded="true" /> </folding> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="21157" max-vertical-offset="45492"> - <caret line="383" column="23" selection-start-line="383" selection-start-column="23" selection-end-line="383" selection-end-column="23" /> + <state vertical-scroll-proportion="0.0" vertical-offset="594" max-vertical-offset="1428"> + <caret line="135" column="29" selection-start-line="135" selection-start-column="29" selection-end-line="135" selection-end-column="29" /> <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.6626984" vertical-offset="556" max-vertical-offset="1989"> - <caret line="112" column="33" selection-start-line="112" selection-start-column="33" selection-end-line="112" selection-end-column="33" /> + <state vertical-scroll-proportion="0.0" vertical-offset="2890" max-vertical-offset="4369"> + <caret line="213" column="36" selection-start-line="213" selection-start-column="31" selection-end-line="213" selection-end-column="43" /> + <folding> + <element signature="e#0#5604#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1071" max-vertical-offset="1972"> + <caret line="136" column="18" selection-start-line="136" selection-start-column="18" selection-end-line="136" selection-end-column="18" /> <folding /> </state> </provider> </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="4895" max-vertical-offset="7531"> + <caret line="323" column="42" selection-start-line="323" selection-start-column="42" selection-end-line="323" selection-end-column="42" /> + <folding> + <element signature="e#0#9451#0" expanded="true" /> + <element signature="imports" expanded="true" /> + <element signature="e#2175#2189#0" expanded="true" /> + <element signature="e#2277#2295#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="928" max-vertical-offset="1683"> + <caret line="110" column="42" selection-start-line="110" selection-start-column="42" selection-end-line="110" selection-end-column="42" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> + <caret line="3" column="0" selection-start-line="3" selection-start-column="0" selection-end-line="3" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="889" max-vertical-offset="2669"> + <caret line="147" column="56" selection-start-line="147" selection-start-column="40" selection-end-line="147" selection-end-column="40" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.4461153" vertical-offset="45833" max-vertical-offset="47804"> + <caret line="2717" column="92" selection-start-line="2717" selection-start-column="51" selection-end-line="2717" selection-end-column="51" /> + <folding> + <element signature="e#0#67517#0" expanded="true" /> + <element signature="imports" expanded="true" /> + <element signature="e#5390#5398#0" expanded="true" /> + <element signature="e#5536#5549#0" expanded="true" /> + <element signature="e#5705#5716#0" expanded="true" /> + <element signature="e#5935#5943#0" expanded="true" /> + <element signature="e#6044#6052#0" expanded="true" /> + <element signature="e#7793#7826#0" expanded="true" /> + <element signature="e#16444#16445#0" expanded="true" /> + <element signature="e#16462#16463#0" expanded="true" /> + <element signature="e#16579#16580#0" expanded="true" /> + <element signature="e#16602#16603#0" expanded="true" /> + <element signature="e#16714#16715#0" expanded="true" /> + <element signature="e#16735#16736#0" expanded="true" /> + <element signature="e#16850#16851#0" expanded="true" /> + <element signature="e#16879#16880#0" expanded="true" /> + <element signature="e#17786#17787#0" expanded="true" /> + <element signature="e#17811#17812#0" expanded="true" /> + <element signature="e#18643#18644#0" expanded="true" /> + <element signature="e#18709#18710#0" expanded="true" /> + <element signature="e#19073#19106#0" expanded="true" /> + <element signature="e#19145#19146#0" expanded="true" /> + <element signature="e#19175#19176#0" expanded="true" /> + <element signature="e#19291#19292#0" expanded="true" /> + <element signature="e#19311#19312#0" expanded="true" /> + <element signature="e#19412#19413#0" expanded="true" /> + <element signature="e#19479#19480#0" expanded="true" /> + <element signature="e#19605#19606#0" expanded="true" /> + <element signature="e#19640#19641#0" expanded="true" /> + <element signature="e#21615#21616#0" expanded="true" /> + <element signature="e#21640#21641#0" expanded="true" /> + <element signature="e#21775#21776#0" expanded="true" /> + <element signature="e#21812#21813#0" expanded="true" /> + <element signature="e#21984#21985#0" expanded="true" /> + <element signature="e#22011#22012#0" expanded="true" /> + <element signature="e#22121#22122#0" expanded="true" /> + <element signature="e#22144#22145#0" expanded="true" /> + <element signature="e#22258#22259#0" expanded="true" /> + <element signature="e#22279#22280#0" expanded="true" /> + <element signature="e#22934#22935#0" expanded="true" /> + <element signature="e#22953#22954#0" expanded="true" /> + <element signature="e#23063#23064#0" expanded="true" /> + <element signature="e#23086#23087#0" expanded="true" /> + <element signature="e#23211#23212#0" expanded="true" /> + <element signature="e#23244#23245#0" expanded="true" /> + <element signature="e#31567#31575#0" expanded="true" /> + <element signature="e#54561#54562#0" expanded="true" /> + <element signature="e#54592#54593#0" expanded="true" /> + <element signature="e#54895#54896#0" expanded="true" /> + <element signature="e#54935#54936#0" expanded="true" /> + <element signature="e#56718#56719#0" expanded="true" /> + <element signature="e#56766#56767#0" expanded="true" /> + <element signature="e#57622#57623#0" expanded="true" /> + <element signature="e#57694#57695#0" expanded="true" /> + <element signature="e#57812#57813#0" expanded="true" /> + <element signature="e#57833#57834#0" expanded="true" /> + <element signature="e#58406#58407#0" expanded="true" /> + <element signature="e#58441#58442#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> </component> <component name="ideajad"> <property name="annotate" value="false" /> diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 4822fb0..607a007 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -94,23 +94,6 @@ public class CurrencyConverter implements Runnable this.bot = bot; } - /** - * Sets the query. - * - * @param sender The nick of the person who sent the message. - * @param query The currency query. - */ - public void setQuery(String sender, String query) - { - this.query = query; - this.sender = sender; - - if (!pubDate.equals(Utils.today())) - { - EXCHANGE_RATES.clear(); - } - } - // Converts specified currencies. public final void run() { @@ -225,4 +208,21 @@ public class CurrencyConverter implements Runnable } } } + + /** + * Sets the query. + * + * @param sender The nick of the person who sent the message. + * @param query The currency query. + */ + public void setQuery(String sender, String query) + { + this.query = query; + this.sender = sender; + + if (!pubDate.equals(Utils.today())) + { + EXCHANGE_RATES.clear(); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index 4c12789..d7b26d8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -49,8 +49,6 @@ public class EntryComment implements Serializable { /** * The serial version UID. - * - * @noinspection UnusedDeclaration */ static final long serialVersionUID = 6957415292233553224L; @@ -77,9 +75,8 @@ public class EntryComment implements Serializable /** * Creates a new comment. - * - * @noinspection UnusedDeclaration */ + @SuppressWarnings("UnusedDeclaration") protected EntryComment() { ; // Required for serialization. @@ -99,9 +96,8 @@ public class EntryComment implements Serializable * Sets the comment. * * @param comment The actual comment. - * - * @noinspection UnusedDeclaration */ + @SuppressWarnings("UnusedDeclaration") public final void setComment(String comment) { this.comment = comment; @@ -111,9 +107,8 @@ public class EntryComment implements Serializable * Returns the comment's creation date. * * @return The date. - * - * @noinspection UnusedDeclaration */ + @SuppressWarnings("UnusedDeclaration") public final Date getDate() { return date; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 2b737cf..89992d9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -53,8 +53,6 @@ public class EntryLink implements Serializable { /** * The serial version UID. - * - * @noinspection UnusedDeclaration */ static final long serialVersionUID = 3676245542270899086L; @@ -179,16 +177,6 @@ public class EntryLink implements Serializable setTags(tags); } - /** - * Creates a new EntryLink object. - * - * @noinspection UnusedDeclaration - */ - protected EntryLink() - { - ; // Required for serialization. - } - /** * Adds a new comment. * @@ -231,9 +219,8 @@ public class EntryLink implements Serializable * Sets the channel. * * @param channel The channel. - * - * @noinspection UnusedDeclaration */ + @SuppressWarnings("UnusedDeclaration") public final synchronized void setChannel(String channel) { this.channel = channel; @@ -333,9 +320,8 @@ public class EntryLink implements Serializable * Set the comment's author login. * * @param login The new login. - * - * @noinspection UnusedDeclaration */ + @SuppressWarnings("UnusedDeclaration") public final synchronized void setLogin(String login) { this.login = login; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 97405a5..8cdb5d7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -51,6 +51,7 @@ import java.io.*; import java.net.UnknownHostException; import java.text.DecimalFormat; import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; /** * Implements the #mobitopia bot. @@ -66,6 +67,11 @@ public class Mobibot extends PircBot */ public static final int CONNECT_TIMEOUT = 5000; + /** + * The empty title string. + */ + public static final String NO_TITLE = "No Title"; + /** * The serialized object file extension. */ @@ -123,11 +129,6 @@ public class Mobibot extends PircBot */ private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - /** - * The empty title string. - */ - private static final String NO_TITLE = "No Title"; - /** * The tags/categories marker. */ @@ -177,7 +178,7 @@ public class Mobibot extends PircBot /** * The {@link Commands#TELL_CMD} messages queue. */ - private final List<TellMessage> tellMessages = new ArrayList<TellMessage>(0); + private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<TellMessage>(); /** * The main channel. @@ -318,6 +319,7 @@ public class Mobibot extends PircBot * @param channel The channel. * @param logsDir The logs directory. */ + @SuppressWarnings("WeakerAccess") public Mobibot(String server, int port, String nickname, String channel, String logsDir) { System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); @@ -378,8 +380,6 @@ public class Mobibot extends PircBot * The Truth Is Out There... * * @param args The command line arguments. - * - * @noinspection UseOfSystemOutOrSystemErr, ACCESS_STATIC_VIA_INSTANCE, unchecked */ public static void main(String[] args) { @@ -790,155 +790,6 @@ public class Mobibot extends PircBot TellMessagesMgr.cleanTellMessages(tellMessages, tellMaxDays); } - /** - * Builds an entry's comment for display on the channel. - * - * @param entryIndex The entry's index. - * @param commentIndex The comment's index. - * @param comment The {@link EntryComment comment} object. - * - * @return The entry's comment. - */ - private static String buildComment(int entryIndex, int commentIndex, EntryComment comment) - { - return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " - + comment.getComment()); - } - - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link EntryLink entry} object. - * - * @return The entry's link. - * - * @see #buildLink(int, EntryLink, boolean) - */ - private static String buildLink(int index, EntryLink entry) - { - return buildLink(index, entry, false); - } - - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link EntryLink entry} object. - * @param isView Set to true to display the number of comments. - * - * @return The entry's link. - */ - private static String buildLink(int index, EntryLink entry, boolean isView) - { - final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); - - buff.append('[').append(entry.getNick()).append(']'); - - if (isView && entry.hasComments()) - { - buff.append("[+").append(entry.getCommentsCount()).append(']'); - } - - buff.append(' '); - - if (NO_TITLE.equals(entry.getTitle())) - { - buff.append(Utils.bold(entry.getTitle())); - } - else - { - buff.append(entry.getTitle()); - } - - buff.append(" ( ").append(entry.getLink()).append(" )"); - - return buff.toString(); - } - - /** - * Build an entry's tags/categories for diplay on the channel. - * - * @param entryIndex The entry's index. - * @param entry The {@link EntryLink entry} object. - * - * @return The entry's tags. - */ - private static String buildTags(int entryIndex, EntryLink entry) - { - return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); - } - - /** - * Get today's date for the feed. - * - * @return Today's date. - */ - public synchronized String getToday() - { - return today; - } - - /** - * Returns the backlogs URL. - * - * @return The backlogs URL. - */ - public final String getBacklogsUrl() - { - return backLogsUrl; - } - - /** - * Sets the backlogs URL. - * - * @param backLogsUrl The backlogs URL. - */ - private void setBacklogsUrl(String backLogsUrl) - { - this.backLogsUrl = backLogsUrl; - } - - /** - * Returns the weblog URL. - * - * @return The weblog URL. - */ - public final String getWeblogUrl() - { - return weblogUrl; - } - - /** - * Sets the weblog URL. - * - * @param weblogUrl The weblog URL. - */ - private void setWeblogUrl(String weblogUrl) - { - this.weblogUrl = weblogUrl; - } - - /** - * Returns the log directory. - * - * @return the log directory. - */ - public final String getLogsDir() - { - return logsDir; - } - - /** - * Returns the irc server. - * - * @return The irc server. - */ - public final String getIrcServer() - { - return ircServer; - } - /** * Sends an action to the current channel. * @@ -963,6 +814,101 @@ public class Mobibot extends PircBot } } + /** + * Processes the {@link Commands#CALC_CMD} command. + * + * @param sender The nick of the person who sent the message + * @param args The command arguments. + * @param message The actual message. + */ + private void calcResponse(String sender, String args, String message) + { + if (Utils.isValidString(args)) + { + final DecimalFormat decimalFormat = new DecimalFormat("#.##"); + + try + { + final Calculable calc = new ExpressionBuilder(args).build(); + send(getChannel(), args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.calculate())); + } + catch (Exception e) + { + if (logger.isDebugEnabled()) + { + logger.debug("Unable to calculate: " + message, e); + } + + send(getChannel(), "No idea. This is the kind of math I don't get."); + } + } + else + { + helpResponse(sender, Commands.CALC_CMD); + } + } + + /** + * Responds with the title and links from the RSS feed. + * + * @param sender The nick of the person who sent the private message. + */ + private void feedResponse(String sender) + { + if (Utils.isValidString(feedURL)) + { + new Thread(new FeedReader(this, sender, feedURL)).start(); + } + else + { + send(sender, "There is no weblog setup for this channel."); + } + } + + /** + * Returns the index of the specified duplicate entry, if any. + * + * @param link The link. + * + * @return The index or -1 if none. + */ + private int findDupEntry(String link) + { + EntryLink entry; + + for (int i = 0; i < entries.size(); i++) + { + entry = entries.get(i); + + if (link.equals(entry.getLink())) + { + return i; + } + } + + return -1; + } + + /** + * Returns the backlogs URL. + * + * @return The backlogs URL. + */ + public final String getBacklogsUrl() + { + return backLogsUrl; + } + + /** + * Sets the backlogs URL. + * + * @param backLogsUrl The backlogs URL. + */ + private void setBacklogsUrl(String backLogsUrl) + { + this.backLogsUrl = backLogsUrl; + } + /** * Returns the {@link FeedFetcherCache feed info cache}. * @@ -973,6 +919,103 @@ public class Mobibot extends PircBot return feedInfoCache; } + /** + * Returns the irc server. + * + * @return The irc server. + */ + public final String getIrcServer() + { + return ircServer; + } + + /** + * Returns the log directory. + * + * @return the log directory. + */ + public final String getLogsDir() + { + return logsDir; + } + + /** + * Returns the bot's nickname regexp pattern. + * + * @return The nickname regexp pattern. + */ + private String getNickPattern() + { + final StringBuilder buff = new StringBuilder(0); + final String nick = getNick(); + char c; + + for (int i = 0; i < nick.length(); i++) + { + c = nick.charAt(i); + + if (Character.isLetter(c)) + { + buff.append('[').append(String.valueOf(c).toLowerCase()).append(String.valueOf(c).toUpperCase()) + .append(']'); + } + else + { + buff.append(c); + } + } + + return buff.toString(); + } + + /** + * Get today's date for the feed. + * + * @return Today's date. + */ + public synchronized String getToday() + { + return today; + } + + /** + * Returns the weblog URL. + * + * @return The weblog URL. + */ + public final String getWeblogUrl() + { + return weblogUrl; + } + + /** + * Sets the weblog URL. + * + * @param weblogUrl The weblog URL. + */ + private void setWeblogUrl(String weblogUrl) + { + this.weblogUrl = weblogUrl; + } + + /** + * Responds with the Google search results for the specified query. + * + * @param sender The nick of the person who sent the private message. + * @param query The Google query to execute. + */ + private void googleResponse(String sender, String query) + { + if (query.length() > 0) + { + new Thread(new GoogleSearch(this, sender, query)).start(); + } + else + { + helpResponse(sender, Commands.GOOGLE_CMD); + } + } + /** * Responds with the bot's help. * @@ -1155,7 +1198,7 @@ public class Mobibot extends PircBot send(sender, "To view queued and sent messages:"); send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.VIEW_CMD)); - send(sender, "Messages are kept for about " + Utils.bold(tellMaxDays) + " days."); + send(sender, "Messages are kept for around " + Utils.bold(tellMaxDays) + " days."); } else { @@ -1230,14 +1273,210 @@ public class Mobibot extends PircBot } /** - * Sends a private notice. + * Processes the {@link net.thauvin.erik.mobibot.Commands#IGNORE_CMD} command. + * + * @param sender The sender. + * @param args The command arguments. + */ + private void ignoreResponse(String sender, String args) + { + if (!isOp(sender)) + { + final String nick = sender.toLowerCase(); + final boolean isMe = args.toLowerCase().startsWith(Commands.IGNORE_ME_KEYWORD); + + if (ignoredNicks.contains(nick)) + { + if (isMe) + { + ignoredNicks.remove(nick); + + send(sender, "You are no longer ignored."); + } + else + { + send(sender, "You are currently ignored."); + } + } + else + { + if (isMe) + { + ignoredNicks.add(nick); + + send(sender, "You are now ignored."); + } + else + { + send(sender, "You are not currently ignored."); + } + } + } + else + { + if (args.length() > 0) + { + final String[] nicks = args.toLowerCase().split(" "); + + for (String nick : nicks) + { + if (Commands.IGNORE_ME_KEYWORD.equals(nick)) + { + nick = sender.toLowerCase(); + } + + if (ignoredNicks.contains(nick)) + { + ignoredNicks.remove(nick); + } + else + { + ignoredNicks.add(nick); + } + } + } + + send(sender, "The following nicks are ignored: " + ignoredNicks.toString()); + } + } + + /** + * Responds with the bot's information. * * @param sender The nick of the person who sent the message. - * @param message The actual message. + * @param isPrivate Set to true is the response should be send as a private message. */ - public final void send(String sender, String message) + private void infoResponse(String sender, boolean isPrivate) { - send(sender, message, false); + for (final String info : INFO_STRS) + { + send(sender, info, isPrivate); + } + + long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; + + final long days = timeInSeconds / 86400L; + timeInSeconds -= (days * 86400L); + + final long hours = timeInSeconds / 3600L; + timeInSeconds -= (hours * 3600L); + + final long minutes = timeInSeconds / 60L; + send(sender, + "Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size() + + (isTellEnabled() && isOp(sender) ? ", Messages: " + tellMessages.size() : "") + ']', + isPrivate + ); + } + + /** + * Determines whether the specified nick should be ignored. + * + * @param nick The nick. + * + * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. + */ + private boolean isIgnoredNick(String nick) + { + return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); + + } + + /** + * Returns true is the specified sender is an Op on the {@link #channel channel}. + * + * @param sender The sender. + * + * @return true, if the sender is an Op. + */ + private boolean isOp(String sender) + { + final User[] users = getUsers(getChannel()); + + for (final User user : users) + { + if (user.getNick().equals(sender)) + { + return user.isOp(); + } + } + + return false; + } + + /** + * Returns <code>true</code> if twitter posting is enabled. + * + * @return <code>true</code> or <code>false</code> + */ + private boolean isTwitterEnabled() + { + return Utils.isValidString(twitterConsumerKey) && Utils.isValidString(twitterConsumerSecret) && Utils + .isValidString(twitterToken) && Utils.isValidString(twitterTokenSecret); + } + + /** + * Responds with the results of a DNS query. + * + * @param sender The nick of the person who sent the message + * @param query The hostname or IP address. + */ + private void lookupResponse(String sender, String query) + { + if (query.matches("(\\S.)+(\\S)+")) + { + try + { + send(getChannel(), Lookup.lookup(query)); + } + catch (UnknownHostException ignore) + { + if (query.matches( + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) + { + try + { + final String[] lines = Lookup.whois(query); + + if ((lines != null) && (lines.length > 0)) + { + String line; + + for (final String rawLine : lines) + { + line = rawLine.trim(); + + if ((line.length() > 0) && (line.charAt(0) != '#')) + { + send(getChannel(), line); + } + } + } + else + { + send(getChannel(), "Unknown host."); + } + } + catch (IOException ioe) + { + if (logger.isDebugEnabled()) + { + logger.debug("Unable to perform whois IP lookup: " + query, ioe); + } + + send(getChannel(), "Unable to perform whois IP lookup: " + ioe.getMessage()); + } + } + else + { + send(getChannel(), "Unknown host."); + } + } + } + else + { + helpResponse(sender, Commands.LOOKUP_CMD); + } } @Override @@ -1300,16 +1539,6 @@ public class Mobibot extends PircBot joinChannel(getChannel()); } - /** - * Returns the current channel. - * - * @return The current channel. - */ - public final String getChannel() - { - return channel; - } - @Override protected final void onMessage(String channel, String sender, String login, String hostname, String message) { @@ -1320,6 +1549,7 @@ public class Mobibot extends PircBot boolean isCommand = false; + // Capture URLs posted on the channel if (message.matches(LINK_MATCH) && !isIgnoredNick(sender)) { isCommand = true; @@ -1388,7 +1618,7 @@ public class Mobibot extends PircBot final int index = entries.size() - 1; final EntryLink entry = entries.get(index); - send(channel, buildLink(index, entry)); + send(channel, Utils.buildLink(index, entry)); if (delicious != null) { @@ -1408,10 +1638,11 @@ public class Mobibot extends PircBot else { final EntryLink entry = entries.get(dupIndex); - send(sender, "Duplicate >> " + buildLink(dupIndex, entry)); + send(sender, "Duplicate >> " + Utils.buildLink(dupIndex, entry)); } } } + // mobibot: <command> else if (message.matches(getNickPattern() + ":.*")) { isCommand = true; @@ -1426,10 +1657,12 @@ public class Mobibot extends PircBot args = cmds[1].trim(); } + // mobibot: help if (cmd.startsWith(Commands.HELP_CMD)) { helpResponse(sender, args); } + // mobibot: ping else if (cmd.equals(Commands.PING_CMD)) { final String[] pings = { @@ -1451,169 +1684,113 @@ public class Mobibot extends PircBot action(channel, pings[r.nextInt(pings.length)]); } + // mobibot: pong else if (cmd.equals(Commands.PONG_CMD)) { send(channel, Commands.PING_CMD, true); } + // mobibot: recap else if (cmd.equals(Commands.RECAP_CMD)) { recapResponse(sender, false); } + // mobibot: users else if (cmd.equals(Commands.USERS_CMD)) { usersResponse(sender, false); } + // mobibot: info else if (cmd.equals(Commands.INFO_CMD)) { infoResponse(sender, false); } + // mobbiot: version else if (cmd.equals(Commands.VERSION_CMD)) { versionResponse(sender, false); } + // mobibot: dice else if (cmd.equals(Commands.DICE_CMD)) { send(getChannel(), shall_we_play_a_game); Dice.roll(this, sender); } + // mobibot: war else if (cmd.equals(Commands.WAR_CMD)) { send(getChannel(), shall_we_play_a_game); War.play(this, sender); } + // mobibot: <channel> else if (cmd.equalsIgnoreCase(getChannel().substring(1))) { feedResponse(sender); } + // mobibot: currency else if (cmd.startsWith(Commands.CURRENCY_CMD)) { currencyConverter.setQuery(sender, args); new Thread(currencyConverter).start(); } + // mobibot: lookup else if (cmd.startsWith(Commands.LOOKUP_CMD)) { lookupResponse(sender, args); } + // mobibot: view else if (cmd.startsWith(Commands.VIEW_CMD)) { viewResponse(sender, args, false); } + // mobibot: google else if (cmd.startsWith(Commands.GOOGLE_CMD)) { googleResponse(sender, args); } + // mobibot: twitter else if (cmd.startsWith(Commands.TWITTER_CMD) && isTwitterEnabled()) { twitterResponse(sender, args); } + // mobibot: stock else if (cmd.startsWith(Commands.STOCK_CMD)) { stockResponse(sender, args); } + // mobibot: quote else if (cmd.startsWith(Commands.QUOTE_CMD)) { new Thread(new Quote(this, sender)).start(); } + // mobibot: calc else if (cmd.startsWith(Commands.CALC_CMD)) { - if (cmds.length > 1) - { - final DecimalFormat decimalFormat = new DecimalFormat("#.##"); - - try - { - final Calculable calc = new ExpressionBuilder(args).build(); - send(getChannel(), args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.calculate())); - } - catch (Exception e) - { - if (logger.isDebugEnabled()) - { - logger.debug("Unable to calculate: " + message, e); - } - } - } - else - { - helpResponse(sender, Commands.CALC_CMD); - } + calcResponse(sender, args, message); } + // mobibot: time else if (cmd.startsWith(Commands.TIME_CMD)) { worldTime.timeResponse(this, sender, args, false); } + // mobibot: tell else if (cmd.startsWith(Commands.TELL_CMD) && isTellEnabled()) { tellResponse(sender, args); } + // mobibot: weather else if (cmd.startsWith(Commands.WEATHER_CMD)) { weatherResponse(sender, args, false); } + // mobibot: ignore else if (cmd.startsWith(Commands.IGNORE_CMD)) { - if (!isOp(sender)) - { - final String nick = sender.toLowerCase(); - final boolean isMe = args.toLowerCase().startsWith(Commands.IGNORE_ME_KEYWORD); - - if (ignoredNicks.contains(nick)) - { - if (isMe) - { - ignoredNicks.remove(nick); - - send(sender, "You are no longer ignored."); - } - else - { - send(sender, "You are currently ignored."); - } - } - else - { - if (isMe) - { - ignoredNicks.add(nick); - - send(sender, "You are now ignored."); - } - else - { - send(sender, "You are not currently ignored."); - } - } - } - else - { - if (args.length() > 0) - { - final String[] nicks = args.toLowerCase().split(" "); - - for (String nick : nicks) - { - if (Commands.IGNORE_ME_KEYWORD.equals(nick)) - { - nick = sender.toLowerCase(); - } - - if (ignoredNicks.contains(nick)) - { - ignoredNicks.remove(nick); - } - else - { - ignoredNicks.add(nick); - } - } - } - - send(sender, "The following nicks are ignored: " + ignoredNicks.toString()); - } + ignoreResponse(sender, args); } } + // L1:<comment>, L1:-, L1:|<title>, etc. else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { isCommand = true; @@ -1621,6 +1798,7 @@ public class Mobibot extends PircBot final String[] cmds = message.substring(1).split(":", 2); final int index = Integer.parseInt(cmds[0]) - 1; + // L1:<comment> if (index < entries.size()) { final String cmd = cmds[1].trim(); @@ -1628,11 +1806,11 @@ public class Mobibot extends PircBot if (cmd.length() == 0) { final EntryLink entry = entries.get(index); - send(getChannel(), buildLink(index, entry)); + send(getChannel(), Utils.buildLink(index, entry)); if (entry.hasTags()) { - send(getChannel(), buildTags(index, entry)); + send(getChannel(), Utils.buildTags(index, entry)); } if (entry.hasComments()) @@ -1641,12 +1819,13 @@ public class Mobibot extends PircBot for (int i = 0; i < comments.length; i++) { - send(getChannel(), buildComment(index, i, comments[i])); + send(getChannel(), Utils.buildComment(index, i, comments[i])); } } } else { + // L1:- if ("-".equals(cmd)) { final EntryLink entry = entries.get(index); @@ -1667,6 +1846,7 @@ public class Mobibot extends PircBot send(sender, "Please ask a channel op to remove this entry for you."); } } + // L1:|<title> else if (cmd.charAt(0) == '|') { if (cmd.length() > 1) @@ -1679,10 +1859,11 @@ public class Mobibot extends PircBot delicious.updatePost(entry.getLink(), entry); } - send(getChannel(), buildLink(index, entry)); + send(getChannel(), Utils.buildLink(index, entry)); saveEntries(false); } } + // L1:=<url> else if (cmd.charAt(0) == '=') { final EntryLink entry = entries.get(index); @@ -1702,7 +1883,7 @@ public class Mobibot extends PircBot delicious.updatePost(oldLink, entry); } - send(getChannel(), buildLink(index, entry)); + send(getChannel(), Utils.buildLink(index, entry)); saveEntries(false); } } @@ -1711,6 +1892,7 @@ public class Mobibot extends PircBot send(sender, "Please ask a channel op to change this link for you."); } } + // L1:?<author> else if (cmd.charAt(0) == '?') { if (isOp(sender)) @@ -1719,7 +1901,7 @@ public class Mobibot extends PircBot { final EntryLink entry = entries.get(index); entry.setNick(cmd.substring(1)); - send(getChannel(), buildLink(index, entry)); + send(getChannel(), Utils.buildLink(index, entry)); saveEntries(false); } } @@ -1734,12 +1916,13 @@ public class Mobibot extends PircBot final int cindex = entry.addComment(cmd, sender); final EntryComment comment = entry.getComment(cindex); - send(sender, buildComment(index, cindex, comment)); + send(sender, Utils.buildComment(index, cindex, comment)); saveEntries(false); } } } } + // L1T:<+-tag> else if (message.matches(Commands.LINK_CMD + "[0-9]+T:.*")) { isCommand = true; @@ -1764,7 +1947,7 @@ public class Mobibot extends PircBot delicious.updatePost(entry.getLink(), entry); } - send(getChannel(), buildTags(index, entry)); + send(getChannel(), Utils.buildTags(index, entry)); saveEntries(false); } else @@ -1776,7 +1959,7 @@ public class Mobibot extends PircBot { if (entry.hasTags()) { - send(getChannel(), buildTags(index, entry)); + send(getChannel(), Utils.buildTags(index, entry)); } else { @@ -1785,6 +1968,7 @@ public class Mobibot extends PircBot } } } + // L1.1:<comment> else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { isCommand = true; @@ -1801,11 +1985,13 @@ public class Mobibot extends PircBot { final String cmd = cmds[2].trim(); + // L1.1: if (cmd.length() == 0) { final EntryComment comment = entry.getComment(cindex); - send(getChannel(), buildComment(index, cindex, comment)); + send(getChannel(), Utils.buildComment(index, cindex, comment)); } + // L1.1:- else if ("-".equals(cmd)) { entry.deleteComment(cindex); @@ -1813,6 +1999,7 @@ public class Mobibot extends PircBot "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); saveEntries(false); } + // L1.1:?<author> else if (cmd.charAt(0) == '?') { if (isOp(sender)) @@ -1821,7 +2008,7 @@ public class Mobibot extends PircBot { final EntryComment comment = entry.getComment(cindex); comment.setNick(cmd.substring(1)); - send(getChannel(), buildComment(index, cindex, comment)); + send(getChannel(), Utils.buildComment(index, cindex, comment)); saveEntries(false); } } @@ -1835,7 +2022,7 @@ public class Mobibot extends PircBot entry.setComment(cindex, cmd, sender); final EntryComment comment = entry.getComment(cindex); - send(sender, buildComment(index, cindex, comment)); + send(sender, Utils.buildComment(index, cindex, comment)); saveEntries(false); } } @@ -1846,6 +2033,8 @@ public class Mobibot extends PircBot { recap(sender, message, false); } + + tellSendMessages(sender, true); } @Override @@ -2062,53 +2251,68 @@ public class Mobibot extends PircBot * @param nickname The user's nickname. */ private void tellSendMessages(String nickname) + { + tellSendMessages(nickname, false); + } + + /** + * Checks and sends {@link Commands#TELL_CMD} messages. + * + * @param nickname The user's nickname. + * @param isMessage The message flag. + */ + private void tellSendMessages(String nickname, boolean isMessage) { if (!nickname.equals(getNick()) && isTellEnabled()) { - synchronized (tellMessages) + for (final TellMessage message : tellMessages) { - for (final TellMessage message : tellMessages) + if (message.isMatch(nickname)) { - if (message.isMatch(nickname)) + if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { - if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) + if (message.getSender().equals(nickname)) { - if (message.getSender().equals(nickname)) + if (!isMessage) { send(nickname, Utils.bold("You") + " wanted me to remind you: " + Colors.REVERSE + message .getMessage() + Colors.REVERSE, true ); + + message.setIsReceived(); + message.setIsNotified(); + + saveTellMessages(); } - else - { - send(nickname, - message.getSender() + " wanted me to tell you: " + Colors.REVERSE + message - .getMessage() + Colors.REVERSE, - true - ); - } - - message.setReceived(); - - saveTellMessages(); - } - else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message - .isNotified()) + else { send(nickname, - "Your message " + Colors.REVERSE + "[ID " + message.getId() + ']' + Colors.REVERSE - + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF - .format(message.getReceived()), + message.getSender() + " wanted me to tell you: " + Colors.REVERSE + message + .getMessage() + Colors.REVERSE, true ); - message.setNotified(); + message.setIsReceived(); saveTellMessages(); } } + else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message + .isNotified()) + { + send(nickname, + "Your message " + Colors.REVERSE + "[ID " + message.getId() + ']' + Colors.REVERSE + + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF + .format(message.getReceived()), + true + ); + + message.setIsNotified(); + + saveTellMessages(); + } } } } @@ -2119,6 +2323,7 @@ public class Mobibot extends PircBot * * @return <code>true</code> or <code>false</code> */ + private boolean isTellEnabled() { return tellMaxSize > 0 && tellMaxDays > 0; @@ -2164,6 +2369,59 @@ public class Mobibot extends PircBot TellMessagesMgr.save(getSerializedObject(), tellMessages, logger); } + /** + * Returns the current channel. + * + * @return The current channel. + */ + public final String getChannel() + { + return channel; + } + + /** + * Responds with the last 10 public messages. + * + * @param sender The nick of the person who sent the private message. + * @param isPrivate Set to true is the response should be send as a private message. + */ + private void recapResponse(String sender, boolean isPrivate) + { + for (final String recap : this.recap) + { + send(sender, recap, isPrivate); + } + } + + /** + * Sends a private notice. + * + * @param sender The nick of the person who sent the message. + * @param message The actual message. + */ + public final void send(String sender, String message) + { + send(sender, message, false); + } + + /** + * Responds with the specified stock quote. + * + * @param sender The nick of the person who sent the message. + * @param symbol The stock symbol to lookup. + */ + private void stockResponse(String sender, String symbol) + { + if (symbol.length() > 0) + { + new Thread(new StockQuote(this, sender, symbol)).start(); + } + else + { + helpResponse(sender, Commands.STOCK_CMD); + } + } + /** * Processes the {@link Commands#TELL_CMD} commands. * @@ -2182,17 +2440,13 @@ public class Mobibot extends PircBot { if (tellMessages.size() > 0) { - synchronized (tellMessages) + for (final TellMessage message : tellMessages) { - for (final TellMessage message : tellMessages) - { - send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") - + ']', - true - ); - } + send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [ID: " + + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', + true + ); } } else @@ -2204,40 +2458,36 @@ public class Mobibot extends PircBot { boolean hasMessage = false; - synchronized (tellMessages) + for (final TellMessage message : tellMessages) { - for (final TellMessage message : tellMessages) + if (message.isMatch(sender)) { - if (message.isMatch(sender)) + if (!hasMessage) { - if (!hasMessage) - { - hasMessage = true; - send(sender, "Here are your messages: ", true); - } - - if (message.isReceived()) - { - send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId() - + ", DELIVERED]", - true - ); - - } - else - { - send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() - + ", QUEUED]", - true - ); - } - - send(sender, DOUBLE_INDENT + message.getMessage(), true); + hasMessage = true; + send(sender, "Here are your messages: ", true); } + + if (message.isReceived()) + { + send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [" + + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId() + + ", DELIVERED]", + true + ); + + } + else + { + send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [" + + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() + ", QUEUED]", + true + ); + } + + send(sender, DOUBLE_INDENT + message.getMessage(), true); } } @@ -2247,12 +2497,13 @@ public class Mobibot extends PircBot } else { - send(sender, "To delete a message that you sent:"); + send(sender, "To delete one or all delivered messages:"); send(sender, DOUBLE_INDENT + Utils - .bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id>") + .bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id|" + + Commands.TELL_ALL_CMD + '>') ); - send(sender, "Messages are kept for about " + Utils.bold(tellMaxDays) + " days."); + send(sender, "Messages are kept for around " + Utils.bold(tellMaxDays) + " days."); } } } @@ -2264,10 +2515,33 @@ public class Mobibot extends PircBot { final String id = split[1]; boolean deleted = false; - boolean found = false; - synchronized (tellMessages) + if (id.equalsIgnoreCase(Commands.TELL_ALL_CMD)) { + for (final TellMessage message : tellMessages) + { + if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) + { + tellMessages.remove(message); + deleted = true; + } + } + + if (deleted) + { + saveTellMessages(); + send(sender, "Delivered messages have been deleted.", true); + } + else + { + send(sender, "No delivered messages were found.", true); + } + + } + else + { + boolean found = false; + for (final TellMessage message : tellMessages) { found = message.isMatchId(id); @@ -2282,17 +2556,17 @@ public class Mobibot extends PircBot break; } } - } - if (!deleted) - { - if (found) + if (!deleted) { - send(sender, "Only messages that you sent can be deleted.", true); - } - else - { - send(sender, "The specified message [ID " + id + "] could not be found.", true); + if (found) + { + send(sender, "Only messages that you sent can be deleted.", true); + } + else + { + send(sender, "The specified message [ID " + id + "] could not be found.", true); + } } } } @@ -2333,105 +2607,6 @@ public class Mobibot extends PircBot cleanTellMessages(); } - /** - * Responds with the title and links from the RSS feed. - * - * @param sender The nick of the person who sent the private message. - */ - private void feedResponse(String sender) - { - if (Utils.isValidString(feedURL)) - { - new Thread(new FeedReader(this, sender, feedURL)).start(); - } - else - { - send(sender, "There is no weblog setup for this channel."); - } - } - - /** - * Returns the index of the specified duplicate entry, if any. - * - * @param link The link. - * - * @return The index or -1 if none. - */ - private int findDupEntry(String link) - { - EntryLink entry; - - for (int i = 0; i < entries.size(); i++) - { - entry = entries.get(i); - - if (link.equals(entry.getLink())) - { - return i; - } - } - - return -1; - } - - /** - * Returns the bot's nickname regexp pattern. - * - * @return The nickname regexp pattern. - */ - private String getNickPattern() - { - final StringBuilder buff = new StringBuilder(0); - final String nick = getNick(); - char c; - - for (int i = 0; i < nick.length(); i++) - { - c = nick.charAt(i); - - if (Character.isLetter(c)) - { - buff.append('[').append(String.valueOf(c).toLowerCase()).append(String.valueOf(c).toUpperCase()) - .append(']'); - } - else - { - buff.append(c); - } - } - - return buff.toString(); - } - - /** - * Responds with the Google search results for the specified query. - * - * @param sender The nick of the person who sent the private message. - * @param query The Google query to execute. - */ - private void googleResponse(String sender, String query) - { - if (query.length() > 0) - { - new Thread(new GoogleSearch(this, sender, query)).start(); - } - else - { - helpResponse(sender, Commands.GOOGLE_CMD); - } - } - - /** - * Returns <code>true</code> if twitter posting is enabled. - * - * @return <code>true</code> or <code>false</code> - */ - private boolean isTwitterEnabled() - { - return Utils.isValidString(twitterConsumerKey) && Utils.isValidString(twitterConsumerSecret) && Utils - .isValidString(twitterToken) && Utils.isValidString(twitterTokenSecret); - } - /** * Posts a message to Twitter. * @@ -2463,183 +2638,6 @@ public class Mobibot extends PircBot } } - /** - * Responds with the bot's information. - * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to true is the response should be send as a private message. - */ - private void infoResponse(String sender, boolean isPrivate) - { - for (final String info : INFO_STRS) - { - send(sender, info, isPrivate); - } - - long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; - - final long days = timeInSeconds / 86400L; - timeInSeconds -= (days * 86400L); - - final long hours = timeInSeconds / 3600L; - timeInSeconds -= (hours * 3600L); - - final long minutes = timeInSeconds / 60L; - send(sender, - "Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size() - + (isTellEnabled() && isOp(sender) ? ", Messages: " + tellMessages.size() : "") + ']', - isPrivate - ); - } - - /** - * Responds with the bot's version info. - * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to true is the response should be send as a private message. - */ - private void versionResponse(String sender, boolean isPrivate) - { - if (isOp(sender)) - { - for (final String version : VERSION_STRS) - { - send(sender, version, isPrivate); - } - } - } - - /** - * Determines whether the specified nick should be ignored. - * - * @param nick The nick. - * - * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. - */ - private boolean isIgnoredNick(String nick) - { - return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); - - } - - /** - * Returns true is the specified sender is an Op on the {@link #channel channel}. - * - * @param sender The sender. - * - * @return true, if the sender is an Op. - */ - private boolean isOp(String sender) - { - final User[] users = getUsers(getChannel()); - - for (final User user : users) - { - if (user.getNick().equals(sender)) - { - return user.isOp(); - } - } - - return false; - } - - /** - * Responds with the results of a DNS query. - * - * @param sender The nick of the person who sent the message - * @param query The hostname or IP address. - */ - private void lookupResponse(String sender, String query) - { - if (query.matches("(\\S.)+(\\S)+")) - { - try - { - send(getChannel(), Lookup.lookup(query)); - } - catch (UnknownHostException ignore) - { - if (query.matches( - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) - { - try - { - final String[] lines = Lookup.whois(query); - - if ((lines != null) && (lines.length > 0)) - { - String line; - - for (final String rawLine : lines) - { - line = rawLine.trim(); - - if ((line.length() > 0) && (line.charAt(0) != '#')) - { - send(getChannel(), line); - } - } - } - else - { - send(getChannel(), "Unknown host."); - } - } - catch (IOException ioe) - { - if (logger.isDebugEnabled()) - { - logger.debug("Unable to perform whois IP lookup: " + query, ioe); - } - - send(getChannel(), "Unable to perform whois IP lookup: " + ioe.getMessage()); - } - } - else - { - send(getChannel(), "Unknown host."); - } - } - } - else - { - helpResponse(sender, Commands.LOOKUP_CMD); - } - } - - /** - * Responds with the last 10 public messages. - * - * @param sender The nick of the person who sent the private message. - * @param isPrivate Set to true is the response should be send as a private message. - */ - private void recapResponse(String sender, boolean isPrivate) - { - for (final String recap : this.recap) - { - send(sender, recap, isPrivate); - } - } - - /** - * Responds with the specified stock quote. - * - * @param sender The nick of the person who sent the message. - * @param symbol The stock symbol to lookup. - */ - private void stockResponse(String sender, String symbol) - { - if (symbol.length() > 0) - { - new Thread(new StockQuote(this, sender, symbol)).start(); - } - else - { - helpResponse(sender, Commands.STOCK_CMD); - } - } - /** * Responds with the users on a channel. * @@ -2673,6 +2671,23 @@ public class Mobibot extends PircBot send(sender, buff.toString(), isPrivate); } + /** + * Responds with the bot's version info. + * + * @param sender The nick of the person who sent the message. + * @param isPrivate Set to true is the response should be send as a private message. + */ + private void versionResponse(String sender, boolean isPrivate) + { + if (isOp(sender)) + { + for (final String version : VERSION_STRS) + { + send(sender, version, isPrivate); + } + } + } + /** * Responds with the stored links. * @@ -2751,7 +2766,7 @@ public class Mobibot extends PircBot break; } - send(sender, buildLink(i, entry, true), isPrivate); + send(sender, Utils.buildLink(i, entry, true), isPrivate); sent++; } } @@ -2766,7 +2781,7 @@ public class Mobibot extends PircBot break; } - send(sender, buildLink(i, entry, true), isPrivate); + send(sender, Utils.buildLink(i, entry, true), isPrivate); sent++; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java index 2ff6379..53307b9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java +++ b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java @@ -10,9 +10,8 @@ import javax.swing.*; * <p/> * Note that the API changed slightly in the 3rd version: You must now invoke start() on the SwingWorker after creating * it. - * - * @noinspection ALL */ +@SuppressWarnings("ALL") public abstract class SwingWorker { private Object value; // see getValue(), setValue() diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 1872208..030b825 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -83,14 +83,9 @@ public class TellMessage implements Serializable } - public String getSender() + public String getId() { - return sender; - } - - public String getRecipient() - { - return recipient; + return this.id; } public String getMessage() @@ -108,30 +103,19 @@ public class TellMessage implements Serializable return received; } - public void setReceived() + public String getRecipient() { - this.received = Calendar.getInstance().getTime(); - this.isReceived = true; + return recipient; } - public boolean isNotified() + public String getSender() { - return this.isNotified; + return sender; } - public void setNotified() + public boolean isMatch(String nick) { - this.isNotified = true; - } - - public String getId() - { - return this.id; - } - - public boolean isReceived() - { - return this.isReceived; + return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); } public boolean isMatchId(String id) @@ -139,8 +123,24 @@ public class TellMessage implements Serializable return this.id.equals(id); } - public boolean isMatch(String nick) + public boolean isNotified() { - return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); + return this.isNotified; + } + + public boolean isReceived() + { + return this.isReceived; + } + + public void setIsNotified() + { + this.isNotified = true; + } + + public void setIsReceived() + { + this.received = Calendar.getInstance().getTime(); + this.isReceived = true; } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index c54f636..b0b9d43 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -139,17 +139,14 @@ public class TellMessagesMgr final Calendar maxDate = Calendar.getInstance(); final Date today = new Date(); - synchronized (tellMessages) + for (final TellMessage message : tellMessages) { - for (final TellMessage message : tellMessages) - { - maxDate.setTime(message.getQueued()); - maxDate.add(Calendar.DATE, tellMaxDays); + maxDate.setTime(message.getQueued()); + maxDate.add(Calendar.DATE, tellMaxDays); - if (maxDate.getTime().before(today)) - { - tellMessages.remove(message); - } + if (maxDate.getTime().before(today)) + { + tellMessages.remove(message); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 3ff10ab..8e65826 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -105,8 +105,8 @@ public class Utils * @param out The destination file. * * @throws java.io.IOException If the file could not be copied. - * @noinspection UnusedDeclaration */ + @SuppressWarnings("UnusedDeclaration") public static void copyFile(File in, File out) throws IOException { @@ -295,6 +295,72 @@ public class Utils return ISO_SDF.format(Calendar.getInstance().getTime()); } + /** + * Builds an entry's comment for display on the channel. + * + * @param entryIndex The entry's index. + * @param commentIndex The comment's index. + * @param comment The {@link net.thauvin.erik.mobibot.EntryComment comment} object. + * + * @return The entry's comment. + */ + static String buildComment(int entryIndex, int commentIndex, EntryComment comment) + { + return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " + + comment.getComment()); + } + + /** + * Builds an entry's link for display on the channel. + * + * @param index The entry's index. + * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. + * + * @return The entry's link. + * + * @see #buildLink(int, net.thauvin.erik.mobibot.EntryLink, boolean) + */ + static String buildLink(int index, EntryLink entry) + { + return buildLink(index, entry, false); + } + + /** + * Builds an entry's link for display on the channel. + * + * @param index The entry's index. + * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. + * @param isView Set to true to display the number of comments. + * + * @return The entry's link. + */ + static String buildLink(int index, EntryLink entry, boolean isView) + { + final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); + + buff.append('[').append(entry.getNick()).append(']'); + + if (isView && entry.hasComments()) + { + buff.append("[+").append(entry.getCommentsCount()).append(']'); + } + + buff.append(' '); + + if (Mobibot.NO_TITLE.equals(entry.getTitle())) + { + buff.append(bold(entry.getTitle())); + } + else + { + buff.append(entry.getTitle()); + } + + buff.append(" ( ").append(entry.getLink()).append(" )"); + + return buff.toString(); + } + /** * Makes the given string bold. * @@ -306,4 +372,17 @@ public class Utils { return Colors.BOLD + s + Colors.BOLD; } + + /** + * Build an entry's tags/categories for display on the channel. + * + * @param entryIndex The entry's index. + * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. + * + * @return The entry's tags. + */ + static String buildTags(int entryIndex, EntryLink entry) + { + return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/Weather.java index 8f8442d..af9eab7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/Weather.java @@ -40,7 +40,6 @@ import net.sf.jweather.metar.WeatherCondition; import java.text.DecimalFormat; import java.util.Date; -import java.util.Iterator; /** * Fetches the weather data from a specific station ID. @@ -160,25 +159,17 @@ public class Weather implements Runnable if (metar.getWeatherConditions() != null) { - final Iterator it = metar.getWeatherConditions().iterator(); - - //noinspection WhileLoopReplaceableByForEach - while (it.hasNext()) + for (final Object weatherCondition : metar.getWeatherConditions()) { - final WeatherCondition weatherCondition = (WeatherCondition) it.next(); - bot.send(sender, weatherCondition.getNaturalLanguageString(), isPrivate); + bot.send(sender, ((WeatherCondition) weatherCondition).getNaturalLanguageString(), isPrivate); } } if (metar.getSkyConditions() != null) { - final Iterator it = metar.getSkyConditions().iterator(); - - //noinspection WhileLoopReplaceableByForEach - while (it.hasNext()) + for (final Object skyCondition : metar.getSkyConditions()) { - final SkyCondition skyCondition = (SkyCondition) it.next(); - bot.send(sender, skyCondition.getNaturalLanguageString(), isPrivate); + bot.send(sender, ((SkyCondition) skyCondition).getNaturalLanguageString(), isPrivate); } } diff --git a/website/index.html b/website/index.html index d8e459b..fff8736 100644 --- a/website/index.html +++ b/website/index.html @@ -42,7 +42,7 @@ <li><a href="http://hc.apache.org/httpclient-3.x/">Commons HTTPClient</a></li> <li><a href="http://commons.apache.org/proper/commons-logging/">Commons Logging</a></li> <li><a href="http://commons.apache.org/proper/commons-net/">Commons Net</a></li> - <li><a href="http://sourceforge.net/projects/delicious-java/">delicious-java</a></li> + <li><a href="https://github.com/czarneckid/delicious-java">delicious-java</a></li> <li><a href="http://sourceforge.net/projects/jweather/">JWeather</a></li> <li><a href="http://www.objecthunter.net/exp4j/">exp4j</a></li> <li><a href="http://ostermiller.org/utils/">OstermillerUtils</a></li> @@ -63,6 +63,9 @@ <p>Other features include:</p> <ul> <li>Displaying the latest entries on Mobitopia</li> + <li>Sending messages on join + <div><code>mobibot: tell Nickname Welcome back!</code></div> + </li> <li>Performing calculations <div><code>mobibot: calc (floor(sqrt(3))+3.14)*3^2</code></div> </li> From b720927061f6fe1636ba5959e14fa67104c4943e Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Wed, 30 Apr 2014 02:36:59 -0700 Subject: [PATCH 010/842] Added user-agent to jsoup posted link title query. More code & comments cleanup. --- build.gradle | 4 + mobibot.iws | 862 +++++++----------- .../erik/mobibot/CurrencyConverter.java | 12 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 2 +- .../java/net/thauvin/erik/mobibot/Dice.java | 6 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 3 + .../net/thauvin/erik/mobibot/EntryLink.java | 52 +- .../net/thauvin/erik/mobibot/FeedReader.java | 4 +- .../thauvin/erik/mobibot/GoogleSearch.java | 8 +- .../java/net/thauvin/erik/mobibot/Lookup.java | 2 +- .../net/thauvin/erik/mobibot/Mobibot.java | 54 +- .../java/net/thauvin/erik/mobibot/Quote.java | 10 +- .../net/thauvin/erik/mobibot/StockQuote.java | 8 +- .../net/thauvin/erik/mobibot/SwingWorker.java | 10 +- .../net/thauvin/erik/mobibot/TellMessage.java | 60 ++ .../thauvin/erik/mobibot/TellMessagesMgr.java | 14 +- .../net/thauvin/erik/mobibot/Twitter.java | 9 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 9 +- .../java/net/thauvin/erik/mobibot/War.java | 4 +- .../net/thauvin/erik/mobibot/Weather.java | 8 +- .../net/thauvin/erik/mobibot/WorldTime.java | 7 +- 21 files changed, 544 insertions(+), 604 deletions(-) diff --git a/build.gradle b/build.gradle index 5904064..587ce85 100644 --- a/build.gradle +++ b/build.gradle @@ -67,6 +67,10 @@ compileJava { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } +javadoc { + options.tags = ['created'] +} + jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) diff --git a/mobibot.iws b/mobibot.iws index 7492a3c..abcd1f6 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -33,26 +33,26 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/Apache LICENSE.txt" afterPath="$PROJECT_DIR$/licenses/Apache LICENSE.txt" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/JDOM License.txt" afterPath="$PROJECT_DIR$/licenses/JDOM License.txt" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/JWeather License.txt" afterPath="$PROJECT_DIR$/licenses/JWeather License.txt" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/License.txt" afterPath="$PROJECT_DIR$/licenses/License.txt" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/OstermillerUtil License.txt" afterPath="$PROJECT_DIR$/licenses/OstermillerUtil License.txt" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/ROME License.txt" afterPath="$PROJECT_DIR$/licenses/ROME License.txt" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/Twitter4J LICENSE.txt" afterPath="$PROJECT_DIR$/licenses/Twitter4J LICENSE.txt" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/build.gradle" afterPath="$PROJECT_DIR$/build.gradle" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/delicious-java License.txt" afterPath="$PROJECT_DIR$/licenses/delicious-java License.txt" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/website/index.html" afterPath="$PROJECT_DIR$/website/index.html" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/licenses/jsoup License.txt" afterPath="$PROJECT_DIR$/licenses/jsoup License.txt" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> </list> <ignored path="$USER_HOME$/.griffon/" /> @@ -124,8 +124,8 @@ <option name="LOG_MESSAGE" value="" /> </breakpoint> </breakpoint_any> - <breakpoint_rules converted="true" /> <ui_properties converted="true" /> + <breakpoint_rules converted="true" /> </component> <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> <component name="FavoritesManager"> @@ -159,112 +159,31 @@ <file leaf-file-name="Mobibot.java" pinned="false" current="true" current-in-tab="true"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.4461153" vertical-offset="45833" max-vertical-offset="47804"> - <caret line="2717" column="92" selection-start-line="2717" selection-start-column="51" selection-end-line="2717" selection-end-column="51" /> + <state vertical-scroll-proportion="0.88023955" vertical-offset="26215" max-vertical-offset="46869"> + <caret line="1619" column="71" selection-start-line="1619" selection-start-column="63" selection-end-line="1619" selection-end-column="63" /> <folding> - <element signature="e#0#67517#0" expanded="true" /> + <element signature="e#0#67752#0" expanded="true" /> <element signature="imports" expanded="true" /> - <element signature="e#5390#5398#0" expanded="true" /> - <element signature="e#5536#5549#0" expanded="true" /> - <element signature="e#5705#5716#0" expanded="true" /> - <element signature="e#5935#5943#0" expanded="true" /> - <element signature="e#6044#6052#0" expanded="true" /> - <element signature="e#7793#7826#0" expanded="true" /> - <element signature="e#16444#16445#0" expanded="true" /> - <element signature="e#16462#16463#0" expanded="true" /> - <element signature="e#16579#16580#0" expanded="true" /> - <element signature="e#16602#16603#0" expanded="true" /> - <element signature="e#16714#16715#0" expanded="true" /> - <element signature="e#16735#16736#0" expanded="true" /> - <element signature="e#16850#16851#0" expanded="true" /> - <element signature="e#16879#16880#0" expanded="true" /> - <element signature="e#17786#17787#0" expanded="true" /> - <element signature="e#17811#17812#0" expanded="true" /> - <element signature="e#18643#18644#0" expanded="true" /> - <element signature="e#18709#18710#0" expanded="true" /> - <element signature="e#19073#19106#0" expanded="true" /> - <element signature="e#19145#19146#0" expanded="true" /> - <element signature="e#19175#19176#0" expanded="true" /> - <element signature="e#19291#19292#0" expanded="true" /> - <element signature="e#19311#19312#0" expanded="true" /> - <element signature="e#19412#19413#0" expanded="true" /> - <element signature="e#19479#19480#0" expanded="true" /> - <element signature="e#19605#19606#0" expanded="true" /> - <element signature="e#19640#19641#0" expanded="true" /> - <element signature="e#21615#21616#0" expanded="true" /> - <element signature="e#21640#21641#0" expanded="true" /> - <element signature="e#21775#21776#0" expanded="true" /> - <element signature="e#21812#21813#0" expanded="true" /> - <element signature="e#21984#21985#0" expanded="true" /> - <element signature="e#22011#22012#0" expanded="true" /> - <element signature="e#22121#22122#0" expanded="true" /> - <element signature="e#22144#22145#0" expanded="true" /> - <element signature="e#22258#22259#0" expanded="true" /> - <element signature="e#22279#22280#0" expanded="true" /> - <element signature="e#22934#22935#0" expanded="true" /> - <element signature="e#22953#22954#0" expanded="true" /> - <element signature="e#23063#23064#0" expanded="true" /> - <element signature="e#23086#23087#0" expanded="true" /> - <element signature="e#23211#23212#0" expanded="true" /> - <element signature="e#23244#23245#0" expanded="true" /> - <element signature="e#31567#31575#0" expanded="true" /> - <element signature="e#54561#54562#0" expanded="true" /> - <element signature="e#54592#54593#0" expanded="true" /> - <element signature="e#54895#54896#0" expanded="true" /> - <element signature="e#54935#54936#0" expanded="true" /> - <element signature="e#56718#56719#0" expanded="true" /> - <element signature="e#56766#56767#0" expanded="true" /> - <element signature="e#57622#57623#0" expanded="true" /> - <element signature="e#57694#57695#0" expanded="true" /> - <element signature="e#57812#57813#0" expanded="true" /> - <element signature="e#57833#57834#0" expanded="true" /> - <element signature="e#58406#58407#0" expanded="true" /> - <element signature="e#58441#58442#0" expanded="true" /> </folding> </state> </provider> </entry> </file> - <file leaf-file-name="Weather.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> + <file leaf-file-name="build.gradle" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/build.gradle"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="889" max-vertical-offset="2669"> - <caret line="147" column="56" selection-start-line="147" selection-start-column="40" selection-end-line="147" selection-end-column="40" /> + <state vertical-scroll-proportion="0.0" vertical-offset="306" max-vertical-offset="2057"> + <caret line="47" column="81" selection-start-line="47" selection-start-column="1" selection-end-line="47" selection-end-column="1" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="buildnum.properties" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <file leaf-file-name="TellMessage.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> - <caret line="3" column="0" selection-start-line="3" selection-start-column="0" selection-end-line="3" selection-end-column="0" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="EntryLink.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="5853" max-vertical-offset="6664"> - <caret line="323" column="42" selection-start-line="323" selection-start-column="42" selection-end-line="323" selection-end-column="42" /> - <folding> - <element signature="e#0#9451#0" expanded="true" /> - <element signature="imports" expanded="true" /> - <element signature="e#2175#2189#0" expanded="true" /> - <element signature="e#2277#2295#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="EntryComment.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="107" max-vertical-offset="1530"> - <caret line="110" column="42" selection-start-line="110" selection-start-column="42" selection-end-line="110" selection-end-column="42" /> + <state vertical-scroll-proportion="0.0" vertical-offset="425" max-vertical-offset="3587"> + <caret line="25" column="78" selection-start-line="25" selection-start-column="72" selection-end-line="25" selection-end-column="72" /> <folding /> </state> </provider> @@ -273,44 +192,39 @@ <file leaf-file-name="TellMessagesMgr.java" pinned="false" current="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1071" max-vertical-offset="1972"> - <caret line="136" column="18" selection-start-line="136" selection-start-column="18" selection-end-line="136" selection-end-column="18" /> + <state vertical-scroll-proportion="0.0" vertical-offset="2414" max-vertical-offset="2890"> + <caret line="142" column="42" selection-start-line="142" selection-start-column="7" selection-end-line="142" selection-end-column="7" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="Utils.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> + <file leaf-file-name="DeliciousPoster.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> - <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> - <folding> - <element signature="e#0#9144#0" expanded="true" /> - <element signature="imports" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="TellMessage.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="594" max-vertical-offset="1428"> - <caret line="135" column="29" selection-start-line="135" selection-start-column="29" selection-end-line="135" selection-end-column="29" /> + <state vertical-scroll-proportion="0.0" vertical-offset="901" max-vertical-offset="2771"> + <caret line="53" column="7" selection-start-line="53" selection-start-column="7" selection-end-line="53" selection-end-column="7" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="Commands.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> + <file leaf-file-name="Dice.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2890" max-vertical-offset="4369"> - <caret line="213" column="36" selection-start-line="213" selection-start-column="31" selection-end-line="213" selection-end-column="43" /> - <folding> - <element signature="e#0#5604#0" expanded="true" /> - </folding> + <state vertical-scroll-proportion="0.0" vertical-offset="646" max-vertical-offset="1734"> + <caret line="38" column="51" selection-start-line="38" selection-start-column="51" selection-end-line="38" selection-end-column="51" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="Weather.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1666" max-vertical-offset="3298"> + <caret line="98" column="59" selection-start-line="98" selection-start-column="7" selection-end-line="98" selection-end-column="7" /> + <folding /> </state> </provider> </entry> @@ -318,8 +232,28 @@ <file leaf-file-name="EntriesMgr.java" pinned="false" current="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1581" max-vertical-offset="6613"> - <caret line="93" column="33" selection-start-line="93" selection-start-column="25" selection-end-line="93" selection-end-column="36" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1581" max-vertical-offset="6664"> + <caret line="93" column="30" selection-start-line="93" selection-start-column="25" selection-end-line="93" selection-end-column="36" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="WorldTime.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1139" max-vertical-offset="3332"> + <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="CurrencyConverter.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1751" max-vertical-offset="4012"> + <caret line="103" column="97" selection-start-line="103" selection-start-column="41" selection-end-line="103" selection-end-column="41" /> <folding /> </state> </provider> @@ -703,7 +637,7 @@ </option> <option name="modificationStamps"> <map> - <entry key="$PROJECT_DIR$" value="2796686495306" /> + <entry key="$PROJECT_DIR$" value="2796764538501" /> </map> </option> <option name="projectBuildClasspath"> @@ -795,7 +729,6 @@ <component name="IdeDocumentHistory"> <option name="changedFiles"> <list> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/DeliciousPoster.java" /> <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/EntryComment.java" /> <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/EntryLink.java" /> <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/GoogleSearch.java" /> @@ -823,29 +756,30 @@ <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/MiscUtils.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Time.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TimeCmd.java" /> - <option value="$PROJECT_DIR$/mobibot.properties" /> <option value="$PROJECT_DIR$/deploy/mobibot.properties" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> + <option value="$PROJECT_DIR$/mobibot.properties" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> </list> </option> </component> @@ -1602,6 +1536,46 @@ <sortByType /> </navigator> <panes> + <pane id="Scope"> + <subPane subId="Project Files"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + <subPane subId="All"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="src"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + </pane> <pane id="ProjectPane"> <subPane> <PATH> @@ -1624,20 +1598,6 @@ <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> </PATH_ELEMENT> </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="website" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> <PATH> <PATH_ELEMENT> <option name="myItemId" value="mobibot" /> @@ -1651,32 +1611,6 @@ <option name="myItemId" value="src" /> <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="test" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="src" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="test" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="java" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> </PATH> <PATH> <PATH_ELEMENT> @@ -1752,46 +1686,6 @@ </PATH> </subPane> </pane> - <pane id="Scope"> - <subPane subId="Project Files"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - <subPane subId="All"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="src"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </pane> <pane id="PackagesPane"> <subPane> <PATH> @@ -1875,7 +1769,7 @@ <property name="vcs_file_view_treeWidth0" value="100" /> <property name="vcs_file_view_treeWidth3" value="101" /> <property name="vcs_file_view_treeWidth2" value="100" /> - <property name="last_opened_file_path" value="K:/Gradle/build.gradle" /> + <property name="last_opened_file_path" value="K:/TestGradle/TestGradle.ipr" /> <property name="GoToClass.toSaveIncludeLibraries" value="false" /> <property name="cvs_file_history_treeWidth0" value="135" /> <property name="cvs_file_history_treeWidth1" value="135" /> @@ -1929,7 +1823,7 @@ <option name="referencePos" value="0" /> <option name="showLabels" value="true" /> </component> - <component name="RunManager" selected="Gradle.mobibot [distZip]"> + <component name="RunManager" selected="Application.Mobibot"> <configuration default="false" name="mobibot [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> <option name="externalProjectPath" value="$PROJECT_DIR$" /> @@ -2209,6 +2103,16 @@ </ExternalSystemSettings> <method /> </configuration> + <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="PARENT_ENVS" value="true" /> + <envs /> + <module name="" /> + <option name="SCRIPT_NAME" value="" /> + <option name="PARAMETERS" value="" /> + <method /> + </configuration> <configuration default="true" type="FlexUnitRunConfigurationType" factoryName="FlexUnit" appDescriptorForEmulator="Android" class_name="" emulatorAdlOptions="" method_name="" package_name="" scope="Class"> <option name="BCName" value="" /> <option name="launcherParameters"> @@ -2223,16 +2127,6 @@ <option name="trusted" value="true" /> <method /> </configuration> - <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> - <option name="INTERPRETER_OPTIONS" value="" /> - <option name="WORKING_DIRECTORY" value="" /> - <option name="PARENT_ENVS" value="true" /> - <envs /> - <module name="" /> - <option name="SCRIPT_NAME" value="" /> - <option name="PARAMETERS" value="" /> - <method /> - </configuration> <configuration default="true" type="CucumberJavaRunConfigurationType" factoryName="Cucumber java"> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <option name="myFilePath" /> @@ -2470,11 +2364,16 @@ <workItem from="1398750996638" duration="2877000" /> <workItem from="1398764128641" duration="5827000" /> <workItem from="1398771310468" duration="7179000" /> + <workItem from="1398807853183" duration="5475000" /> + <workItem from="1398819448662" duration="4091000" /> + <workItem from="1398827780758" duration="5002000" /> + <workItem from="1398841856936" duration="2865000" /> + <workItem from="1398846143646" duration="974000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="212978000" /> + <option name="totallyTimeSpent" value="231385000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2498,14 +2397,13 @@ </component> <component name="ToolWindowManager"> <frame x="1592" y="-8" width="1616" height="1216" extended-state="6" /> - <editor active="true" /> + <editor active="false" /> <layout> <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31728044" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32797733" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> @@ -2514,23 +2412,24 @@ <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12660669" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32041588" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="true" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12660669" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.1844473" sideWeight="0.5288007" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.1844473" sideWeight="0.47119924" order="0" side_tool="false" content_ui="combo" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18602455" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> + <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4395085" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31568998" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3210576" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> + <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="FLOATING" visible="true" weight="0.32969153" sideWeight="0.5" order="19" side_tool="true" content_ui="tabs" x="2155" y="309" width="663" height="473" /> <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> @@ -2544,22 +2443,22 @@ <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> + <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> + <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32294616" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3295562" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32389048" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> </layout> <layout-to-restore> - <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> + <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> + <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> + <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> + <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> @@ -2578,28 +2477,28 @@ <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> + <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3201133" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> - <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> + <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3219659" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.52619326" order="1" side_tool="true" content_ui="tabs" /> - <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> + <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> + <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> + <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> - <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32698095" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> </layout-to-restore> </component> @@ -2689,6 +2588,37 @@ <option name="myLastEditedConfigurable" /> </component> <component name="editorHistoryManager"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1139" max-vertical-offset="3332"> + <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="4012"> + <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="6613"> + <caret line="295" column="19" selection-start-line="292" selection-start-column="4" selection-end-line="295" selection-end-column="17" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/mobibot.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="221" max-vertical-offset="374"> + <caret line="13" column="1" selection-start-line="13" selection-start-column="1" selection-end-line="13" selection-end-column="1" /> + </state> + </provider> + </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="408" max-vertical-offset="1462"> @@ -2776,27 +2706,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/logs/2014-04-20.xml"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="551"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/logs/current.xml"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="551"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/logs/nav.xml"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="595"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/log4j.properties"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="425"> @@ -2804,13 +2713,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/logs/2006-02-17.xml"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="323"> - <caret line="1" column="1" selection-start-line="1" selection-start-column="1" selection-end-line="1" selection-end-column="1" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/.gitignore"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.3242972" vertical-offset="0" max-vertical-offset="996"> @@ -2842,13 +2744,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1938"> - <caret line="10" column="0" selection-start-line="10" selection-start-column="0" selection-end-line="10" selection-end-column="60" /> - </state> - </provider> - </entry> <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/User.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.33333334" vertical-offset="984" max-vertical-offset="2448"> @@ -2863,13 +2758,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="374"> - <caret line="16" column="25" selection-start-line="16" selection-start-column="7" selection-end-line="16" selection-end-column="7" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="561"> @@ -2884,117 +2772,10 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1650" max-vertical-offset="2703"> - <caret line="125" column="66" selection-start-line="125" selection-start-column="52" selection-end-line="125" selection-end-column="52" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="250" max-vertical-offset="3230"> - <caret line="96" column="0" selection-start-line="96" selection-start-column="0" selection-end-line="96" selection-end-column="0" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="879" max-vertical-offset="1989"> - <caret line="106" column="58" selection-start-line="106" selection-start-column="44" selection-end-line="106" selection-end-column="44" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122"> - <caret line="44" column="13" selection-start-line="44" selection-start-column="13" selection-end-line="44" selection-end-column="13" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="221" max-vertical-offset="1564"> - <caret line="54" column="13" selection-start-line="54" selection-start-column="1" selection-end-line="54" selection-end-column="1" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="607" max-vertical-offset="1632"> - <caret line="120" column="27" selection-start-line="120" selection-start-column="27" selection-end-line="120" selection-end-column="27" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1391" max-vertical-offset="2091"> - <caret line="142" column="42" selection-start-line="142" selection-start-column="42" selection-end-line="142" selection-end-column="42" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.020383693" vertical-offset="136" max-vertical-offset="2244"> - <caret line="45" column="129" selection-start-line="45" selection-start-column="26" selection-end-line="45" selection-end-column="26" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="153" max-vertical-offset="1734"> - <caret line="44" column="0" selection-start-line="44" selection-start-column="0" selection-end-line="44" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="17" max-vertical-offset="1343"> - <caret line="47" column="94" selection-start-line="47" selection-start-column="94" selection-end-line="47" selection-end-column="94" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1360"> - <caret line="41" column="81" selection-start-line="41" selection-start-column="81" selection-end-line="41" selection-end-column="81" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.75152254" vertical-offset="556" max-vertical-offset="1377"> - <caret line="107" column="30" selection-start-line="107" selection-start-column="27" selection-end-line="107" selection-end-column="27" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="1097" max-vertical-offset="1785"> <caret line="104" column="4" selection-start-line="104" selection-start-column="4" selection-end-line="104" selection-end-column="4" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="612" max-vertical-offset="2788"> - <caret line="36" column="13" selection-start-line="36" selection-start-column="13" selection-end-line="36" selection-end-column="13" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1371" max-vertical-offset="6545"> - <caret line="93" column="33" selection-start-line="93" selection-start-column="25" selection-end-line="93" selection-end-column="36" /> - <folding /> </state> </provider> </entry> @@ -3002,17 +2783,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> - <folding> - <element signature="e#0#9144#0" expanded="true" /> - <element signature="imports" expanded="true" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="594" max-vertical-offset="1428"> - <caret line="135" column="29" selection-start-line="135" selection-start-column="29" selection-end-line="135" selection-end-column="29" /> <folding /> </state> </provider> @@ -3021,37 +2791,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="2890" max-vertical-offset="4369"> <caret line="213" column="36" selection-start-line="213" selection-start-column="31" selection-end-line="213" selection-end-column="43" /> - <folding> - <element signature="e#0#5604#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1071" max-vertical-offset="1972"> - <caret line="136" column="18" selection-start-line="136" selection-start-column="18" selection-end-line="136" selection-end-column="18" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="4895" max-vertical-offset="7531"> - <caret line="323" column="42" selection-start-line="323" selection-start-column="42" selection-end-line="323" selection-end-column="42" /> - <folding> - <element signature="e#0#9451#0" expanded="true" /> - <element signature="imports" expanded="true" /> - <element signature="e#2175#2189#0" expanded="true" /> - <element signature="e#2277#2295#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="928" max-vertical-offset="1683"> - <caret line="110" column="42" selection-start-line="110" selection-start-column="42" selection-end-line="110" selection-end-column="42" /> <folding /> </state> </provider> @@ -3060,81 +2799,176 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> <caret line="3" column="0" selection-start-line="3" selection-start-column="0" selection-end-line="3" selection-end-column="0" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/mobibot.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="374"> + <caret line="13" column="1" selection-start-line="13" selection-start-column="1" selection-end-line="13" selection-end-column="1" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1377"> + <caret line="17" column="99" selection-start-line="17" selection-start-column="99" selection-end-line="17" selection-end-column="99" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="760" max-vertical-offset="2465"> + <caret line="65" column="34" selection-start-line="65" selection-start-column="34" selection-end-line="65" selection-end-column="34" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1343"> + <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="5967"> + <caret line="51" column="13" selection-start-line="51" selection-start-column="13" selection-end-line="51" selection-end-column="13" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2091"> + <caret line="43" column="3" selection-start-line="43" selection-start-column="3" selection-end-line="43" selection-end-column="53" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="473" max-vertical-offset="1564"> + <caret line="81" column="39" selection-start-line="81" selection-start-column="39" selection-end-line="81" selection-end-column="39" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="388" max-vertical-offset="1632"> + <caret line="75" column="22" selection-start-line="75" selection-start-column="22" selection-end-line="75" selection-end-column="22" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="286" max-vertical-offset="1530"> + <caret line="64" column="7" selection-start-line="64" selection-start-column="7" selection-end-line="64" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="341" max-vertical-offset="1377"> + <caret line="71" column="48" selection-start-line="71" selection-start-column="48" selection-end-line="71" selection-end-column="48" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="388" max-vertical-offset="2244"> + <caret line="72" column="48" selection-start-line="72" selection-start-column="48" selection-end-line="72" selection-end-column="48" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="643" max-vertical-offset="1785"> + <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="303" max-vertical-offset="2720"> + <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1117" max-vertical-offset="2176"> + <caret line="142" column="42" selection-start-line="142" selection-start-column="7" selection-end-line="142" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="558" max-vertical-offset="3264"> + <caret line="103" column="97" selection-start-line="103" selection-start-column="41" selection-end-line="103" selection-end-column="41" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="133" max-vertical-offset="1989"> + <caret line="53" column="7" selection-start-line="53" selection-start-column="7" selection-end-line="53" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122"> + <caret line="38" column="51" selection-start-line="38" selection-start-column="51" selection-end-line="38" selection-end-column="51" /> <folding /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="889" max-vertical-offset="2669"> - <caret line="147" column="56" selection-start-line="147" selection-start-column="40" selection-end-line="147" selection-end-column="40" /> + <state vertical-scroll-proportion="0.0" vertical-offset="619" max-vertical-offset="2669"> + <caret line="98" column="59" selection-start-line="98" selection-start-column="7" selection-end-line="98" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="3587"> + <caret line="25" column="78" selection-start-line="25" selection-start-column="72" selection-end-line="25" selection-end-column="72" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="529" max-vertical-offset="5916"> + <caret line="93" column="30" selection-start-line="93" selection-start-column="25" selection-end-line="93" selection-end-column="36" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/build.gradle"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="306" max-vertical-offset="2057"> + <caret line="47" column="81" selection-start-line="47" selection-start-column="1" selection-end-line="47" selection-end-column="1" /> <folding /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.4461153" vertical-offset="45833" max-vertical-offset="47804"> - <caret line="2717" column="92" selection-start-line="2717" selection-start-column="51" selection-end-line="2717" selection-end-column="51" /> + <state vertical-scroll-proportion="0.88023955" vertical-offset="26215" max-vertical-offset="46869"> + <caret line="1619" column="71" selection-start-line="1619" selection-start-column="63" selection-end-line="1619" selection-end-column="63" /> <folding> - <element signature="e#0#67517#0" expanded="true" /> + <element signature="e#0#67752#0" expanded="true" /> <element signature="imports" expanded="true" /> - <element signature="e#5390#5398#0" expanded="true" /> - <element signature="e#5536#5549#0" expanded="true" /> - <element signature="e#5705#5716#0" expanded="true" /> - <element signature="e#5935#5943#0" expanded="true" /> - <element signature="e#6044#6052#0" expanded="true" /> - <element signature="e#7793#7826#0" expanded="true" /> - <element signature="e#16444#16445#0" expanded="true" /> - <element signature="e#16462#16463#0" expanded="true" /> - <element signature="e#16579#16580#0" expanded="true" /> - <element signature="e#16602#16603#0" expanded="true" /> - <element signature="e#16714#16715#0" expanded="true" /> - <element signature="e#16735#16736#0" expanded="true" /> - <element signature="e#16850#16851#0" expanded="true" /> - <element signature="e#16879#16880#0" expanded="true" /> - <element signature="e#17786#17787#0" expanded="true" /> - <element signature="e#17811#17812#0" expanded="true" /> - <element signature="e#18643#18644#0" expanded="true" /> - <element signature="e#18709#18710#0" expanded="true" /> - <element signature="e#19073#19106#0" expanded="true" /> - <element signature="e#19145#19146#0" expanded="true" /> - <element signature="e#19175#19176#0" expanded="true" /> - <element signature="e#19291#19292#0" expanded="true" /> - <element signature="e#19311#19312#0" expanded="true" /> - <element signature="e#19412#19413#0" expanded="true" /> - <element signature="e#19479#19480#0" expanded="true" /> - <element signature="e#19605#19606#0" expanded="true" /> - <element signature="e#19640#19641#0" expanded="true" /> - <element signature="e#21615#21616#0" expanded="true" /> - <element signature="e#21640#21641#0" expanded="true" /> - <element signature="e#21775#21776#0" expanded="true" /> - <element signature="e#21812#21813#0" expanded="true" /> - <element signature="e#21984#21985#0" expanded="true" /> - <element signature="e#22011#22012#0" expanded="true" /> - <element signature="e#22121#22122#0" expanded="true" /> - <element signature="e#22144#22145#0" expanded="true" /> - <element signature="e#22258#22259#0" expanded="true" /> - <element signature="e#22279#22280#0" expanded="true" /> - <element signature="e#22934#22935#0" expanded="true" /> - <element signature="e#22953#22954#0" expanded="true" /> - <element signature="e#23063#23064#0" expanded="true" /> - <element signature="e#23086#23087#0" expanded="true" /> - <element signature="e#23211#23212#0" expanded="true" /> - <element signature="e#23244#23245#0" expanded="true" /> - <element signature="e#31567#31575#0" expanded="true" /> - <element signature="e#54561#54562#0" expanded="true" /> - <element signature="e#54592#54593#0" expanded="true" /> - <element signature="e#54895#54896#0" expanded="true" /> - <element signature="e#54935#54936#0" expanded="true" /> - <element signature="e#56718#56719#0" expanded="true" /> - <element signature="e#56766#56767#0" expanded="true" /> - <element signature="e#57622#57623#0" expanded="true" /> - <element signature="e#57694#57695#0" expanded="true" /> - <element signature="e#57812#57813#0" expanded="true" /> - <element signature="e#57833#57834#0" expanded="true" /> - <element signature="e#58406#58407#0" expanded="true" /> - <element signature="e#58441#58442#0" expanded="true" /> </folding> </state> </provider> diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 607a007..4d18356 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -46,7 +46,7 @@ import java.text.NumberFormat; import java.util.*; /** - * Converts various currencies. + * Processes the {@link Commands#CURRENCY_CMD} command. * * @author Erik C. Thauvin * @created Feb 11, 2004 @@ -85,16 +85,18 @@ public class CurrencyConverter implements Runnable private String pubDate = ""; /** - * Creates a new CurrencyConverter object. + * Creates a new {@link CurrencyConverter} instance. * - * @param bot The bot. + * @param bot The bot's instance. */ public CurrencyConverter(Mobibot bot) { this.bot = bot; } - // Converts specified currencies. + /** + * Converts the specified currencies. + */ public final void run() { if (Utils.isValidString(sender) && Utils.isValidString(query)) @@ -215,7 +217,7 @@ public class CurrencyConverter implements Runnable * @param sender The nick of the person who sent the message. * @param query The currency query. */ - public void setQuery(String sender, String query) + public synchronized void setQuery(String sender, String query) { this.query = query; this.sender = sender; diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 8f60136..36ef5ca 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -51,7 +51,7 @@ public class DeliciousPoster private final String ircServer; /** - * Creates a new DeliciousPoster instance. + * Creates a new {@link DeliciousPoster} instance. * * @param username The del.icio.us username. * @param password The del.icio.us password. diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/Dice.java index f5f63d2..7ffdc9f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/Dice.java @@ -36,7 +36,7 @@ package net.thauvin.erik.mobibot; import java.util.Random; /** - * The {@link net.thauvin.erik.mobibot.Commands#DICE_CMD} command + * Processes the {@link Commands#DICE_CMD} command. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-28 @@ -56,9 +56,9 @@ public class Dice } /** - * Rolls the dice + * Rolls the dice. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The sender's nickname. */ public static void roll(Mobibot bot, String sender) diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index a3add76..e1d00af 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -214,6 +214,9 @@ public class EntriesMgr /** * Saves the entries. * + * @param bot The bot object. + * @param entries The entries array. + * @param history The history array. * @param isDayBackup Set the true if the daily backup file should also be created. */ public static void saveEntries(Mobibot bot, List<EntryLink> entries, List<String> history, boolean isDayBackup) diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 89992d9..d734f6e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -37,10 +37,10 @@ package net.thauvin.erik.mobibot; import com.sun.syndication.feed.synd.SyndCategoryImpl; import java.io.Serializable; -import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * The class used to store link entries. @@ -57,10 +57,10 @@ public class EntryLink implements Serializable static final long serialVersionUID = 3676245542270899086L; // The link's comments - private final List<EntryComment> comments = new ArrayList<EntryComment>(0); + private final List<EntryComment> comments = new CopyOnWriteArrayList<EntryComment>(); // The tags/categories - private final List<SyndCategoryImpl> tags = new ArrayList<SyndCategoryImpl>(0); + private final List<SyndCategoryImpl> tags = new CopyOnWriteArrayList<SyndCategoryImpl>(); // The channel private String channel = ""; @@ -106,7 +106,7 @@ public class EntryLink implements Serializable * * @param tags The space-delimited tags. */ - public final synchronized void setTags(String tags) + public final void setTags(String tags) { if (tags != null) { @@ -185,7 +185,7 @@ public class EntryLink implements Serializable * * @return The total number of comments for this entry. */ - public final synchronized int addComment(String comment, String nick) + public final int addComment(String comment, String nick) { comments.add(new EntryComment(comment, nick)); @@ -197,7 +197,7 @@ public class EntryLink implements Serializable * * @param index The index of the comment to delete. */ - public final synchronized void deleteComment(int index) + public final void deleteComment(int index) { if (index < comments.size()) { @@ -210,7 +210,7 @@ public class EntryLink implements Serializable * * @return The channel */ - public final synchronized String getChannel() + public final String getChannel() { return channel; } @@ -221,7 +221,7 @@ public class EntryLink implements Serializable * @param channel The channel. */ @SuppressWarnings("UnusedDeclaration") - public final synchronized void setChannel(String channel) + public final void setChannel(String channel) { this.channel = channel; } @@ -233,7 +233,7 @@ public class EntryLink implements Serializable * * @return The specific comment. */ - public final synchronized EntryComment getComment(int index) + public final EntryComment getComment(int index) { return (comments.get(index)); } @@ -243,7 +243,7 @@ public class EntryLink implements Serializable * * @return The comments. */ - public final synchronized EntryComment[] getComments() + public final EntryComment[] getComments() { return (comments.toArray(new EntryComment[comments.size()])); } @@ -253,7 +253,7 @@ public class EntryLink implements Serializable * * @return The count of comments. */ - public final synchronized int getCommentsCount() + public final int getCommentsCount() { return comments.size(); } @@ -263,7 +263,7 @@ public class EntryLink implements Serializable * * @return The date. */ - public final synchronized Date getDate() + public final Date getDate() { return date; } @@ -273,7 +273,7 @@ public class EntryLink implements Serializable * * @return The tags as a comma-delimited string. */ - public final synchronized String getDeliciousTags() + public final String getDeliciousTags() { final StringBuilder tags = new StringBuilder(nick); @@ -291,7 +291,7 @@ public class EntryLink implements Serializable * * @return The link. */ - public final synchronized String getLink() + public final String getLink() { return link; } @@ -301,7 +301,7 @@ public class EntryLink implements Serializable * * @param link The new link. */ - public final synchronized void setLink(String link) + public final void setLink(String link) { this.link = link; } @@ -311,7 +311,7 @@ public class EntryLink implements Serializable * * @return The login; */ - public final synchronized String getLogin() + public final String getLogin() { return login; } @@ -322,7 +322,7 @@ public class EntryLink implements Serializable * @param login The new login. */ @SuppressWarnings("UnusedDeclaration") - public final synchronized void setLogin(String login) + public final void setLogin(String login) { this.login = login; } @@ -332,7 +332,7 @@ public class EntryLink implements Serializable * * @return The nickname. */ - public final synchronized String getNick() + public final String getNick() { return nick; } @@ -342,7 +342,7 @@ public class EntryLink implements Serializable * * @param nick The new nickname. */ - public final synchronized void setNick(String nick) + public final void setNick(String nick) { this.nick = nick; } @@ -352,7 +352,7 @@ public class EntryLink implements Serializable * * @return The tags. */ - public final synchronized List getTags() + public final List getTags() { return tags; } @@ -362,7 +362,7 @@ public class EntryLink implements Serializable * * @param tags The tags. */ - final synchronized void setTags(List<SyndCategoryImpl> tags) + final void setTags(List<SyndCategoryImpl> tags) { this.tags.addAll(tags); } @@ -372,7 +372,7 @@ public class EntryLink implements Serializable * * @return The title. */ - public final synchronized String getTitle() + public final String getTitle() { return title; } @@ -382,7 +382,7 @@ public class EntryLink implements Serializable * * @param title The new title. */ - public final synchronized void setTitle(String title) + public final void setTitle(String title) { this.title = title; } @@ -392,7 +392,7 @@ public class EntryLink implements Serializable * * @return true if there are comments, false otherwise. */ - public final synchronized boolean hasComments() + public final boolean hasComments() { return (!comments.isEmpty()); } @@ -402,7 +402,7 @@ public class EntryLink implements Serializable * * @return true if there are tags, false otherwise. */ - public final synchronized boolean hasTags() + public final boolean hasTags() { return (!tags.isEmpty()); } @@ -414,7 +414,7 @@ public class EntryLink implements Serializable * @param comment The actual comment. * @param nick The nickname of the author of the comment. */ - public final synchronized void setComment(int index, String comment, String nick) + public final void setComment(int index, String comment, String nick) { if (index < comments.size()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index adbcdda..ffc91e9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -79,9 +79,9 @@ public class FeedReader implements Runnable private final String url; /** - * Creates a new FeedReader object. + * Creates a new {@link FeedReader} instance. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The nick of the person who sent the message. * @param url The URL to fetch. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java index 190a232..7a01981 100644 --- a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java @@ -44,7 +44,7 @@ import java.net.URLConnection; import java.net.URLEncoder; /** - * Performs a Google search or spell checking query. + * Processes the {@link Commands#GOOGLE_CMD} command. * * @author Erik C. Thauvin * @created Feb 7, 2004 @@ -73,9 +73,9 @@ public class GoogleSearch implements Runnable private final String sender; /** - * Creates a new GoogleSearch object. + * Creates a new {@link GoogleSearch} instance. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The nick of the person who sent the message. * @param query The Google query */ @@ -87,7 +87,7 @@ public class GoogleSearch implements Runnable } /** - * Main processing method. + * Searches Google. */ public final void run() { diff --git a/src/main/java/net/thauvin/erik/mobibot/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/Lookup.java index 43884ec..c3a20b1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/Lookup.java @@ -41,7 +41,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; /** - * Performs a DNS lookup query. + * Processes the {@link Commands#LOOKUP_CMD} command. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-26 diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 8cdb5d7..f566027 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -176,7 +176,7 @@ public class Mobibot extends PircBot private final List<String> recap = new ArrayList<String>(0); /** - * The {@link Commands#TELL_CMD} messages queue. + * Processes the {@link Commands#TELL_CMD} messages queue. */ private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<TellMessage>(); @@ -311,7 +311,7 @@ public class Mobibot extends PircBot private String weblogUrl = ""; /** - * Creates a new Mobibot object. + * Creates a new {@link Mobibot} instance. * * @param server The server. * @param port The port. @@ -346,6 +346,11 @@ public class Mobibot extends PircBot { today = EntriesMgr.loadEntries(this.logsDir + EntriesMgr.CURRENT_XML, this.channel, entries); + if (logger.isDebugEnabled()) + { + logger.debug("Last feed: " + today); + } + if (!Utils.today().equals(today)) { entries.clear(); @@ -787,6 +792,11 @@ public class Mobibot extends PircBot */ private void cleanTellMessages() { + if (logger.isDebugEnabled()) + { + logger.debug("Cleaning the messages."); + } + TellMessagesMgr.cleanTellMessages(tellMessages, tellMaxDays); } @@ -876,13 +886,16 @@ public class Mobibot extends PircBot { EntryLink entry; - for (int i = 0; i < entries.size(); i++) + synchronized (entries) { - entry = entries.get(i); - - if (link.equals(entry.getLink())) + for (int i = 0; i < entries.size(); i++) { - return i; + entry = entries.get(i); + + if (link.equals(entry.getLink())) + { + return i; + } } } @@ -896,7 +909,7 @@ public class Mobibot extends PircBot */ public final String getBacklogsUrl() { - return backLogsUrl; + return this.backLogsUrl; } /** @@ -914,9 +927,9 @@ public class Mobibot extends PircBot * * @return The feed info cache. */ - public final synchronized FeedFetcherCache getFeedInfoCache() + public final FeedFetcherCache getFeedInfoCache() { - return feedInfoCache; + return this.feedInfoCache; } /** @@ -926,7 +939,7 @@ public class Mobibot extends PircBot */ public final String getIrcServer() { - return ircServer; + return this.ircServer; } /** @@ -936,7 +949,7 @@ public class Mobibot extends PircBot */ public final String getLogsDir() { - return logsDir; + return this.logsDir; } /** @@ -973,9 +986,9 @@ public class Mobibot extends PircBot * * @return Today's date. */ - public synchronized String getToday() + public String getToday() { - return today; + return this.today; } /** @@ -985,7 +998,7 @@ public class Mobibot extends PircBot */ public final String getWeblogUrl() { - return weblogUrl; + return this.weblogUrl; } /** @@ -1198,7 +1211,7 @@ public class Mobibot extends PircBot send(sender, "To view queued and sent messages:"); send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.VIEW_CMD)); - send(sender, "Messages are kept for around " + Utils.bold(tellMaxDays) + " days."); + send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days."); } else { @@ -1362,6 +1375,7 @@ public class Mobibot extends PircBot timeInSeconds -= (hours * 3600L); final long minutes = timeInSeconds / 60L; + send(sender, "Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size() + (isTellEnabled() && isOp(sender) ? ", Messages: " + tellMessages.size() : "") + ']', @@ -1565,7 +1579,7 @@ public class Mobibot extends PircBot if (dupIndex == -1) { - if (!Utils.today().equals(getToday())) + if (!Utils.today().equals(today)) { isBackup = true; saveEntries(true); @@ -1600,7 +1614,7 @@ public class Mobibot extends PircBot { try { - final Document html = Jsoup.connect(link).get(); + final Document html = Jsoup.connect(link).userAgent("Mozilla").get(); final String htmlTitle = html.title(); if (Utils.isValidString(htmlTitle)) @@ -1968,7 +1982,7 @@ public class Mobibot extends PircBot } } } - // L1.1:<comment> + // L1.1:<command> else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { isCommand = true; @@ -2503,7 +2517,7 @@ public class Mobibot extends PircBot .bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id|" + Commands.TELL_ALL_CMD + '>') ); - send(sender, "Messages are kept for around " + Utils.bold(tellMaxDays) + " days."); + send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days."); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Quote.java b/src/main/java/net/thauvin/erik/mobibot/Quote.java index 721a384..b626988 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Quote.java +++ b/src/main/java/net/thauvin/erik/mobibot/Quote.java @@ -43,7 +43,7 @@ import java.net.URL; import java.net.URLConnection; /** - * Retrieve quote from <a href="iheartquotes.com">I Heart Quotes</a> + * Processes the {@link Commands#QUOTE_CMD} command. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-20 @@ -59,7 +59,7 @@ public class Quote implements Runnable "http://www.iheartquotes.com/api/v1/random?format=json&max_lines=1&source=esr+humorix_misc+humorix_stories+joel_on_software+macintosh+math+mav_flame+osp_rules+paul_graham+prog_style+subversion"; /** - * The bot. + * The bot's instance. */ private final Mobibot bot; @@ -69,9 +69,9 @@ public class Quote implements Runnable private final String sender; /** - * Creates a new StockQuote object. + * Creates a new {@link StockQuote} instance. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The nick of the person who sent the message. */ public Quote(Mobibot bot, String sender) @@ -81,7 +81,7 @@ public class Quote implements Runnable } /** - * Returns a random quote. + * Returns a random quote from <a href="http://iheartquotes.com/">I Heart Quote</a> */ public final void run() { diff --git a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java index 492c1f5..50d57e6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java @@ -41,7 +41,7 @@ import org.apache.commons.httpclient.methods.GetMethod; import java.io.IOException; /** - * Retrieves a stock quote from Yahoo!. + * Processes the {@link Commands#STOCK_CMD} command. * * @author Erik C. Thauvin * @created Feb 7, 2004 @@ -70,9 +70,9 @@ public class StockQuote implements Runnable private final String symbol; /** - * Creates a new StockQuote object. + * Creates a new {@link StockQuote} instance. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The nick of the person who sent the message. * @param symbol The stock symbol. */ @@ -84,7 +84,7 @@ public class StockQuote implements Runnable } /** - * Returns the specified stock quote. + * Returns the specified stock quote from Yahoo! */ public final void run() { diff --git a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java index 53307b9..c99a9ab 100644 --- a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java +++ b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java @@ -5,9 +5,9 @@ import javax.swing.*; /** * This is the 3rd version of SwingWorker (also known as SwingWorker 3), an abstract class that you subclass to perform * GUI-related work in a dedicated thread. For instructions on and examples of using this class, see: - * <p/> + * <p> * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html - * <p/> + * </p> * Note that the API changed slightly in the 3rd version: You must now invoke start() on the SwingWorker after creating * it. */ @@ -62,6 +62,8 @@ public abstract class SwingWorker /** * Compute the value to be returned by the <code>get</code> method. + * + * @return The computed value. */ public abstract Object construct(); @@ -107,6 +109,8 @@ public abstract class SwingWorker /** * Get the value produced by the worker thread, or null if it hasn't been constructed yet. + * + * @return The value. */ protected synchronized Object getValue() { @@ -115,6 +119,8 @@ public abstract class SwingWorker /** * Set the value produced by worker thread + * + * @param x The object. */ private synchronized void setValue(Object x) { diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 030b825..ceb9de2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -83,61 +83,121 @@ public class TellMessage implements Serializable } + /** + * Returns the message id. + * + * @return The message id. + */ public String getId() { return this.id; } + /** + * Returns the message text. + * + * @return The text of the message. + */ public String getMessage() { return message; } + /** + * Returns the state of the queue flag. + * + * @return <code>true</code> if the message is queued. + */ public Date getQueued() { return queued; } + /** + * Returns the state of the received flag. + * + * @return <code>true</code> if the message has been received. + */ public Date getReceived() { return received; } + /** + * Returns the message's recipient. + * + * @return The recipient of the message. + */ public String getRecipient() { return recipient; } + /** + * Returns the message's sender. + * + * @return The sender of the message. + */ public String getSender() { return sender; } + /** + * Matches the message sender or recipient. + * + * @param nick The nickname to match with. + * + * @return <code>true</code> if the nickname matches. + */ public boolean isMatch(String nick) { return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); } + /** + * Match the message ID. + * + * @param id The ID to match with. + * + * @return <code>true</code> if the id matches. + */ public boolean isMatchId(String id) { return this.id.equals(id); } + /** + * Returns the notification flag state. + * + * @return <code>true</code> if the sender has been notified. + */ public boolean isNotified() { return this.isNotified; } + /** + * Returns the received flag state. + * + * @return <code>true</code> if the message was received. + */ public boolean isReceived() { return this.isReceived; } + /** + * Sets the notified flag. + */ public void setIsNotified() { this.isNotified = true; } + /** + * Sets the received flag. + */ public void setIsReceived() { this.received = Calendar.getInstance().getTime(); diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index b0b9d43..bb261e5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -80,6 +80,11 @@ public class TellMessagesMgr try { + if (logger.isDebugEnabled()) + { + logger.debug("Loading the messages."); + } + return ((List<TellMessage>) input.readObject()); } finally @@ -118,6 +123,10 @@ public class TellMessagesMgr try { + if (logger.isDebugEnabled()) + { + logger.debug("Saving the messages."); + } output.writeObject(messages); } finally @@ -132,7 +141,10 @@ public class TellMessagesMgr } /** - * Cleans the messages queue. + * Cleans the messages queue + * + * @param tellMessages The messages list. + * @param tellMaxDays The maximum number of days to keep messages for. */ public static void cleanTellMessages(List<TellMessage> tellMessages, int tellMaxDays) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/Twitter.java index 23fd404..049a15f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/Twitter.java @@ -39,7 +39,7 @@ import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; /** - * Inserts presence information into Twitter. + * Processes the {@link Commands#TWITTER_CMD} command. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created Sept 10, 2008 @@ -83,9 +83,9 @@ public class Twitter implements Runnable private final String sender; /** - * Creates a new Twitter object. + * Creates a new {@link Twitter} instance. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The nick of the person who sent the message. * @param consumerKey The Twitter consumer key. * @param consumerSecret The Twitter consumer secret. @@ -105,6 +105,9 @@ public class Twitter implements Runnable this.sender = sender; } + /** + * Posts to twitter. + */ public final void run() { try diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 1bcb5ba..3fa40a7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -10,12 +10,13 @@ import java.io.InputStreamReader; /** * The <code>TwitterOAuth</code> class. - * <p/> + * <p> * Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> to register your bot. + * </p> * Then execute: - * <p/> - * <code>java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret></code> - * <p/> + * <p> + * <code>java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret></code> + * </p> * and follow the prompts/instructions. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/War.java index 6430374..ec2ba9f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/War.java @@ -36,7 +36,7 @@ package net.thauvin.erik.mobibot; import java.util.Random; /** - * The <code>War</code> class. + * Processes the {@link Commands#WAR_CMD} command. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-28 @@ -69,7 +69,7 @@ public class War /** * Plays war. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The sender's nickname. */ public static void play(Mobibot bot, String sender) diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/Weather.java index af9eab7..105e770 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/Weather.java @@ -42,7 +42,7 @@ import java.text.DecimalFormat; import java.util.Date; /** - * Fetches the weather data from a specific station ID. + * Processes the {@link Commands#LOOKUP_CMD} command. * * @author Erik C. Thauvin * @created Feb 7, 2004 @@ -81,9 +81,9 @@ public class Weather implements Runnable private final boolean isPrivate; /** - * Creates a new Weather object. + * Creates a new {@link Weather} instance. * - * @param bot The bot. + * @param bot The bot's instance. * @param sender The nick of the person who sent the message. * @param station The station ID. * @param isPrivate Set to true is the response should be send as a private message. @@ -97,7 +97,7 @@ public class Weather implements Runnable } /** - * Main processing method. + * Fetches the weather data from a specific station ID. */ public final void run() { diff --git a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java index e22514f..26eccfe 100644 --- a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java @@ -40,7 +40,7 @@ import java.util.TimeZone; import java.util.TreeMap; /** - * Processes the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD} command. + * The {@link Commands#TIME_CMD} command. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-27 @@ -65,7 +65,7 @@ public class WorldTime new SimpleDateFormat("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"); /** - * Creates a new time object. + * Creates a new {@link WorldTime} instance. */ public WorldTime() { @@ -140,8 +140,9 @@ public class WorldTime } /** - * Responds with the current time. + * Responds with the current time in the specified timezone/country. * + * @param bot The bot instance. * @param sender The nick of the person who sent the message. * @param args The time command arguments. * @param isPrivate Set to true is the response should be send as a private message. From 2518945cfca5e0632aa3601a3e7b1401139f8238 Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Thu, 1 May 2014 02:39:23 -0700 Subject: [PATCH 011/842] Validation for the addlog command. More code cleanup. --- mobibot.iws | 634 ++++++++++-------- .../net/thauvin/erik/mobibot/EntriesMgr.java | 14 +- .../net/thauvin/erik/mobibot/Mobibot.java | 90 +-- .../net/thauvin/erik/mobibot/TellMessage.java | 12 +- .../thauvin/erik/mobibot/TellMessagesMgr.java | 1 + 5 files changed, 414 insertions(+), 337 deletions(-) diff --git a/mobibot.iws b/mobibot.iws index abcd1f6..5a560ff 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -33,26 +33,10 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/build.gradle" afterPath="$PROJECT_DIR$/build.gradle" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> </list> <ignored path="$USER_HOME$/.griffon/" /> @@ -124,8 +108,8 @@ <option name="LOG_MESSAGE" value="" /> </breakpoint> </breakpoint_any> - <ui_properties converted="true" /> <breakpoint_rules converted="true" /> + <ui_properties converted="true" /> </component> <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> <component name="FavoritesManager"> @@ -159,102 +143,135 @@ <file leaf-file-name="Mobibot.java" pinned="false" current="true" current-in-tab="true"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.88023955" vertical-offset="26215" max-vertical-offset="46869"> - <caret line="1619" column="71" selection-start-line="1619" selection-start-column="63" selection-end-line="1619" selection-end-column="63" /> + <state vertical-scroll-proportion="0.5952381" vertical-offset="34796" max-vertical-offset="47005"> + <caret line="2114" column="36" selection-start-line="2114" selection-start-column="36" selection-end-line="2114" selection-end-column="36" /> <folding> - <element signature="e#0#67752#0" expanded="true" /> + <element signature="e#0#67784#0" expanded="true" /> <element signature="imports" expanded="true" /> + <element signature="e#5390#5398#0" expanded="true" /> + <element signature="e#5546#5559#0" expanded="true" /> + <element signature="e#5715#5726#0" expanded="true" /> + <element signature="e#5945#5953#0" expanded="true" /> + <element signature="e#6054#6062#0" expanded="true" /> + <element signature="e#31758#31766#0" expanded="true" /> </folding> </state> </provider> </entry> </file> - <file leaf-file-name="build.gradle" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="306" max-vertical-offset="2057"> - <caret line="47" column="81" selection-start-line="47" selection-start-column="1" selection-end-line="47" selection-end-column="1" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="TellMessage.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="425" max-vertical-offset="3587"> - <caret line="25" column="78" selection-start-line="25" selection-start-column="72" selection-end-line="25" selection-end-column="72" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="TellMessagesMgr.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2414" max-vertical-offset="2890"> - <caret line="142" column="42" selection-start-line="142" selection-start-column="7" selection-end-line="142" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="DeliciousPoster.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="901" max-vertical-offset="2771"> - <caret line="53" column="7" selection-start-line="53" selection-start-column="7" selection-end-line="53" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Dice.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="646" max-vertical-offset="1734"> - <caret line="38" column="51" selection-start-line="38" selection-start-column="51" selection-end-line="38" selection-end-column="51" /> - <folding /> - </state> - </provider> - </entry> - </file> <file leaf-file-name="Weather.java" pinned="false" current="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1666" max-vertical-offset="3298"> + <state vertical-scroll-proportion="0.0" vertical-offset="619" max-vertical-offset="2669"> <caret line="98" column="59" selection-start-line="98" selection-start-column="7" selection-end-line="98" selection-end-column="7" /> <folding /> </state> </provider> </entry> </file> + <file leaf-file-name="War.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="391" max-vertical-offset="1343"> + <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="Commands.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-12.035714" vertical-offset="530" max-vertical-offset="3825"> + <caret line="83" column="36" selection-start-line="83" selection-start-column="31" selection-end-line="83" selection-end-column="41" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="Utils.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="4741" max-vertical-offset="5678"> + <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="TwitterOAuth.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="170" max-vertical-offset="1377"> + <caret line="17" column="99" selection-start-line="17" selection-start-column="99" selection-end-line="17" selection-end-column="99" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="Twitter.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="848" max-vertical-offset="1785"> + <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> + <folding /> + </state> + </provider> + </entry> + </file> <file leaf-file-name="EntriesMgr.java" pinned="false" current="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1581" max-vertical-offset="6664"> - <caret line="93" column="30" selection-start-line="93" selection-start-column="25" selection-end-line="93" selection-end-column="36" /> + <state vertical-scroll-proportion="0.0" vertical-offset="238" max-vertical-offset="6154"> + <caret line="72" column="47" selection-start-line="72" selection-start-column="47" selection-end-line="72" selection-end-column="47" /> + <folding> + <element signature="imports" expanded="true" /> + </folding> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="TellMessagesMgr.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1256" max-vertical-offset="2193"> + <caret line="129" column="0" selection-start-line="129" selection-start-column="0" selection-end-line="129" selection-end-column="0" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="WorldTime.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <file leaf-file-name="TellMessage.java" pinned="false" current="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1139" max-vertical-offset="3332"> - <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="CurrencyConverter.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1751" max-vertical-offset="4012"> - <caret line="103" column="97" selection-start-line="103" selection-start-column="41" selection-end-line="103" selection-end-column="41" /> - <folding /> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="3587"> + <caret line="35" column="68" selection-start-line="35" selection-start-column="0" selection-end-line="35" selection-end-column="0" /> + <folding> + <element signature="e#0#4562#0" expanded="true" /> + <element signature="imports" expanded="true" /> + <element signature="e#2721#2722#0" expanded="true" /> + <element signature="e#2737#2738#0" expanded="true" /> + <element signature="e#2850#2851#0" expanded="true" /> + <element signature="e#2871#2872#0" expanded="true" /> + <element signature="e#3011#3012#0" expanded="true" /> + <element signature="e#3031#3032#0" expanded="true" /> + <element signature="e#3184#3185#0" expanded="true" /> + <element signature="e#3206#3207#0" expanded="true" /> + <element signature="e#3333#3334#0" expanded="true" /> + <element signature="e#3356#3357#0" expanded="true" /> + <element signature="e#3474#3475#0" expanded="true" /> + <element signature="e#3494#3495#0" expanded="true" /> + <element signature="e#3697#3698#0" expanded="true" /> + <element signature="e#3778#3779#0" expanded="true" /> + <element signature="e#3948#3949#0" expanded="true" /> + <element signature="e#3980#3981#0" expanded="true" /> + <element signature="e#4131#4132#0" expanded="true" /> + <element signature="e#4155#4156#0" expanded="true" /> + <element signature="e#4298#4299#0" expanded="true" /> + <element signature="e#4322#4323#0" expanded="true" /> + <element signature="e#4393#4394#0" expanded="true" /> + <element signature="e#4417#4418#0" expanded="true" /> + </folding> </state> </provider> </entry> @@ -761,8 +778,6 @@ <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> - <option value="$PROJECT_DIR$/mobibot.properties" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> @@ -770,13 +785,15 @@ <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> + <option value="$PROJECT_DIR$/mobibot.properties" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> @@ -838,10 +855,10 @@ <option name="port" value="jasper:1666" /> </component> <component name="ProjectFrameBounds"> - <option name="x" value="1592" /> - <option name="y" value="-8" /> - <option name="width" value="1616" /> - <option name="height" value="1216" /> + <option name="x" value="1703" /> + <option name="y" value="29" /> + <option name="width" value="1500" /> + <option name="height" value="1146" /> </component> <component name="ProjectInspectionProfilesVisibleTreeState"> <entry key="Default"> @@ -1576,6 +1593,34 @@ </PATH> </subPane> </pane> + <pane id="PackagesPane"> + <subPane> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="net.thauvin.erik.mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> + </PATH_ELEMENT> + </PATH> + </subPane> + </pane> <pane id="ProjectPane"> <subPane> <PATH> @@ -1686,34 +1731,6 @@ </PATH> </subPane> </pane> - <pane id="PackagesPane"> - <subPane> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="net.thauvin.erik.mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </pane> <pane id="Favorites" /> </panes> </component> @@ -2103,16 +2120,6 @@ </ExternalSystemSettings> <method /> </configuration> - <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> - <option name="INTERPRETER_OPTIONS" value="" /> - <option name="WORKING_DIRECTORY" value="" /> - <option name="PARENT_ENVS" value="true" /> - <envs /> - <module name="" /> - <option name="SCRIPT_NAME" value="" /> - <option name="PARAMETERS" value="" /> - <method /> - </configuration> <configuration default="true" type="FlexUnitRunConfigurationType" factoryName="FlexUnit" appDescriptorForEmulator="Android" class_name="" emulatorAdlOptions="" method_name="" package_name="" scope="Class"> <option name="BCName" value="" /> <option name="launcherParameters"> @@ -2127,6 +2134,16 @@ <option name="trusted" value="true" /> <method /> </configuration> + <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="PARENT_ENVS" value="true" /> + <envs /> + <module name="" /> + <option name="SCRIPT_NAME" value="" /> + <option name="PARAMETERS" value="" /> + <method /> + </configuration> <configuration default="true" type="CucumberJavaRunConfigurationType" factoryName="Cucumber java"> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <option name="myFilePath" /> @@ -2369,11 +2386,16 @@ <workItem from="1398827780758" duration="5002000" /> <workItem from="1398841856936" duration="2865000" /> <workItem from="1398846143646" duration="974000" /> + <workItem from="1398908087195" duration="409000" /> + <workItem from="1398910788873" duration="1036000" /> + <workItem from="1398912049023" duration="768000" /> + <workItem from="1398914151479" duration="538000" /> + <workItem from="1398929482588" duration="5179000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="231385000" /> + <option name="totallyTimeSpent" value="239315000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2396,14 +2418,15 @@ </todo-panel> </component> <component name="ToolWindowManager"> - <frame x="1592" y="-8" width="1616" height="1216" extended-state="6" /> + <frame x="1703" y="29" width="1500" height="1146" extended-state="0" /> <editor active="false" /> <layout> <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32797733" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32692307" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.27732792" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> @@ -2412,24 +2435,24 @@ <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32041588" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="true" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12660669" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31983805" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12361111" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.1844473" sideWeight="0.5288007" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.1844473" sideWeight="0.47119924" order="0" side_tool="false" content_ui="combo" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4395085" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> + <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.18055555" sideWeight="0.47119924" order="0" side_tool="false" content_ui="combo" /> + <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3228745" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43927124" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31568998" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> + <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="FLOATING" visible="true" weight="0.32969153" sideWeight="0.5" order="19" side_tool="true" content_ui="tabs" x="2155" y="309" width="663" height="473" /> <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> @@ -2443,22 +2466,21 @@ <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> + <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32294616" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3295562" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> </layout> <layout-to-restore> - <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> + <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> + <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> + <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> + <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> @@ -2477,28 +2499,28 @@ <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> + <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3201133" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> - <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> + <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3219659" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.52619326" order="1" side_tool="true" content_ui="tabs" /> - <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> + <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> + <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32698095" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32698095" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> </layout-to-restore> </component> @@ -2588,14 +2610,6 @@ <option name="myLastEditedConfigurable" /> </component> <component name="editorHistoryManager"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1139" max-vertical-offset="3332"> - <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="4012"> @@ -2608,7 +2622,9 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="6613"> <caret line="295" column="19" selection-start-line="292" selection-start-column="4" selection-end-line="295" selection-end-column="17" /> - <folding /> + <folding> + <element signature="imports" expanded="true" /> + </folding> </state> </provider> </entry> @@ -2683,14 +2699,14 @@ </provider> </entry> <entry file="file://$PROJECT_DIR$/README.txt"> + <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> + <state /> + </provider> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="-8.16" vertical-offset="0" max-vertical-offset="391"> <caret line="12" column="4" selection-start-line="12" selection-start-column="4" selection-end-line="12" selection-end-column="4" /> </state> </provider> - <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> - <state /> - </provider> </entry> <entry file="file://K:/Gradle/build.gradle"> <provider selected="true" editor-type-id="text-editor"> @@ -2699,20 +2715,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/settings.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-0.0" vertical-offset="0" max-vertical-offset="272"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/log4j.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="425"> - <caret line="18" column="1" selection-start-line="18" selection-start-column="1" selection-end-line="18" selection-end-column="1" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/.gitignore"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.3242972" vertical-offset="0" max-vertical-offset="996"> @@ -2721,14 +2723,14 @@ </provider> </entry> <entry file="file://$PROJECT_DIR$/licenses/License.txt"> + <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> + <state /> + </provider> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.035789475" vertical-offset="0" max-vertical-offset="950"> <caret line="2" column="23" selection-start-line="2" selection-start-column="23" selection-end-line="2" selection-end-column="23" /> </state> </provider> - <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> - <state /> - </provider> </entry> <entry file="file://$PROJECT_DIR$/website/simple.css"> <provider selected="true" editor-type-id="text-editor"> @@ -2751,13 +2753,6 @@ </state> </provider> </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/PircBot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.33266333" vertical-offset="6724" max-vertical-offset="50932"> - <caret line="450" column="22" selection-start-line="450" selection-start-column="22" selection-end-line="450" selection-end-column="22" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="561"> @@ -2779,22 +2774,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> - <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2890" max-vertical-offset="4369"> - <caret line="213" column="36" selection-start-line="213" selection-start-column="31" selection-end-line="213" selection-end-column="43" /> - <folding /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/buildnum.properties"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> @@ -2802,20 +2781,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="374"> - <caret line="13" column="1" selection-start-line="13" selection-start-column="1" selection-end-line="13" selection-end-column="1" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1377"> - <caret line="17" column="99" selection-start-line="17" selection-start-column="99" selection-end-line="17" selection-end-column="99" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="760" max-vertical-offset="2465"> @@ -2823,21 +2788,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1343"> - <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="5967"> - <caret line="51" column="13" selection-start-line="51" selection-start-column="13" selection-end-line="51" selection-end-column="13" /> - <folding /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2091"> @@ -2845,14 +2795,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="473" max-vertical-offset="1564"> - <caret line="81" column="39" selection-start-line="81" selection-start-column="39" selection-end-line="81" selection-end-column="39" /> - <folding /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="388" max-vertical-offset="1632"> @@ -2864,7 +2806,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="286" max-vertical-offset="1530"> <caret line="64" column="7" selection-start-line="64" selection-start-column="7" selection-end-line="64" selection-end-column="7" /> - <folding /> </state> </provider> </entry> @@ -2875,57 +2816,105 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="388" max-vertical-offset="2244"> - <caret line="72" column="48" selection-start-line="72" selection-start-column="48" selection-end-line="72" selection-end-column="48" /> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122"> + <caret line="38" column="51" selection-start-line="38" selection-start-column="51" selection-end-line="38" selection-end-column="51" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> + <entry file="file://$PROJECT_DIR$/settings.gradle"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="643" max-vertical-offset="1785"> - <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="272"> + <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="303" max-vertical-offset="2720"> - <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1984" max-vertical-offset="5967"> + <caret line="168" column="14" selection-start-line="168" selection-start-column="11" selection-end-line="168" selection-end-column="20" /> <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/com/sun/syndication/feed/synd/SyndEntry.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1117" max-vertical-offset="2176"> - <caret line="142" column="42" selection-start-line="142" selection-start-column="7" selection-end-line="142" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="558" max-vertical-offset="3264"> - <caret line="103" column="97" selection-start-line="103" selection-start-column="41" selection-end-line="103" selection-end-column="41" /> + <state vertical-scroll-proportion="0.0" vertical-offset="2575" max-vertical-offset="6290"> + <caret line="184" column="9" selection-start-line="184" selection-start-column="9" selection-end-line="184" selection-end-column="9" /> <folding /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="133" max-vertical-offset="1989"> - <caret line="53" column="7" selection-start-line="53" selection-start-column="7" selection-end-line="53" selection-end-column="7" /> + <state vertical-scroll-proportion="0.0" vertical-offset="493" max-vertical-offset="1989"> + <caret line="61" column="54" selection-start-line="61" selection-start-column="54" selection-end-line="61" selection-end-column="54" /> <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> + <entry file="file://$PROJECT_DIR$/build.gradle"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122"> - <caret line="38" column="51" selection-start-line="38" selection-start-column="51" selection-end-line="38" selection-end-column="51" /> + <state vertical-scroll-proportion="0.0" vertical-offset="306" max-vertical-offset="2057"> + <caret line="44" column="88" selection-start-line="44" selection-start-column="0" selection-end-line="44" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/log4j.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="425"> + <caret line="18" column="1" selection-start-line="18" selection-start-column="1" selection-end-line="18" selection-end-column="1" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1275" max-vertical-offset="3264"> + <caret line="132" column="36" selection-start-line="132" selection-start-column="36" selection-end-line="132" selection-end-column="36" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1581" max-vertical-offset="2244"> + <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="901" max-vertical-offset="1564"> + <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/PircBot.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.3323077" vertical-offset="39751" max-vertical-offset="50932"> + <caret line="2424" column="18" selection-start-line="2424" selection-start-column="18" selection-end-line="2424" selection-end-column="18" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/mobibot.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="374"> + <caret line="16" column="7" selection-start-line="16" selection-start-column="7" selection-end-line="16" selection-end-column="7" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1783" max-vertical-offset="2720"> + <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> <folding /> </state> </provider> @@ -2938,37 +2927,110 @@ </state> </provider> </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="391" max-vertical-offset="1343"> + <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> + <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="170" max-vertical-offset="1377"> + <caret line="17" column="99" selection-start-line="17" selection-start-column="99" selection-end-line="17" selection-end-column="99" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="848" max-vertical-offset="1785"> + <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1256" max-vertical-offset="2193"> + <caret line="129" column="0" selection-start-line="129" selection-start-column="0" selection-end-line="129" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="3587"> - <caret line="25" column="78" selection-start-line="25" selection-start-column="72" selection-end-line="25" selection-end-column="72" /> + <caret line="35" column="68" selection-start-line="35" selection-start-column="0" selection-end-line="35" selection-end-column="0" /> + <folding> + <element signature="e#0#4562#0" expanded="true" /> + <element signature="imports" expanded="true" /> + <element signature="e#2721#2722#0" expanded="true" /> + <element signature="e#2737#2738#0" expanded="true" /> + <element signature="e#2850#2851#0" expanded="true" /> + <element signature="e#2871#2872#0" expanded="true" /> + <element signature="e#3011#3012#0" expanded="true" /> + <element signature="e#3031#3032#0" expanded="true" /> + <element signature="e#3184#3185#0" expanded="true" /> + <element signature="e#3206#3207#0" expanded="true" /> + <element signature="e#3333#3334#0" expanded="true" /> + <element signature="e#3356#3357#0" expanded="true" /> + <element signature="e#3474#3475#0" expanded="true" /> + <element signature="e#3494#3495#0" expanded="true" /> + <element signature="e#3697#3698#0" expanded="true" /> + <element signature="e#3778#3779#0" expanded="true" /> + <element signature="e#3948#3949#0" expanded="true" /> + <element signature="e#3980#3981#0" expanded="true" /> + <element signature="e#4131#4132#0" expanded="true" /> + <element signature="e#4155#4156#0" expanded="true" /> + <element signature="e#4298#4299#0" expanded="true" /> + <element signature="e#4322#4323#0" expanded="true" /> + <element signature="e#4393#4394#0" expanded="true" /> + <element signature="e#4417#4418#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-12.035714" vertical-offset="530" max-vertical-offset="3825"> + <caret line="83" column="36" selection-start-line="83" selection-start-column="31" selection-end-line="83" selection-end-column="41" /> <folding /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="529" max-vertical-offset="5916"> - <caret line="93" column="30" selection-start-line="93" selection-start-column="25" selection-end-line="93" selection-end-column="36" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="306" max-vertical-offset="2057"> - <caret line="47" column="81" selection-start-line="47" selection-start-column="1" selection-end-line="47" selection-end-column="1" /> - <folding /> + <state vertical-scroll-proportion="0.0" vertical-offset="238" max-vertical-offset="6154"> + <caret line="72" column="47" selection-start-line="72" selection-start-column="47" selection-end-line="72" selection-end-column="47" /> + <folding> + <element signature="imports" expanded="true" /> + </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.88023955" vertical-offset="26215" max-vertical-offset="46869"> - <caret line="1619" column="71" selection-start-line="1619" selection-start-column="63" selection-end-line="1619" selection-end-column="63" /> + <state vertical-scroll-proportion="0.5952381" vertical-offset="34796" max-vertical-offset="47005"> + <caret line="2114" column="36" selection-start-line="2114" selection-start-column="36" selection-end-line="2114" selection-end-column="36" /> <folding> - <element signature="e#0#67752#0" expanded="true" /> + <element signature="e#0#67784#0" expanded="true" /> <element signature="imports" expanded="true" /> + <element signature="e#5390#5398#0" expanded="true" /> + <element signature="e#5546#5559#0" expanded="true" /> + <element signature="e#5715#5726#0" expanded="true" /> + <element signature="e#5945#5953#0" expanded="true" /> + <element signature="e#6054#6062#0" expanded="true" /> + <element signature="e#31758#31766#0" expanded="true" /> </folding> </state> </provider> diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index e1d00af..91ac6db 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -62,6 +62,11 @@ public class EntriesMgr */ public static final String NAV_XML = "nav.xml"; + /** + * The .xml extension + */ + public static final String XML_EXT = ".xml"; + /** * The maximum number of backlogs to keep. */ @@ -223,7 +228,7 @@ public class EntriesMgr { if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Saving..."); + bot.getLogger().debug("Saving the feeds..."); } if (Utils.isValidString(bot.getLogsDir()) && Utils.isValidString(bot.getWeblogUrl())) @@ -302,7 +307,7 @@ public class EntriesMgr output.output(rss, fw); fw.close(); - fw = new FileWriter(new File(bot.getLogsDir() + bot.getToday() + ".xml")); + fw = new FileWriter(new File(bot.getLogsDir() + bot.getToday() + XML_EXT)); output.output(rss, fw); if (isDayBackup) @@ -362,7 +367,7 @@ public class EntriesMgr } catch (Exception e) { - bot.getLogger().warn("Unable to generate the feed.", e); + bot.getLogger().warn("Unable to generate the entries feed.", e); } finally { @@ -381,7 +386,8 @@ public class EntriesMgr } else { - bot.getLogger().warn("Unable to generate the feed. At least one of the required property is missing."); + bot.getLogger() + .warn("Unable to generate the entries feed. At least one of the required property is missing."); } } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index f566027..cf0258f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -223,7 +223,7 @@ public class Mobibot extends PircBot /** * The logger. */ - private final Log4JLogger logger; + private final Log4JLogger logger = new Log4JLogger(Mobibot.class.getPackage().getName()); /** * The logger default level. @@ -333,8 +333,7 @@ public class Mobibot extends PircBot this.logsDir = logsDir; this.serializedObject = logsDir + getName() + SER_EXT; - // Set the logger - logger = new Log4JLogger(Mobibot.class.getPackage().getName()); + // Set the logger level loggerLevel = logger.getLogger().getLevel(); // Initialization @@ -807,7 +806,7 @@ public class Mobibot extends PircBot */ final void action(String action) { - action(getChannel(), action); + action(channel, action); } /** @@ -840,7 +839,7 @@ public class Mobibot extends PircBot try { final Calculable calc = new ExpressionBuilder(args).build(); - send(getChannel(), args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.calculate())); + send(channel, args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.calculate())); } catch (Exception e) { @@ -849,7 +848,7 @@ public class Mobibot extends PircBot logger.debug("Unable to calculate: " + message, e); } - send(getChannel(), "No idea. This is the kind of math I don't get."); + send(channel, "No idea. This is the kind of math I don't get."); } } else @@ -1065,10 +1064,10 @@ public class Mobibot extends PircBot send(sender, "To list or search the current URL posts:"); send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); } - else if (lcTopic.endsWith(getChannel().substring(1).toLowerCase())) + else if (lcTopic.endsWith(channel.substring(1).toLowerCase())) { send(sender, "To list the last 5 posts from the channel's weblog:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + getChannel().substring(1))); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + channel.substring(1))); } else if (lcTopic.endsWith(Commands.GOOGLE_CMD)) { @@ -1215,7 +1214,7 @@ public class Mobibot extends PircBot } else { - send(sender, Utils.bold("Type a URL on " + getChannel() + " to post it.")); + send(sender, Utils.bold("Type a URL on " + channel + " to post it.")); send(sender, "For more information on specific command, type:"); send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.HELP_CMD + " <command>")); send(sender, "The commands are:"); @@ -1228,7 +1227,7 @@ public class Mobibot extends PircBot cmds.add(Commands.IGNORE_CMD); cmds.add(Commands.INFO_CMD); cmds.add(Commands.LOOKUP_CMD); - cmds.add(getChannel().substring(1)); + cmds.add(channel.substring(1)); cmds.add(Commands.HELP_POSTING_KEYWORD); cmds.add(Commands.QUOTE_CMD); cmds.add(Commands.RECAP_CMD); @@ -1405,7 +1404,7 @@ public class Mobibot extends PircBot */ private boolean isOp(String sender) { - final User[] users = getUsers(getChannel()); + final User[] users = getUsers(channel); for (final User user : users) { @@ -1441,7 +1440,7 @@ public class Mobibot extends PircBot { try { - send(getChannel(), Lookup.lookup(query)); + send(channel, Lookup.lookup(query)); } catch (UnknownHostException ignore) { @@ -1462,13 +1461,13 @@ public class Mobibot extends PircBot if ((line.length() > 0) && (line.charAt(0) != '#')) { - send(getChannel(), line); + send(channel, line); } } } else { - send(getChannel(), "Unknown host."); + send(channel, "Unknown host."); } } catch (IOException ioe) @@ -1478,12 +1477,12 @@ public class Mobibot extends PircBot logger.debug("Unable to perform whois IP lookup: " + query, ioe); } - send(getChannel(), "Unable to perform whois IP lookup: " + ioe.getMessage()); + send(channel, "Unable to perform whois IP lookup: " + ioe.getMessage()); } } else { - send(getChannel(), "Unknown host."); + send(channel, "Unknown host."); } } } @@ -1550,7 +1549,7 @@ public class Mobibot extends PircBot sendMessage(identNick, identMsg); } - joinChannel(getChannel()); + joinChannel(channel); } @Override @@ -1726,19 +1725,19 @@ public class Mobibot extends PircBot // mobibot: dice else if (cmd.equals(Commands.DICE_CMD)) { - send(getChannel(), shall_we_play_a_game); + send(channel, shall_we_play_a_game); Dice.roll(this, sender); } // mobibot: war else if (cmd.equals(Commands.WAR_CMD)) { - send(getChannel(), shall_we_play_a_game); + send(channel, shall_we_play_a_game); War.play(this, sender); } // mobibot: <channel> - else if (cmd.equalsIgnoreCase(getChannel().substring(1))) + else if (cmd.equalsIgnoreCase(channel.substring(1))) { feedResponse(sender); } @@ -1820,11 +1819,11 @@ public class Mobibot extends PircBot if (cmd.length() == 0) { final EntryLink entry = entries.get(index); - send(getChannel(), Utils.buildLink(index, entry)); + send(channel, Utils.buildLink(index, entry)); if (entry.hasTags()) { - send(getChannel(), Utils.buildTags(index, entry)); + send(channel, Utils.buildTags(index, entry)); } if (entry.hasComments()) @@ -1833,7 +1832,7 @@ public class Mobibot extends PircBot for (int i = 0; i < comments.length; i++) { - send(getChannel(), Utils.buildComment(index, i, comments[i])); + send(channel, Utils.buildComment(index, i, comments[i])); } } } @@ -1852,7 +1851,7 @@ public class Mobibot extends PircBot } entries.remove(index); - send(getChannel(), "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); + send(channel, "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); saveEntries(false); } else @@ -1873,7 +1872,7 @@ public class Mobibot extends PircBot delicious.updatePost(entry.getLink(), entry); } - send(getChannel(), Utils.buildLink(index, entry)); + send(channel, Utils.buildLink(index, entry)); saveEntries(false); } } @@ -1897,7 +1896,7 @@ public class Mobibot extends PircBot delicious.updatePost(oldLink, entry); } - send(getChannel(), Utils.buildLink(index, entry)); + send(channel, Utils.buildLink(index, entry)); saveEntries(false); } } @@ -1915,7 +1914,7 @@ public class Mobibot extends PircBot { final EntryLink entry = entries.get(index); entry.setNick(cmd.substring(1)); - send(getChannel(), Utils.buildLink(index, entry)); + send(channel, Utils.buildLink(index, entry)); saveEntries(false); } } @@ -1961,7 +1960,7 @@ public class Mobibot extends PircBot delicious.updatePost(entry.getLink(), entry); } - send(getChannel(), Utils.buildTags(index, entry)); + send(channel, Utils.buildTags(index, entry)); saveEntries(false); } else @@ -1973,7 +1972,7 @@ public class Mobibot extends PircBot { if (entry.hasTags()) { - send(getChannel(), Utils.buildTags(index, entry)); + send(channel, Utils.buildTags(index, entry)); } else { @@ -2003,13 +2002,13 @@ public class Mobibot extends PircBot if (cmd.length() == 0) { final EntryComment comment = entry.getComment(cindex); - send(getChannel(), Utils.buildComment(index, cindex, comment)); + send(channel, Utils.buildComment(index, cindex, comment)); } // L1.1:- else if ("-".equals(cmd)) { entry.deleteComment(cindex); - send(getChannel(), + send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); saveEntries(false); } @@ -2022,7 +2021,7 @@ public class Mobibot extends PircBot { final EntryComment comment = entry.getComment(cindex); comment.setNick(cmd.substring(1)); - send(getChannel(), Utils.buildComment(index, cindex, comment)); + send(channel, Utils.buildComment(index, cindex, comment)); saveEntries(false); } } @@ -2084,7 +2083,7 @@ public class Mobibot extends PircBot { if (isOp(sender)) { - send(getChannel(), sender + " has just signed my death sentence."); + send(channel, sender + " has just signed my death sentence."); saveEntries(true); sleep(3); quitServer("The Bot Is Out There!"); @@ -2093,11 +2092,11 @@ public class Mobibot extends PircBot } else if (cmd.equals(Commands.CYCLE_CMD)) { - send(getChannel(), sender + " has just asked me to leave. I'll be back!"); + send(channel, sender + " has just asked me to leave. I'll be back!"); sleep(0); - partChannel(getChannel()); + partChannel(channel); sleep(10); - joinChannel(getChannel()); + joinChannel(channel); } else if (cmd.equals(Commands.RECAP_CMD)) { @@ -2111,8 +2110,17 @@ public class Mobibot extends PircBot { if (isOp(sender)) { - history.add(0, args); - send(sender, history.toString(), true); + // e.g. 2014-04-01 + final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); + if (backlog.exists()) + { + history.add(0, args); + send(sender, history.toString(), true); + } + else + { + send(sender, "The specified log could not be found."); + } } } else if (cmd.startsWith(Commands.ME_CMD)) @@ -2142,7 +2150,7 @@ public class Mobibot extends PircBot { if (cmds.length > 1) { - send(getChannel(), args, true); + send(channel, args, true); } else { @@ -2222,7 +2230,7 @@ public class Mobibot extends PircBot @Override protected final void onAction(String sender, String login, String hostname, String target, String action) { - if (target.equals(getChannel())) + if (target.equals(channel)) { recap(sender, action, true); } @@ -2660,7 +2668,7 @@ public class Mobibot extends PircBot */ private void usersResponse(String sender, boolean isPrivate) { - final User[] users = getUsers(getChannel()); + final User[] users = getUsers(channel); final String[] nicks = new String[users.length]; for (int i = 0; i < users.length; i++) diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index ceb9de2..77ebdf9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -90,7 +90,7 @@ public class TellMessage implements Serializable */ public String getId() { - return this.id; + return id; } /** @@ -174,7 +174,7 @@ public class TellMessage implements Serializable */ public boolean isNotified() { - return this.isNotified; + return isNotified; } /** @@ -184,7 +184,7 @@ public class TellMessage implements Serializable */ public boolean isReceived() { - return this.isReceived; + return isReceived; } /** @@ -192,7 +192,7 @@ public class TellMessage implements Serializable */ public void setIsNotified() { - this.isNotified = true; + isNotified = true; } /** @@ -200,7 +200,7 @@ public class TellMessage implements Serializable */ public void setIsReceived() { - this.received = Calendar.getInstance().getTime(); - this.isReceived = true; + received = Calendar.getInstance().getTime(); + isReceived = true; } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index bb261e5..9e9b4aa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -127,6 +127,7 @@ public class TellMessagesMgr { logger.debug("Saving the messages."); } + output.writeObject(messages); } finally From ad6a0e25d8c0c80891456e910215c824879e7d84 Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Fri, 30 Jan 2015 01:22:49 -0800 Subject: [PATCH 012/842] Save messages after clean, if needed. --- .../net/thauvin/erik/mobibot/Mobibot.java | 261 +++++++++--------- 1 file changed, 134 insertions(+), 127 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index cf0258f..b779157 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -72,83 +72,11 @@ public class Mobibot extends PircBot */ public static final String NO_TITLE = "No Title"; - /** - * The serialized object file extension. - */ - private static final String SER_EXT = ".ser"; - - /** - * Shall we play a game? - */ - private static final String shall_we_play_a_game = "Shall we play a game?"; - - /** - * The info strings. - */ - private static final String[] INFO_STRS = { - ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() - + " by Erik C. Thauvin (erik@thauvin.net)", "http://www.mobitopia.org/mobibot/" - }; - - /** - * The version strings. - */ - private static final String[] VERSION_STRS = { - "Version: " + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + " (" + Utils.ISO_SDF - .format(ReleaseInfo.getBuildDate()) + ')', - "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System - .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System - .getProperty("java.runtime.version") + ')', - "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " - + System.getProperty("java.vm.info") + ')' - }; - - /** - * The maximum number of times the bot will try to reconnect, if disconnected. - */ - private static final int MAX_RECONNECT = 10; - - /** - * The default maximum number of entries to display. - */ - private static final int MAX_ENTRIES = 8; - - /** - * The default maximum recap entries. - */ - private static final int MAX_RECAP = 10; - - /** - * The double tab indent (8 spaces). - */ - private static final String DOUBLE_INDENT = " "; - - /** - * The link match string. - */ - private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - - /** - * The tags/categories marker. - */ - private static final String TAGS_MARKER = "tags:"; - - /** - * The start time. - */ - private static final long START_TIME = System.currentTimeMillis(); - /** * The default port. */ private static final int DEFAULT_PORT = 6667; - /** - * The number of milliseconds to delay between consecutive messages. - */ - private static final long MESSAGE_DELAY = 1000L; - /** * The default maximum number of days to keep {@link Commands#TELL_CMD} messages. */ @@ -171,20 +99,87 @@ public class Mobibot extends PircBot private int tellMaxSize = DEFAULT_TELL_MAX_SIZE; /** - * The recap array. + * The double tab indent (8 spaces). */ - private final List<String> recap = new ArrayList<String>(0); + private static final String DOUBLE_INDENT = " "; /** - * Processes the {@link Commands#TELL_CMD} messages queue. + * The info strings. */ - private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<TellMessage>(); + private static final String[] INFO_STRS = { + ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + + " by Erik C. Thauvin (erik@thauvin.net)", "http://www.mobitopia.org/mobibot/" + }; + + /** + * The link match string. + */ + private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; + + /** + * The default maximum number of entries to display. + */ + private static final int MAX_ENTRIES = 8; + + /** + * The default maximum recap entries. + */ + private static final int MAX_RECAP = 10; + + /** + * The maximum number of times the bot will try to reconnect, if disconnected. + */ + private static final int MAX_RECONNECT = 10; + + /** + * The number of milliseconds to delay between consecutive messages. + */ + private static final long MESSAGE_DELAY = 1000L; + + /** + * The serialized object file extension. + */ + private static final String SER_EXT = ".ser"; + + /** + * The start time. + */ + private static final long START_TIME = System.currentTimeMillis(); + + /** + * The tags/categories marker. + */ + private static final String TAGS_MARKER = "tags:"; + + /** + * The version strings. + */ + private static final String[] VERSION_STRS = { + "Version: " + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + " (" + Utils.ISO_SDF + .format(ReleaseInfo.getBuildDate()) + ')', + "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System + .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System + .getProperty("java.runtime.version") + ')', + "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " + + System.getProperty("java.vm.info") + ')' + }; + + /** + * Shall we play a game? + */ + private static final String shall_we_play_a_game = "Shall we play a game?"; /** * The main channel. */ private final String channel; + /** + * The currency converter. + */ + private final CurrencyConverter currencyConverter; + /** * The entries array. */ @@ -215,11 +210,6 @@ public class Mobibot extends PircBot */ private final String ircServer; - /** - * The currency converter. - */ - private final CurrencyConverter currencyConverter; - /** * The logger. */ @@ -235,11 +225,21 @@ public class Mobibot extends PircBot */ private final String logsDir; + /** + * The recap array. + */ + private final List<String> recap = new ArrayList<String>(0); + /** * The serialized object file. */ private final String serializedObject; + /** + * Processes the {@link Commands#TELL_CMD} messages queue. + */ + private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<TellMessage>(); + /** * Time command. */ @@ -265,6 +265,26 @@ public class Mobibot extends PircBot */ private String feedURL = ""; + /** + * The NickServ ident password. + */ + private String ident = ""; + + /** + * The ident message. + */ + private String identMsg = ""; + + /** + * The ident nick. + */ + private String identNick = ""; + + /** + * Today's date. + */ + private String today = Utils.today(); + /** * The Twitter consumer key. */ @@ -285,26 +305,6 @@ public class Mobibot extends PircBot */ private String twitterTokenSecret = ""; - /** - * The ident message. - */ - private String identMsg = ""; - - /** - * The ident nick. - */ - private String identNick = ""; - - /** - * The NickServ ident password. - */ - private String ident = ""; - - /** - * Today's date. - */ - private String today = Utils.today(); - /** * The weblog URL. */ @@ -623,11 +623,14 @@ public class Mobibot extends PircBot bot.sendMessage(identNick, identMsg); } + bot.joinChannel(channel); + // Load the messages queue bot.tellMessages.addAll(TellMessagesMgr.load(bot.getSerializedObject(), bot.getLogger())); - bot.cleanTellMessages(); - - bot.joinChannel(channel); + if (bot.cleanTellMessages()) + { + bot.saveTellMessages(); + } } } @@ -788,15 +791,25 @@ public class Mobibot extends PircBot /** * Cleans the {@link #tellMessages} messages queue. + * + * @return <code>True</code> if the queue was cleaned. */ - private void cleanTellMessages() + private boolean cleanTellMessages() { if (logger.isDebugEnabled()) { logger.debug("Cleaning the messages."); } - TellMessagesMgr.cleanTellMessages(tellMessages, tellMaxDays); + return TellMessagesMgr.cleanTellMessages(tellMessages, tellMaxDays); + } + + /** + * Saves the {@link #tellMessages} messages queue. + */ + private void saveTellMessages() + { + TellMessagesMgr.save(getSerializedObject(), tellMessages, logger); } /** @@ -921,6 +934,16 @@ public class Mobibot extends PircBot this.backLogsUrl = backLogsUrl; } + /** + * Returns the current channel. + * + * @return The current channel. + */ + public final String getChannel() + { + return channel; + } + /** * Returns the {@link FeedFetcherCache feed info cache}. * @@ -2008,8 +2031,7 @@ public class Mobibot extends PircBot else if ("-".equals(cmd)) { entry.deleteComment(cindex); - send(channel, - "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); + send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); saveEntries(false); } // L1.1:?<author> @@ -2383,24 +2405,6 @@ public class Mobibot extends PircBot } } - /** - * Saves the {@link #tellMessages} messages queue. - */ - private void saveTellMessages() - { - TellMessagesMgr.save(getSerializedObject(), tellMessages, logger); - } - - /** - * Returns the current channel. - * - * @return The current channel. - */ - public final String getChannel() - { - return channel; - } - /** * Responds with the last 10 public messages. * @@ -2626,7 +2630,10 @@ public class Mobibot extends PircBot } } - cleanTellMessages(); + if (cleanTellMessages()) + { + saveTellMessages(); + } } /** From d02377a491470b862aff5fcbdbfcaae558bd3b8a Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Fri, 30 Jan 2015 01:23:57 -0800 Subject: [PATCH 013/842] Added doLast to deploy task. --- build.gradle | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 587ce85..2d9aedc 100644 --- a/build.gradle +++ b/build.gradle @@ -95,18 +95,20 @@ task copyToDeploy(type: Copy) { } task copyToDeployLib(type: Copy) { - configurations.runtime + from configurations.runtime into deployDir + '/lib' } -task deploy(dependsOn: ['build', 'copyToDeploy', 'copyToDeployLib']) { +task deploy(dependsOn: ['build']) { description = "Copies all needed files to the ${deployDir} directory." group = "Publishing" outputs.dir deployDir inputs.files copyToDeploy inputs.files copyToDeployLib + doLast { + file(deployDir + '/logs').mkdir() + } mustRunAfter clean - file(deployDir + '/logs').mkdir() } task release(dependsOn: ['deploy', 'wrapper']) { From 0255fa537fa405836fb80c1721bac92bb147a46f Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Fri, 30 Jan 2015 01:28:26 -0800 Subject: [PATCH 014/842] Code clean/reformat. Updated copyright. --- mobibot.iml | 5 +- mobibot.ipr | 15 +- mobibot.iws | 1656 ++++++++--------- .../net/thauvin/erik/mobibot/Commands.java | 137 +- .../erik/mobibot/CurrencyConverter.java | 28 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 5 +- .../java/net/thauvin/erik/mobibot/Dice.java | 9 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 107 +- .../thauvin/erik/mobibot/EntryComment.java | 5 +- .../net/thauvin/erik/mobibot/EntryLink.java | 5 +- .../net/thauvin/erik/mobibot/FeedReader.java | 5 +- .../thauvin/erik/mobibot/GoogleSearch.java | 5 +- .../java/net/thauvin/erik/mobibot/Lookup.java | 7 +- .../net/thauvin/erik/mobibot/Mobibot.java | 57 +- .../java/net/thauvin/erik/mobibot/Quote.java | 5 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 24 +- .../net/thauvin/erik/mobibot/StockQuote.java | 5 +- .../net/thauvin/erik/mobibot/SwingWorker.java | 50 +- .../net/thauvin/erik/mobibot/TellMessage.java | 22 +- .../thauvin/erik/mobibot/TellMessagesMgr.java | 60 +- .../net/thauvin/erik/mobibot/Twitter.java | 48 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 12 +- .../java/net/thauvin/erik/mobibot/Utils.java | 425 +++-- .../java/net/thauvin/erik/mobibot/War.java | 6 +- .../net/thauvin/erik/mobibot/Weather.java | 24 +- .../net/thauvin/erik/mobibot/WorldTime.java | 14 +- 26 files changed, 1298 insertions(+), 1443 deletions(-) diff --git a/mobibot.iml b/mobibot.iml index 66b7d51..bce15a1 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.path="K:\java\mobibot" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6" relativePaths="false" type="JAVA_MODULE" version="4"> +<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6" relativePaths="false" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> @@ -31,5 +31,4 @@ <orderEntry type="library" exported="" name="Gradle: exp4j-0.3.11" level="project" /> <orderEntry type="library" exported="" name="Gradle: utils-1.07.00" level="project" /> </component> -</module> - +</module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index c5eb45b..fdac789 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -72,20 +72,24 @@ </component> <component name="CopyrightManager" default="Mobibot"> <copyright> - <option name="notice" value="@(#)&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> + <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> <option name="keyword" value="Copyright" /> <option name="allowReplaceKeyword" value="Erik C. Thauvin" /> <option name="myName" value="Mobibot" /> <option name="myLocal" value="true" /> </copyright> <module2copyright> - <element module="All" copyright="Mobibot" /> + <element module="Source" copyright="Mobibot" /> </module2copyright> + <LanguageOptions name="__TEMPLATE__"> + <option name="addBlankAfter" value="false" /> + </LanguageOptions> </component> <component name="DependenciesAnalyzeManager"> <option name="myForwardDirection" value="false" /> </component> <component name="DependencyValidationManager"> + <scope name="Source" pattern="file:src/main/java/net/thauvin/erik/mobibot/*&&!file:src/main/java/net/thauvin/erik/mobibot/SwingWorker.java&&!file:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> <option name="SKIP_IMPORT_STATEMENTS" value="false" /> </component> <component name="EclipseCompilerSettings"> @@ -120,7 +124,7 @@ <option name="gradleHome" value="C:/gradle" /> <option name="modules"> <set> - <option value="K:\java\mobibot" /> + <option value="$PROJECT_DIR$" /> </set> </option> </GradleProjectSettings> @@ -651,7 +655,7 @@ <module fileurl="file://$PROJECT_DIR$/mobibot.iml" filepath="$PROJECT_DIR$/mobibot.iml" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="1.7.x" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="1.8.x" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/out" /> </component> <component name="ResourceManagerContainer"> @@ -840,5 +844,4 @@ </SOURCES> </library> </component> -</project> - +</project> \ No newline at end of file diff --git a/mobibot.iws b/mobibot.iws index 5a560ff..05024de 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="AnalysisUIOptions"> + <option name="SCOPE_TYPE" value="8" /> <option name="CUSTOM_SCOPE_NAME" value="Source" /> </component> <component name="CCaseConfig"> @@ -33,14 +34,40 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iml" afterPath="$PROJECT_DIR$/mobibot.iml" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.ipr" afterPath="$PROJECT_DIR$/mobibot.ipr" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> </list> <ignored path="$USER_HOME$/.griffon/" /> <ignored path="$USER_HOME$/.grails/" /> + <ignored path="$PROJECT_DIR$/out/" /> + <ignored path="$PROJECT_DIR$/.gradle/" /> + <ignored path="$PROJECT_DIR$/build/" /> + <ignored path=".idea/dataSources.local.xml" /> + <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" /> <option name="TRACKING_ENABLED" value="true" /> <option name="SHOW_DIALOG" value="false" /> <option name="HIGHLIGHT_CONFLICTS" value="true" /> @@ -72,6 +99,7 @@ <disable_hints /> </component> <component name="DebuggerManager"> + <ui_properties converted="true" /> <breakpoint_any default_suspend_policy="SuspendAll" default_condition_enabled="true" converted="true"> <breakpoint> <option name="NOTIFY_CAUGHT" value="true" /> @@ -109,7 +137,6 @@ </breakpoint> </breakpoint_any> <breakpoint_rules converted="true" /> - <ui_properties converted="true" /> </component> <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> <component name="FavoritesManager"> @@ -140,138 +167,108 @@ </component> <component name="FileEditorManager"> <leaf> - <file leaf-file-name="Mobibot.java" pinned="false" current="true" current-in-tab="true"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <file leaf-file-name="FeedReader.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.5952381" vertical-offset="34796" max-vertical-offset="47005"> - <caret line="2114" column="36" selection-start-line="2114" selection-start-column="36" selection-end-line="2114" selection-end-column="36" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1216" max-vertical-offset="1456"> + <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="GoogleSearch.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="560" max-vertical-offset="1520"> + <caret line="74" column="49" selection-start-line="74" selection-start-column="49" selection-end-line="74" selection-end-column="49" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="Lookup.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="112" max-vertical-offset="2080"> + <caret line="43" column="2" selection-start-line="43" selection-start-column="2" selection-end-line="43" selection-end-column="2" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="CurrencyConverter.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1552" max-vertical-offset="3184"> + <caret line="132" column="36" selection-start-line="132" selection-start-column="36" selection-end-line="132" selection-end-column="36" /> <folding> - <element signature="e#0#67784#0" expanded="true" /> <element signature="imports" expanded="true" /> - <element signature="e#5390#5398#0" expanded="true" /> - <element signature="e#5546#5559#0" expanded="true" /> - <element signature="e#5715#5726#0" expanded="true" /> - <element signature="e#5945#5953#0" expanded="true" /> - <element signature="e#6054#6062#0" expanded="true" /> - <element signature="e#31758#31766#0" expanded="true" /> </folding> </state> </provider> </entry> </file> - <file leaf-file-name="Weather.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> + <file leaf-file-name="EntryComment.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="619" max-vertical-offset="2669"> - <caret line="98" column="59" selection-start-line="98" selection-start-column="7" selection-end-line="98" selection-end-column="7" /> - <folding /> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1968"> + <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> + <folding> + <element signature="imports" expanded="true" /> + </folding> </state> </provider> </entry> </file> - <file leaf-file-name="War.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="391" max-vertical-offset="1343"> - <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Commands.java" pinned="false" current="false" current-in-tab="false"> + <file leaf-file-name="Commands.java" pinned="false" current-in-tab="true"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-12.035714" vertical-offset="530" max-vertical-offset="3825"> - <caret line="83" column="36" selection-start-line="83" selection-start-column="31" selection-end-line="83" selection-end-column="41" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Utils.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="4741" max-vertical-offset="5678"> - <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="TwitterOAuth.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="170" max-vertical-offset="1377"> - <caret line="17" column="99" selection-start-line="17" selection-start-column="99" selection-end-line="17" selection-end-column="99" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Twitter.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="848" max-vertical-offset="1785"> - <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="EntriesMgr.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="238" max-vertical-offset="6154"> - <caret line="72" column="47" selection-start-line="72" selection-start-column="47" selection-end-line="72" selection-end-column="47" /> + <state vertical-scroll-proportion="0.96961063" vertical-offset="2931" max-vertical-offset="4160"> + <caret line="247" column="37" selection-start-line="247" selection-start-column="37" selection-end-line="247" selection-end-column="37" /> <folding> - <element signature="imports" expanded="true" /> + <element signature="e#0#5577#0" expanded="true" /> </folding> </state> </provider> </entry> </file> - <file leaf-file-name="TellMessagesMgr.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <file leaf-file-name="Quote.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1256" max-vertical-offset="2193"> - <caret line="129" column="0" selection-start-line="129" selection-start-column="0" selection-end-line="129" selection-end-column="0" /> + <state vertical-scroll-proportion="0.0" vertical-offset="512" max-vertical-offset="1280"> + <caret line="70" column="47" selection-start-line="70" selection-start-column="47" selection-end-line="70" selection-end-column="47" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="TellMessage.java" pinned="false" current="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> + <file leaf-file-name="SwingWorker.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="3587"> - <caret line="35" column="68" selection-start-line="35" selection-start-column="0" selection-end-line="35" selection-end-column="0" /> - <folding> - <element signature="e#0#4562#0" expanded="true" /> - <element signature="imports" expanded="true" /> - <element signature="e#2721#2722#0" expanded="true" /> - <element signature="e#2737#2738#0" expanded="true" /> - <element signature="e#2850#2851#0" expanded="true" /> - <element signature="e#2871#2872#0" expanded="true" /> - <element signature="e#3011#3012#0" expanded="true" /> - <element signature="e#3031#3032#0" expanded="true" /> - <element signature="e#3184#3185#0" expanded="true" /> - <element signature="e#3206#3207#0" expanded="true" /> - <element signature="e#3333#3334#0" expanded="true" /> - <element signature="e#3356#3357#0" expanded="true" /> - <element signature="e#3474#3475#0" expanded="true" /> - <element signature="e#3494#3495#0" expanded="true" /> - <element signature="e#3697#3698#0" expanded="true" /> - <element signature="e#3778#3779#0" expanded="true" /> - <element signature="e#3948#3949#0" expanded="true" /> - <element signature="e#3980#3981#0" expanded="true" /> - <element signature="e#4131#4132#0" expanded="true" /> - <element signature="e#4155#4156#0" expanded="true" /> - <element signature="e#4298#4299#0" expanded="true" /> - <element signature="e#4322#4323#0" expanded="true" /> - <element signature="e#4393#4394#0" expanded="true" /> - <element signature="e#4417#4418#0" expanded="true" /> - </folding> + <state vertical-scroll-proportion="0.0" vertical-offset="1728" max-vertical-offset="2432"> + <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="ReleaseInfo.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1040" max-vertical-offset="1184"> + <caret line="112" column="5" selection-start-line="112" selection-start-column="5" selection-end-line="112" selection-end-column="5" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="StockQuote.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1936" max-vertical-offset="2096"> + <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> + <folding /> </state> </provider> </entry> @@ -314,6 +311,34 @@ </ExternalSystemSettings> </option> </ExternalTaskExecutionInfo> + <ExternalTaskExecutionInfo> + <option name="executorId" value="Run" /> + <option name="settings"> + <ExternalSystemSettings> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="taskNames"> + <list> + <option value="copyToDeployLib" /> + </list> + </option> + </ExternalSystemSettings> + </option> + </ExternalTaskExecutionInfo> + <ExternalTaskExecutionInfo> + <option name="executorId" value="Run" /> + <option name="settings"> + <ExternalSystemSettings> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="taskNames"> + <list> + <option value="clean" /> + </list> + </option> + </ExternalSystemSettings> + </option> + </ExternalTaskExecutionInfo> <ExternalTaskExecutionInfo> <option name="executorId" value="Run" /> <option name="settings"> @@ -385,7 +410,7 @@ <list> <ExternalProjectPojo> <option name="name" value="mobibot" /> - <option name="path" value="K:\java\mobibot" /> + <option name="path" value="$PROJECT_DIR$" /> </ExternalProjectPojo> </list> </value> @@ -522,139 +547,11 @@ </list> </value> </entry> - <entry key="K:\java\mobibot"> - <value> - <list> - <ExternalTaskPojo> - <option name="description" value="Assembles the outputs of this project." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="assemble" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="build" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project and all projects that depend on it." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="buildDependents" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project and all projects it depends on." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="buildNeeded" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs all checks." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="check" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles classes 'main'." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="classes" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Deletes the build directory." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="clean" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'main:java'." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="compileJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'test:java'." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="compileTestJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="copyToDeploy" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="copyToDeployLib" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Copies all needed files to the deploy directory." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="deploy" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="distTar" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="distZip" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="installApp" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles a jar archive containing the main classes." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="jar" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Generates Javadoc API documentation for the main source code." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="javadoc" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Processes resources 'main:resources'." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="processResources" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Processes resources 'test:resources'." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="processTestResources" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Releases new version." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="release" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs this project as a JVM application" /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="run" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Creates OS specific scripts to run the project as a JVM application." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="startScripts" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs the unit tests." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="test" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles classes 'test'." /> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="testClasses" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="K:\java\mobibot" /> - <option name="name" value="wrapper" /> - </ExternalTaskPojo> - </list> - </value> - </entry> </map> </option> <option name="modificationStamps"> <map> - <entry key="$PROJECT_DIR$" value="2796764538501" /> + <entry key="$PROJECT_DIR$" value="2797270596074" /> </map> </option> <option name="projectBuildClasspath"> @@ -667,6 +564,46 @@ <entry key="$PROJECT_DIR$"> <value> <ExternalModuleBuildClasspathPojo> + <option name="entries"> + <list> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/9ce04e34240f674bc72680f8b843b1457383161a/commons-codec-1.9.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/28bb0405fddaf04f15058fbfbe01fe2780d7d3b6/commons-logging-1.1.3-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f/commons-logging-1.1.3.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/4c85b39e7f03471338bf7d36558eefe1e463e3de/commons-net-1.4.1-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/abb932adb2c10790c1eaa4365d3ac2a1ac7cb700/commons-net-1.4.1.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/6c5459816530a1962ac18cd315cc775b1b384329/commons-cli-1.2-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/61abb5ef010830519e47ac56faade402807b245b/exp4j-0.3.11-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/150e7b4a77af47b03a1b65be7a01bd663ea64420/exp4j-0.3.11.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar" /> + </list> + </option> <option name="path" value="$PROJECT_DIR$" /> </ExternalModuleBuildClasspathPojo> </value> @@ -683,48 +620,86 @@ <option name="name" value="mobibot" /> <option name="projectBuildClasspath"> <list> - <option value="C:/gradle/lib/ant-1.9.2.jar" /> - <option value="C:/gradle/lib/ant-launcher-1.9.2.jar" /> - <option value="C:/gradle/lib/gradle-base-services-1.11.jar" /> - <option value="C:/gradle/lib/gradle-base-services-groovy-1.11.jar" /> - <option value="C:/gradle/lib/gradle-cli-1.11.jar" /> - <option value="C:/gradle/lib/gradle-core-1.11.jar" /> - <option value="C:/gradle/lib/gradle-docs-1.11.jar" /> - <option value="C:/gradle/lib/gradle-launcher-1.11.jar" /> - <option value="C:/gradle/lib/gradle-messaging-1.11.jar" /> - <option value="C:/gradle/lib/gradle-native-1.11.jar" /> - <option value="C:/gradle/lib/gradle-open-api-1.11.jar" /> - <option value="C:/gradle/lib/gradle-resources-1.11.jar" /> - <option value="C:/gradle/lib/gradle-tooling-api-1.11.jar" /> - <option value="C:/gradle/lib/gradle-ui-1.11.jar" /> - <option value="C:/gradle/lib/gradle-wrapper-1.11.jar" /> + <option value="C:/gradle/src/announce" /> + <option value="C:/gradle/src/antlr" /> + <option value="C:/gradle/src/base-services" /> + <option value="C:/gradle/src/base-services-groovy" /> + <option value="C:/gradle/src/build-comparison" /> + <option value="C:/gradle/src/build-init" /> + <option value="C:/gradle/src/cli" /> + <option value="C:/gradle/src/code-quality" /> + <option value="C:/gradle/src/core" /> + <option value="C:/gradle/src/core-impl" /> + <option value="C:/gradle/src/cpp" /> + <option value="C:/gradle/src/diagnostics" /> + <option value="C:/gradle/src/ear" /> + <option value="C:/gradle/src/ide" /> + <option value="C:/gradle/src/internal-integ-testing" /> + <option value="C:/gradle/src/internal-testing" /> + <option value="C:/gradle/src/ivy" /> + <option value="C:/gradle/src/jacoco" /> + <option value="C:/gradle/src/javascript" /> + <option value="C:/gradle/src/jetty" /> + <option value="C:/gradle/src/language-base" /> + <option value="C:/gradle/src/language-jvm" /> + <option value="C:/gradle/src/launcher" /> + <option value="C:/gradle/src/maven" /> + <option value="C:/gradle/src/messaging" /> + <option value="C:/gradle/src/native" /> + <option value="C:/gradle/src/open-api" /> + <option value="C:/gradle/src/osgi" /> + <option value="C:/gradle/src/plugins" /> + <option value="C:/gradle/src/publish" /> + <option value="C:/gradle/src/reporting" /> + <option value="C:/gradle/src/resources" /> + <option value="C:/gradle/src/scala" /> + <option value="C:/gradle/src/signing" /> + <option value="C:/gradle/src/sonar" /> + <option value="C:/gradle/src/tooling-api" /> + <option value="C:/gradle/src/ui" /> + <option value="C:/gradle/src/wrapper" /> + <option value="C:/gradle/lib/ant-1.9.3.jar" /> + <option value="C:/gradle/lib/ant-launcher-1.9.3.jar" /> + <option value="C:/gradle/lib/gradle-base-services-1.12.jar" /> + <option value="C:/gradle/lib/gradle-base-services-groovy-1.12.jar" /> + <option value="C:/gradle/lib/gradle-cli-1.12.jar" /> + <option value="C:/gradle/lib/gradle-core-1.12.jar" /> + <option value="C:/gradle/lib/gradle-docs-1.12.jar" /> + <option value="C:/gradle/lib/gradle-launcher-1.12.jar" /> + <option value="C:/gradle/lib/gradle-messaging-1.12.jar" /> + <option value="C:/gradle/lib/gradle-native-1.12.jar" /> + <option value="C:/gradle/lib/gradle-open-api-1.12.jar" /> + <option value="C:/gradle/lib/gradle-resources-1.12.jar" /> + <option value="C:/gradle/lib/gradle-tooling-api-1.12.jar" /> + <option value="C:/gradle/lib/gradle-ui-1.12.jar" /> + <option value="C:/gradle/lib/gradle-wrapper-1.12.jar" /> <option value="C:/gradle/lib/groovy-all-1.8.6.jar" /> <option value="C:/gradle/lib/ivy-2.2.0.jar" /> - <option value="C:/gradle/lib/plugins/ant-antlr-1.9.2.jar" /> - <option value="C:/gradle/lib/plugins/gradle-announce-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-antlr-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-comparison-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-init-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-code-quality-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-core-impl-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-cpp-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-diagnostics-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ear-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ide-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ivy-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jacoco-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-javascript-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jetty-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-base-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-jvm-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-maven-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-osgi-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-plugins-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-publish-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-reporting-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-scala-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-signing-1.11.jar" /> - <option value="C:/gradle/lib/plugins/gradle-sonar-1.11.jar" /> + <option value="C:/gradle/lib/plugins/ant-antlr-1.9.3.jar" /> + <option value="C:/gradle/lib/plugins/gradle-announce-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-antlr-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-build-comparison-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-build-init-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-code-quality-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-core-impl-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-cpp-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-diagnostics-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ear-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ide-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ivy-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-jacoco-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-javascript-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-jetty-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-base-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-jvm-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-maven-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-osgi-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-plugins-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-publish-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-reporting-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-scala-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-signing-1.12.jar" /> + <option value="C:/gradle/lib/plugins/gradle-sonar-1.12.jar" /> <option value="$PROJECT_DIR$/buildSrc/src/main/java" /> <option value="$PROJECT_DIR$/buildSrc/src/main/groovy" /> </list> @@ -744,59 +719,10 @@ <setting file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" root0="FORCE_HIGHLIGHTING" /> </component> <component name="IdeDocumentHistory"> - <option name="changedFiles"> + <option name="CHANGED_PATHS"> <list> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/EntryComment.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/EntryLink.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/GoogleSearch.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/Identica.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/StockQuote.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/Twitter.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/TwitterOAuth.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/Weather.java" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/FeedReader.java" /> - <option value="$PROJECT_DIR$/build.xml" /> - <option value="$PROJECT_DIR$/src/net/thauvin/erik/mobibot/Mobibot.java" /> - <option value="$PROJECT_DIR$/README.txt" /> - <option value="K:/Gradle/build.gradle" /> - <option value="$PROJECT_DIR$/log4j.properties" /> - <option value="$PROJECT_DIR$/.gitignore" /> - <option value="$PROJECT_DIR$/src/main/resources/version.properties" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" /> - <option value="$PROJECT_DIR$/website/simple.css" /> - <option value="$PROJECT_DIR$/licenses/License.txt" /> - <option value="$PROJECT_DIR$/website/index.html" /> - <option value="$PROJECT_DIR$/properties/mobibot.properties" /> - <option value="$PROJECT_DIR$/buildnum.properties" /> - <option value="$PROJECT_DIR$/build.gradle" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Entries.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/MiscUtils.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Time.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TimeCmd.java" /> - <option value="$PROJECT_DIR$/deploy/mobibot.properties" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> - <option value="$PROJECT_DIR$/mobibot.properties" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> </list> </option> </component> @@ -809,6 +735,9 @@ <property name="poll-interval" value="500" /> <property name="external-in-idea" value="false" /> </component> + <component name="JsGulpfileManager"> + <detection-done>true</detection-done> + </component> <component name="MavenProjectNavigator"> <treeState /> </component> @@ -816,9 +745,6 @@ <option name="LAST_EDITED_MODULE_NAME" value="mobibot" /> <option name="LAST_EDITED_TAB_NAME" value="Libraries (Classpath)" /> </component> - <component name="NamedScopeManager"> - <scope name="Source" pattern="file:src/main/java/net/thauvin/erik/mobibot/*&&!file:src/main/java/net/thauvin/erik/mobibot/SwingWorker.java&&!file:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> - </component> <component name="PackagesPane"> <subPane> <PATH> @@ -855,10 +781,10 @@ <option name="port" value="jasper:1666" /> </component> <component name="ProjectFrameBounds"> - <option name="x" value="1703" /> - <option name="y" value="29" /> - <option name="width" value="1500" /> - <option name="height" value="1146" /> + <option name="x" value="1592" /> + <option name="y" value="-8" /> + <option name="width" value="1616" /> + <option name="height" value="1216" /> </component> <component name="ProjectInspectionProfilesVisibleTreeState"> <entry key="Default"> @@ -1554,14 +1480,6 @@ </navigator> <panes> <pane id="Scope"> - <subPane subId="Project Files"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> <subPane subId="All"> <PATH> <PATH_ELEMENT USER_OBJECT="Root"> @@ -1592,6 +1510,14 @@ </PATH_ELEMENT> </PATH> </subPane> + <subPane subId="Project Files"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> </pane> <pane id="PackagesPane"> <subPane> @@ -1640,7 +1566,7 @@ </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> </PATH> <PATH> @@ -1650,11 +1576,11 @@ </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="src" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> </PATH> <PATH> @@ -1664,15 +1590,15 @@ </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="src" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="main" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> </PATH> <PATH> @@ -1682,51 +1608,23 @@ </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="src" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="main" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="java" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> <PATH_ELEMENT> <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="properties" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="deploy" /> - <option name="myItemType" value="com.android.tools.idea.gradle.projectView.AndroidPsiDirectoryNode" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> </PATH_ELEMENT> </PATH> </subPane> @@ -1776,7 +1674,7 @@ <property name="vcs_file_view_flatWidth4" value="82" /> <property name="GoToFile.includeJavaFiles" value="false" /> <property name="RunManagerConfig.compileBeforeRunning" value="true" /> - <property name="options.lastSelected" value="preferences.sourceCode.Java" /> + <property name="options.lastSelected" value="copyright.profiles" /> <property name="project.structure.side.proportion" value="0.2" /> <property name="MemberChooser.copyJavadoc" value="false" /> <property name="project.structure.last.edited" value="Modules" /> @@ -1812,6 +1710,7 @@ <property name="OverrideImplement.combined" value="true" /> <property name="OverrideImplement.overriding.sorted" value="false" /> <property name="LayoutCode.rearrangeEntries" value="true" /> + <property name="aspect.path.notification.shown" value="true" /> </component> <component name="RecentsManager"> <key name="CopyClassDialog.RECENTS_KEY"> @@ -1841,65 +1740,9 @@ <option name="showLabels" value="true" /> </component> <component name="RunManager" selected="Application.Mobibot"> - <configuration default="false" name="mobibot [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="build" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> - <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> - <method /> - </configuration> - <configuration default="false" name="mobibot [deploy]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="deploy" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> - <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> - <method /> - </configuration> - <configuration default="false" name="mobibot [release]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="release" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> - <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> - <method /> - </configuration> <configuration default="false" name="mobibot [compileJava]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> + <option name="executionName" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalSystemIdString" value="GRADLE" /> <option name="scriptParameters" /> @@ -1919,6 +1762,7 @@ </configuration> <configuration default="false" name="mobibot [distZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> + <option name="executionName" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalSystemIdString" value="GRADLE" /> <option name="scriptParameters" /> @@ -1936,13 +1780,124 @@ <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> <method /> </configuration> - <configuration default="true" type="GroovyScriptRunConfiguration" factoryName="Groovy"> + <configuration default="false" name="mobibot [copyToDeployLib]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="copyToDeployLib" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> + <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> + <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> + <method /> + </configuration> + <configuration default="false" name="mobibot [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="clean" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> + <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> + <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> + <method /> + </configuration> + <configuration default="true" type="Remote" factoryName="Remote"> + <option name="USE_SOCKET_TRANSPORT" value="true" /> + <option name="SERVER_MODE" value="false" /> + <option name="SHMEM_ADDRESS" value="javadebug" /> + <option name="HOST" value="localhost" /> + <option name="PORT" value="5005" /> + <method /> + </configuration> + <configuration default="true" type="JUnit" factoryName="JUnit"> + <extension name="coverage" enabled="false" merge="false" runner="idea" /> + <extension name="snapshooter" /> <module name="" /> - <setting name="path" value="" /> - <setting name="vmparams" value="" /> - <setting name="params" value="" /> - <setting name="workDir" value="file://$PROJECT_DIR$" /> - <setting name="debug" value="false" /> + <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> + <option name="ALTERNATIVE_JRE_PATH" /> + <option name="PACKAGE_NAME" /> + <option name="MAIN_CLASS_NAME" /> + <option name="METHOD_NAME" /> + <option name="TEST_OBJECT" /> + <option name="VM_PARAMETERS" /> + <option name="PARAMETERS" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> + <option name="ENV_VARIABLES" /> + <option name="PASS_PARENT_ENVS" value="true" /> + <option name="TEST_SEARCH_SCOPE"> + <value defaultName="wholeProject" /> + </option> + <envs /> + <patterns /> + <method /> + </configuration> + <configuration default="true" type="AndroidRunConfigurationType" factoryName="Android Application"> + <module name="" /> + <option name="ACTIVITY_CLASS" value="" /> + <option name="MODE" value="default_activity" /> + <option name="DEPLOY" value="true" /> + <option name="ARTIFACT_NAME" value="" /> + <option name="TARGET_SELECTION_MODE" value="EMULATOR" /> + <option name="USE_LAST_SELECTED_DEVICE" value="false" /> + <option name="PREFERRED_AVD" value="" /> + <option name="USE_COMMAND_LINE" value="true" /> + <option name="COMMAND_LINE" value="" /> + <option name="WIPE_USER_DATA" value="false" /> + <option name="DISABLE_BOOT_ANIMATION" value="false" /> + <option name="NETWORK_SPEED" value="full" /> + <option name="NETWORK_LATENCY" value="none" /> + <option name="CLEAR_LOGCAT" value="false" /> + <option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" /> + <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> + <method /> + </configuration> + <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="PARENT_ENVS" value="true" /> + <envs /> + <module name="" /> + <option name="SCRIPT_NAME" value="" /> + <option name="PARAMETERS" value="" /> + <method /> + </configuration> + <configuration default="true" type="WebLogic Instance" factoryName="Local" ALTERNATIVE_JRE_ENABLED="false"> + <option name="PORT" value="7001" /> + <deployment /> + <server-settings> + <option name="DOMAIN_PATH" value="" /> + <option name="SERVER_NAME" value="myserver" /> + <option name="DOMAIN_NAME" value="mydomain" /> + <option name="ADMIN_SERVER_HOST" value="localhost" /> + <option name="ADMIN_SERVER_PORT" value="7001" /> + <option name="TARGET_TYPE_NAME" value="TYPE_SERVER" /> + <option name="CLUSTER_NAME" value="mycluster" /> + <option name="CONNECT_TO_MANAGED_SERVER" value="false" /> + <option name="USERNAME" value="weblogic" /> + <option name="PASSWORD" value="weblogic" /> + </server-settings> + <predefined_log_file id="WEBLOGIC_DOMAIN_LOG_FILE" enabled="true" /> + <predefined_log_file id="WEBLOGIC_SERVER_LOG_FILE" enabled="true" /> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <method /> </configuration> <configuration default="true" type="GrailsRunConfigurationType" factoryName="Grails"> @@ -1955,15 +1910,18 @@ <setting name="launchBrowser" value="false" /> <method /> </configuration> - <configuration default="true" type="Remote" factoryName="Remote"> - <option name="USE_SOCKET_TRANSPORT" value="true" /> - <option name="SERVER_MODE" value="false" /> - <option name="SHMEM_ADDRESS" value="javadebug" /> - <option name="HOST" value="localhost" /> - <option name="PORT" value="5005" /> + <configuration default="true" type="JarApplication" factoryName="JAR Application"> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <envs /> <method /> </configuration> - <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug"> + <configuration default="true" type="GroovyScriptRunConfiguration" factoryName="Groovy"> + <module name="" /> + <setting name="path" value="" /> + <setting name="vmparams" value="" /> + <setting name="params" value="" /> + <setting name="workDir" value="file://$PROJECT_DIR$" /> + <setting name="debug" value="false" /> <method /> </configuration> <configuration default="true" type="TestNG" factoryName="TestNG"> @@ -1995,58 +1953,47 @@ <listeners /> <method /> </configuration> - <configuration default="true" type="Applet" factoryName="Applet"> - <module name="" /> - <option name="MAIN_CLASS_NAME" /> - <option name="HTML_FILE_NAME" /> - <option name="HTML_USED" value="false" /> - <option name="WIDTH" value="400" /> - <option name="HEIGHT" value="300" /> - <option name="POLICY_FILE" value="C:/IntelliJ-IDEA/bin/appletviewer.policy" /> - <option name="VM_PARAMETERS" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <method /> - </configuration> - <configuration default="true" type="WebLogic Instance" factoryName="Local" ALTERNATIVE_JRE_ENABLED="false"> - <option name="PORT" value="7001" /> - <deployment /> - <server-settings> - <option name="DOMAIN_PATH" value="" /> - <option name="SERVER_NAME" value="myserver" /> - <option name="DOMAIN_NAME" value="mydomain" /> - <option name="ADMIN_SERVER_HOST" value="localhost" /> - <option name="ADMIN_SERVER_PORT" value="7001" /> - <option name="CLUSTER_NAME" value="mycluster" /> - <option name="CONNECT_TO_MANAGED_SERVER" value="false" /> - <option name="USERNAME" value="weblogic" /> - <option name="PASSWORD" value="weblogic" /> - </server-settings> - <predefined_log_file id="WEBLOGIC_DOMAIN_LOG_FILE" enabled="true" /> - <predefined_log_file id="WEBLOGIC_SERVER_LOG_FILE" enabled="true" /> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <method /> - </configuration> - <configuration default="true" type="JUnit" factoryName="JUnit"> + <configuration default="true" type="Application" factoryName="Application"> <extension name="coverage" enabled="false" merge="false" runner="idea" /> - <extension name="snapshooter" /> - <module name="" /> + <option name="MAIN_CLASS_NAME" /> + <option name="VM_PARAMETERS" /> + <option name="PROGRAM_PARAMETERS" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH" /> - <option name="PACKAGE_NAME" /> - <option name="MAIN_CLASS_NAME" /> - <option name="METHOD_NAME" /> - <option name="TEST_OBJECT" /> - <option name="VM_PARAMETERS" /> - <option name="PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> + <option name="ENABLE_SWING_INSPECTOR" value="false" /> <option name="ENV_VARIABLES" /> <option name="PASS_PARENT_ENVS" value="true" /> - <option name="TEST_SEARCH_SCOPE"> - <value defaultName="wholeProject" /> - </option> + <module name="" /> <envs /> - <patterns /> + <method /> + </configuration> + <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js"> + <method /> + </configuration> + <configuration default="true" type="BashConfigurationType" factoryName="Bash"> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="INTERPRETER_PATH" value="" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="PARENT_ENVS" value="true" /> + <option name="SCRIPT_NAME" value="" /> + <option name="PARAMETERS" value="" /> + <module name="" /> + <envs /> + <method /> + </configuration> + <configuration default="true" type="FlexUnitRunConfigurationType" factoryName="FlexUnit" appDescriptorForEmulator="Android" class_name="" emulatorAdlOptions="" method_name="" package_name="" scope="Class"> + <option name="BCName" value="" /> + <option name="launcherParameters"> + <LauncherParameters> + <option name="browser" value="a7bb68e0-33c0-4d6f-a81a-aac1fdb870c8" /> + <option name="launcherType" value="OSDefault" /> + <option name="newPlayerInstance" value="false" /> + <option name="playerPath" value="FlashPlayerDebugger.exe" /> + </LauncherParameters> + </option> + <option name="moduleName" value="" /> + <option name="trusted" value="true" /> <method /> </configuration> <configuration default="true" type="FlashRunConfigurationType" factoryName="Flash App"> @@ -2105,8 +2052,12 @@ <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> <method /> </configuration> + <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug"> + <method /> + </configuration> <configuration default="true" type="GradleRunConfiguration" factoryName="Gradle"> <ExternalSystemSettings> + <option name="executionName" /> <option name="externalProjectPath" /> <option name="externalSystemIdString" value="GRADLE" /> <option name="scriptParameters" /> @@ -2120,28 +2071,17 @@ </ExternalSystemSettings> <method /> </configuration> - <configuration default="true" type="FlexUnitRunConfigurationType" factoryName="FlexUnit" appDescriptorForEmulator="Android" class_name="" emulatorAdlOptions="" method_name="" package_name="" scope="Class"> - <option name="BCName" value="" /> - <option name="launcherParameters"> - <LauncherParameters> - <option name="browser" value="a7bb68e0-33c0-4d6f-a81a-aac1fdb870c8" /> - <option name="launcherType" value="OSDefault" /> - <option name="newPlayerInstance" value="false" /> - <option name="playerPath" value="FlashPlayerDebugger.exe" /> - </LauncherParameters> - </option> - <option name="moduleName" value="" /> - <option name="trusted" value="true" /> - <method /> - </configuration> - <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> - <option name="INTERPRETER_OPTIONS" value="" /> - <option name="WORKING_DIRECTORY" value="" /> - <option name="PARENT_ENVS" value="true" /> - <envs /> + <configuration default="true" type="Applet" factoryName="Applet"> <module name="" /> - <option name="SCRIPT_NAME" value="" /> - <option name="PARAMETERS" value="" /> + <option name="MAIN_CLASS_NAME" /> + <option name="HTML_FILE_NAME" /> + <option name="HTML_USED" value="false" /> + <option name="WIDTH" value="400" /> + <option name="HEIGHT" value="300" /> + <option name="POLICY_FILE" value="C:/IntelliJ-IDEA/bin/appletviewer.policy" /> + <option name="VM_PARAMETERS" /> + <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> + <option name="ALTERNATIVE_JRE_PATH" /> <method /> </configuration> <configuration default="true" type="CucumberJavaRunConfigurationType" factoryName="Cucumber java"> @@ -2163,42 +2103,8 @@ <envs /> <method /> </configuration> - <configuration default="true" type="Application" factoryName="Application"> - <extension name="coverage" enabled="false" merge="false" runner="idea" /> - <option name="MAIN_CLASS_NAME" /> - <option name="VM_PARAMETERS" /> - <option name="PROGRAM_PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="ENABLE_SWING_INSPECTOR" value="false" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <module name="" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="AndroidRunConfigurationType" factoryName="Android Application"> - <module name="" /> - <option name="ACTIVITY_CLASS" value="" /> - <option name="MODE" value="default_activity" /> - <option name="DEPLOY" value="true" /> - <option name="ARTIFACT_NAME" value="" /> - <option name="TARGET_SELECTION_MODE" value="EMULATOR" /> - <option name="USE_LAST_SELECTED_DEVICE" value="false" /> - <option name="PREFERRED_AVD" value="" /> - <option name="USE_COMMAND_LINE" value="true" /> - <option name="COMMAND_LINE" value="" /> - <option name="WIPE_USER_DATA" value="false" /> - <option name="DISABLE_BOOT_ANIMATION" value="false" /> - <option name="NETWORK_SPEED" value="full" /> - <option name="NETWORK_LATENCY" value="none" /> - <option name="CLEAR_LOGCAT" value="false" /> - <option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" /> - <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> - <method /> - </configuration> - <configuration default="false" name="Mobibot" type="Application" factoryName="Application"> + <configuration default="false" name="Mobibot" type="Application" factoryName="Application" show_console_on_std_out="true" show_console_on_std_err="true"> + <log_file path="$PROJECT_DIR$/mobibot.log" checked="true" skipped="true" show_all="false" alias="K:\java\mobibot\mobibot.log" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <option name="MAIN_CLASS_NAME" value="net.thauvin.erik.mobibot.Mobibot" /> <option name="VM_PARAMETERS" value="" /> @@ -2225,7 +2131,7 @@ <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <option name="MAIN_CLASS_NAME" value="net.thauvin.erik.mobibot.TwitterOAuth" /> <option name="VM_PARAMETERS" value="" /> - <option name="PROGRAM_PARAMETERS" value="i22OBdegJVWVEOEMokW1ug RN5BUWBqZcUETzRgsJe1MtbQ0UfYpVn8ckJ5Ljga9sY" /> + <option name="PROGRAM_PARAMETERS" value="" /> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH" value="" /> @@ -2238,22 +2144,20 @@ <ConfigurationWrapper RunnerId="Run" /> <method /> </configuration> - <list size="7"> + <list size="6"> <item index="0" class="java.lang.String" itemvalue="Application.Mobibot" /> <item index="1" class="java.lang.String" itemvalue="Application.TwitterOAuth" /> - <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [build]" /> - <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> - <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> - <item index="5" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> - <item index="6" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> + <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> + <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> + <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> + <item index="5" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> </list> <recent_temporary> - <list size="5"> - <item index="0" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> - <item index="1" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> - <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> - <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> - <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [build]" /> + <list size="4"> + <item index="0" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> + <item index="1" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> + <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> + <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> </list> </recent_temporary> </component> @@ -2326,6 +2230,7 @@ <task active="true" id="Default" summary="Default task"> <changelist id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment="" /> <created>1267745358497</created> + <option name="number" value="Default" /> <updated>1267745358497</updated> <workItem from="1387264882357" duration="735000" /> <workItem from="1387481199054" duration="134000" /> @@ -2391,11 +2296,33 @@ <workItem from="1398912049023" duration="768000" /> <workItem from="1398914151479" duration="538000" /> <workItem from="1398929482588" duration="5179000" /> + <workItem from="1399325436405" duration="6194000" /> + <workItem from="1399509126352" duration="26000" /> + <workItem from="1399592116678" duration="2183000" /> + <workItem from="1399618330807" duration="138000" /> + <workItem from="1400291768180" duration="1564000" /> + <workItem from="1400293761353" duration="2015000" /> + <workItem from="1400355710459" duration="1522000" /> + <workItem from="1400441036505" duration="68000" /> + <workItem from="1400460251513" duration="252000" /> + <workItem from="1400557767374" duration="78000" /> + <workItem from="1400738955715" duration="26000" /> + <workItem from="1400739268468" duration="73000" /> + <workItem from="1400740251061" duration="232000" /> + <workItem from="1400840140039" duration="712000" /> + <workItem from="1401258805459" duration="33000" /> + <workItem from="1401258976812" duration="149000" /> + <workItem from="1422520621496" duration="116000" /> + <workItem from="1422520824706" duration="103000" /> + <workItem from="1422522541534" duration="102000" /> + <workItem from="1422608665004" duration="181000" /> + <workItem from="1422608953671" duration="213000" /> + <workItem from="1422609958367" duration="95000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="239315000" /> + <option name="totallyTimeSpent" value="255390000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2418,110 +2345,110 @@ </todo-panel> </component> <component name="ToolWindowManager"> - <frame x="1703" y="29" width="1500" height="1146" extended-state="0" /> - <editor active="false" /> + <frame x="1592" y="-8" width="1616" height="1216" extended-state="6" /> + <editor active="true" /> <layout> - <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32692307" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.27732792" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> - <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> - <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> - <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31983805" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12361111" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.1844473" sideWeight="0.5288007" order="1" side_tool="true" content_ui="tabs" /> + <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.18055555" sideWeight="0.47119924" order="0" side_tool="false" content_ui="combo" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3228745" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43927124" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> + <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> + <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32692307" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> + <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.19625" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> + <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> + <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.2751678" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18055555" sideWeight="0.5289929" order="1" side_tool="true" content_ui="tabs" /> + <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> + <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> + <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> + <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> + <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> + <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> + <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> + <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> + <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31983805" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> + <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="FLOATING" visible="true" weight="0.32969153" sideWeight="0.5" order="19" side_tool="true" content_ui="tabs" x="2155" y="309" width="663" height="473" /> + <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3228745" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="BeanShell Box" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3293247" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> + <window_info id="CVS File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> + <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> - <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="FLOATING" visible="true" weight="0.32969153" sideWeight="0.5" order="19" side_tool="true" content_ui="tabs" x="2155" y="309" width="663" height="473" /> - <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> <window_info id="Properties File Structure" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.20918368" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3296845" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="CVS File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="BeanShell Box" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3293247" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" /> - <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> - <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> + <window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3296845" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> + <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" /> + <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> + <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> + <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> + <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3295562" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> + <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> + <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3988658" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> + <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43761814" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> + <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> </layout> <layout-to-restore> - <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> - <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> - <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Properties File Structure" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.20918368" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12339332" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> - <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3296845" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.47380674" order="0" side_tool="false" content_ui="combo" /> - <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.1888574" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> - <window_info id="CVS File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="BeanShell Box" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3293247" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" /> - <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3201133" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> - <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> - <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> - <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3219659" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> - <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.52619326" order="1" side_tool="true" content_ui="tabs" /> - <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> + <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> + <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> - <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32698095" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> + <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> + <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> + <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> + <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3219659" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> + <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> + <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32698095" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> + <window_info id="BeanShell Box" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3293247" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> + <window_info id="CVS File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> + <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> + <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.47380674" order="0" side_tool="false" content_ui="combo" /> + <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> + <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> + <window_info id="Properties File Structure" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.20918368" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> + <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> + <window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3296845" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> + <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> + <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" /> + <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> + <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> + <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> + <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> + <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> + <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> + <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> + <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> + <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.52619326" order="1" side_tool="true" content_ui="tabs" /> + <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> + <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> + <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> + <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> + <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.1888574" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> + <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> + <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> + <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3201133" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12339332" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> </layout-to-restore> </component> <component name="VCS.FileViewConfiguration"> @@ -2592,8 +2519,17 @@ </component> <component name="XDebuggerManager"> <breakpoint-manager> - <option name="time" value="2" /> + <breakpoints> + <line-breakpoint enabled="true" type="java-line"> + <url>file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java</url> + <line>126</line> + <properties /> + <option name="timeStamp" value="2" /> + </line-breakpoint> + </breakpoints> + <option name="time" value="4" /> </breakpoint-manager> + <watches-manager /> </component> <component name="antWorkspaceConfiguration"> <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" /> @@ -2610,11 +2546,18 @@ <option name="myLastEditedConfigurable" /> </component> <component name="editorHistoryManager"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1936" max-vertical-offset="2096"> + <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> + <folding /> + </state> + </provider> + </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="4012"> <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - <folding /> </state> </provider> </entry> @@ -2622,9 +2565,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="6613"> <caret line="295" column="19" selection-start-line="292" selection-start-column="4" selection-end-line="295" selection-end-column="17" /> - <folding> - <element signature="imports" expanded="true" /> - </folding> </state> </provider> </entry> @@ -2684,24 +2624,7 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/properties/log4j.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="909"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/properties/fetcher.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="909"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/README.txt"> - <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> - <state /> - </provider> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="-8.16" vertical-offset="0" max-vertical-offset="391"> <caret line="12" column="4" selection-start-line="12" selection-start-column="4" selection-end-line="12" selection-end-column="4" /> @@ -2723,9 +2646,6 @@ </provider> </entry> <entry file="file://$PROJECT_DIR$/licenses/License.txt"> - <provider editor-type-id="com.intellij.persistence.database.editor.CsvTableFileEditorProvider"> - <state /> - </provider> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.035789475" vertical-offset="0" max-vertical-offset="950"> <caret line="2" column="23" selection-start-line="2" selection-start-column="23" selection-end-line="2" selection-end-column="23" /> @@ -2753,27 +2673,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="561"> - <caret line="18" column="17" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/deploy/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="561"> - <caret line="17" column="15" selection-start-line="17" selection-start-column="15" selection-end-line="17" selection-end-column="15" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1097" max-vertical-offset="1785"> - <caret line="104" column="4" selection-start-line="104" selection-start-column="4" selection-end-line="104" selection-end-column="4" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/buildnum.properties"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> @@ -2781,61 +2680,10 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="760" max-vertical-offset="2465"> - <caret line="65" column="34" selection-start-line="65" selection-start-column="34" selection-end-line="65" selection-end-column="34" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2091"> - <caret line="43" column="3" selection-start-line="43" selection-start-column="3" selection-end-line="43" selection-end-column="53" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="388" max-vertical-offset="1632"> - <caret line="75" column="22" selection-start-line="75" selection-start-column="22" selection-end-line="75" selection-end-column="22" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="286" max-vertical-offset="1530"> - <caret line="64" column="7" selection-start-line="64" selection-start-column="7" selection-end-line="64" selection-end-column="7" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="341" max-vertical-offset="1377"> - <caret line="71" column="48" selection-start-line="71" selection-start-column="48" selection-end-line="71" selection-end-column="48" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122"> - <caret line="38" column="51" selection-start-line="38" selection-start-column="51" selection-end-line="38" selection-end-column="51" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/settings.gradle"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="272"> <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1984" max-vertical-offset="5967"> - <caret line="168" column="14" selection-start-line="168" selection-start-column="11" selection-end-line="168" selection-end-column="20" /> - <folding /> </state> </provider> </entry> @@ -2843,55 +2691,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="2575" max-vertical-offset="6290"> <caret line="184" column="9" selection-start-line="184" selection-start-column="9" selection-end-line="184" selection-end-column="9" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="493" max-vertical-offset="1989"> - <caret line="61" column="54" selection-start-line="61" selection-start-column="54" selection-end-line="61" selection-end-column="54" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="306" max-vertical-offset="2057"> - <caret line="44" column="88" selection-start-line="44" selection-start-column="0" selection-end-line="44" selection-end-column="0" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/log4j.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="425"> - <caret line="18" column="1" selection-start-line="18" selection-start-column="1" selection-end-line="18" selection-end-column="1" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1275" max-vertical-offset="3264"> - <caret line="132" column="36" selection-start-line="132" selection-start-column="36" selection-end-line="132" selection-end-column="36" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1581" max-vertical-offset="2244"> - <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="901" max-vertical-offset="1564"> - <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> - <folding /> </state> </provider> </entry> @@ -2899,7 +2698,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.3323077" vertical-offset="39751" max-vertical-offset="50932"> <caret line="2424" column="18" selection-start-line="2424" selection-start-column="18" selection-end-line="2424" selection-end-column="18" /> - <folding /> </state> </provider> </entry> @@ -2907,23 +2705,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="374"> <caret line="16" column="7" selection-start-line="16" selection-start-column="7" selection-end-line="16" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1783" max-vertical-offset="2720"> - <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="619" max-vertical-offset="2669"> - <caret line="98" column="59" selection-start-line="98" selection-start-column="7" selection-end-line="98" selection-end-column="7" /> - <folding /> </state> </provider> </entry> @@ -2931,7 +2712,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="391" max-vertical-offset="1343"> <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> - <folding /> </state> </provider> </entry> @@ -2939,15 +2719,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="170" max-vertical-offset="1377"> - <caret line="17" column="99" selection-start-line="17" selection-start-column="99" selection-end-line="17" selection-end-column="99" /> - <folding /> </state> </provider> </entry> @@ -2955,82 +2726,198 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0" vertical-offset="848" max-vertical-offset="1785"> <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2125"> + <caret line="77" column="57" selection-start-line="77" selection-start-column="57" selection-end-line="77" selection-end-column="57" /> <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1256" max-vertical-offset="2193"> - <caret line="129" column="0" selection-start-line="129" selection-start-column="0" selection-end-line="129" selection-end-column="0" /> - <folding /> + <state vertical-scroll-proportion="-10.75" vertical-offset="923" max-vertical-offset="2669"> + <caret line="109" column="19" selection-start-line="109" selection-start-column="17" selection-end-line="109" selection-end-column="19" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/build.gradle"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-31.52" vertical-offset="1031" max-vertical-offset="2091"> + <caret line="107" column="5" selection-start-line="107" selection-start-column="5" selection-end-line="107" selection-end-column="5" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.32041883" vertical-offset="0" max-vertical-offset="955"> + <caret line="18" column="17" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/properties/fetcher.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="102"> + <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/properties/log4j.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="408"> + <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/log4j.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.38931298" vertical-offset="0" max-vertical-offset="524"> + <caret line="12" column="67" selection-start-line="12" selection-start-column="42" selection-end-line="12" selection-end-column="42" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="3587"> - <caret line="35" column="68" selection-start-line="35" selection-start-column="0" selection-end-line="35" selection-end-column="0" /> + <state vertical-scroll-proportion="0.0" vertical-offset="1200" max-vertical-offset="3376"> + <caret line="75" column="5" selection-start-line="75" selection-start-column="5" selection-end-line="75" selection-end-column="5" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="5152" max-vertical-offset="6368"> + <caret line="322" column="40" selection-start-line="322" selection-start-column="40" selection-end-line="322" selection-end-column="40" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="41216" max-vertical-offset="45456"> + <caret line="2576" column="47" selection-start-line="2576" selection-start-column="41" selection-end-line="2576" selection-end-column="47" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="6112"> + <caret line="33" column="7" selection-start-line="33" selection-start-column="0" selection-end-line="33" selection-end-column="7" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2128"> + <caret line="55" column="74" selection-start-line="55" selection-start-column="45" selection-end-line="55" selection-end-column="74" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="37" max-vertical-offset="1056"> + <caret line="49" column="45" selection-start-line="49" selection-start-column="45" selection-end-line="49" selection-end-column="45" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1568" max-vertical-offset="2560"> + <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="176" max-vertical-offset="1200"> + <caret line="17" column="24" selection-start-line="17" selection-start-column="24" selection-end-line="17" selection-end-column="24" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="560" max-vertical-offset="1520"> + <caret line="74" column="49" selection-start-line="74" selection-start-column="49" selection-end-line="74" selection-end-column="49" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="112" max-vertical-offset="2080"> + <caret line="43" column="2" selection-start-line="43" selection-start-column="2" selection-end-line="43" selection-end-column="2" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="512" max-vertical-offset="1280"> + <caret line="70" column="47" selection-start-line="70" selection-start-column="47" selection-end-line="70" selection-end-column="47" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="464" max-vertical-offset="1456"> + <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1680"> + <caret line="112" column="5" selection-start-line="112" selection-start-column="5" selection-end-line="112" selection-end-column="5" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1152" max-vertical-offset="2160"> + <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1328" max-vertical-offset="2320"> + <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="1552" max-vertical-offset="3200"> + <caret line="132" column="36" selection-start-line="132" selection-start-column="36" selection-end-line="132" selection-end-column="36" /> + <folding> + <element signature="imports" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1968"> + <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> <folding> - <element signature="e#0#4562#0" expanded="true" /> <element signature="imports" expanded="true" /> - <element signature="e#2721#2722#0" expanded="true" /> - <element signature="e#2737#2738#0" expanded="true" /> - <element signature="e#2850#2851#0" expanded="true" /> - <element signature="e#2871#2872#0" expanded="true" /> - <element signature="e#3011#3012#0" expanded="true" /> - <element signature="e#3031#3032#0" expanded="true" /> - <element signature="e#3184#3185#0" expanded="true" /> - <element signature="e#3206#3207#0" expanded="true" /> - <element signature="e#3333#3334#0" expanded="true" /> - <element signature="e#3356#3357#0" expanded="true" /> - <element signature="e#3474#3475#0" expanded="true" /> - <element signature="e#3494#3495#0" expanded="true" /> - <element signature="e#3697#3698#0" expanded="true" /> - <element signature="e#3778#3779#0" expanded="true" /> - <element signature="e#3948#3949#0" expanded="true" /> - <element signature="e#3980#3981#0" expanded="true" /> - <element signature="e#4131#4132#0" expanded="true" /> - <element signature="e#4155#4156#0" expanded="true" /> - <element signature="e#4298#4299#0" expanded="true" /> - <element signature="e#4322#4323#0" expanded="true" /> - <element signature="e#4393#4394#0" expanded="true" /> - <element signature="e#4417#4418#0" expanded="true" /> </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-12.035714" vertical-offset="530" max-vertical-offset="3825"> - <caret line="83" column="36" selection-start-line="83" selection-start-column="31" selection-end-line="83" selection-end-column="41" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="238" max-vertical-offset="6154"> - <caret line="72" column="47" selection-start-line="72" selection-start-column="47" selection-end-line="72" selection-end-column="47" /> + <state vertical-scroll-proportion="0.96961063" vertical-offset="2931" max-vertical-offset="4160"> + <caret line="247" column="37" selection-start-line="247" selection-start-column="37" selection-end-line="247" selection-end-column="37" /> <folding> - <element signature="imports" expanded="true" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.5952381" vertical-offset="34796" max-vertical-offset="47005"> - <caret line="2114" column="36" selection-start-line="2114" selection-start-column="36" selection-end-line="2114" selection-end-column="36" /> - <folding> - <element signature="e#0#67784#0" expanded="true" /> - <element signature="imports" expanded="true" /> - <element signature="e#5390#5398#0" expanded="true" /> - <element signature="e#5546#5559#0" expanded="true" /> - <element signature="e#5715#5726#0" expanded="true" /> - <element signature="e#5945#5953#0" expanded="true" /> - <element signature="e#6054#6062#0" expanded="true" /> - <element signature="e#31758#31766#0" expanded="true" /> + <element signature="e#0#5577#0" expanded="true" /> </folding> </state> </provider> @@ -3131,7 +3018,7 @@ </state> <state key="JdkListConfigurable.UI"> <settings> - <last-edited>1.7.x</last-edited> + <last-edited>1.8.x</last-edited> <splitter-proportions> <option name="proportions"> <list> @@ -3186,5 +3073,4 @@ <outputDirectory /> <properties /> </component> -</project> - +</project> \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 47dbedf..9d8b08c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -1,7 +1,7 @@ /* - * @(#)Commands.java + * Commands.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; /** @@ -43,6 +42,16 @@ package net.thauvin.erik.mobibot; */ public class Commands { + /** + * The add (back)log command. + */ + public static final String ADDLOG_CMD = "addlog"; + + /** + * The math command. + */ + public static final String CALC_CMD = "calc"; + /** * The currency command. */ @@ -54,35 +63,15 @@ public class Commands public static final String CURRENCY_RATES_KEYWORD = "rates"; /** - * The weather command. + * The cycle command. */ - public static final String WEATHER_CMD = "weather"; + public static final String CYCLE_CMD = "cycle"; /** * Debug command line argument. */ public static final String DEBUG_ARG = "debug"; - /** - * Help command line argument. - */ - public static final String HELP_ARG = "help"; - - /** - * Properties command line argument. - */ - public static final String PROPS_ARG = "properties"; - - /** - * Properties version line argument. - */ - public static final String VERSION_ARG = "version"; - - /** - * The add (back)log command. - */ - public static final String ADDLOG_CMD = "addlog"; - /** * The debug command. */ @@ -93,35 +82,20 @@ public class Commands */ public static final String DICE_CMD = "dice"; - /** - * The say command. - */ - public static final String SAY_CMD = "say"; - /** * The die command. */ public static final String DIE_CMD = "die"; /** - * The cycle command. + * The Google command. */ - public static final String CYCLE_CMD = "cycle"; + public static final String GOOGLE_CMD = "google"; /** - * The msg command. + * Help command line argument. */ - public static final String MSG_CMD = "msg"; - - /** - * The ignore command. - */ - public static final String IGNORE_CMD = "ignore"; - - /** - * The ignore <code>me</code> keyword. - */ - public static final String IGNORE_ME_KEYWORD = "me"; + public static final String HELP_ARG = "help"; /** * The help command. @@ -139,29 +113,19 @@ public class Commands public static final String HELP_TAGS_KEYWORD = "tags"; /** - * The Google command. + * The ignore command. */ - public static final String GOOGLE_CMD = "google"; + public static final String IGNORE_CMD = "ignore"; /** - * The Twitter command. + * The ignore <code>me</code> keyword. */ - public static final String TWITTER_CMD = "twitter"; + public static final String IGNORE_ME_KEYWORD = "me"; /** - * The math command. + * The info command. */ - public static final String CALC_CMD = "calc"; - - /** - * The me command. - */ - public static final String ME_CMD = "me"; - - /** - * The nick command. - */ - public static final String NICK_CMD = "nick"; + public static final String INFO_CMD = "info"; /** * The link command. @@ -173,6 +137,21 @@ public class Commands */ public static final String LOOKUP_CMD = "lookup"; + /** + * The me command. + */ + public static final String ME_CMD = "me"; + + /** + * The msg command. + */ + public static final String MSG_CMD = "msg"; + + /** + * The nick command. + */ + public static final String NICK_CMD = "nick"; + /** * The ping command. */ @@ -183,6 +162,11 @@ public class Commands */ public static final String PONG_CMD = "pong"; + /** + * Properties command line argument. + */ + public static final String PROPS_ARG = "properties"; + /** * The quote command. */ @@ -193,15 +177,20 @@ public class Commands */ public static final String RECAP_CMD = "recap"; + /** + * The say command. + */ + public static final String SAY_CMD = "say"; + /** * The stock command. */ public static final String STOCK_CMD = "stock"; /** - * The time command. + * The {@link #TELL_CMD} all command. */ - public static final String TIME_CMD = "time"; + public static final String TELL_ALL_CMD = "all"; /** * The tell command. @@ -214,14 +203,14 @@ public class Commands public static final String TELL_DEL_CMD = "del"; /** - * The {@link #TELL_CMD} all command. + * The time command. */ - public static final String TELL_ALL_CMD = "all"; + public static final String TIME_CMD = "time"; /** - * The war command. + * The Twitter command. */ - public static final String WAR_CMD = "war"; + public static final String TWITTER_CMD = "twitter"; /** * The users command. @@ -229,9 +218,9 @@ public class Commands public static final String USERS_CMD = "users"; /** - * The info command. + * Properties version line argument. */ - public static final String INFO_CMD = "info"; + public static final String VERSION_ARG = "version"; /** * The version command. @@ -243,10 +232,20 @@ public class Commands */ public static final String VIEW_CMD = "view"; + /** + * The war command. + */ + public static final String WAR_CMD = "war"; + + /** + * The weather command. + */ + public static final String WEATHER_CMD = "weather"; + /** * Disables the default constructor. * - * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + * @throws UnsupportedOperationException If the constructor is called. */ private Commands() throws UnsupportedOperationException diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 4d18356..f5936c9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -1,7 +1,7 @@ /* - * @(#)CurrencyConverter.java + * CurrencyConverter.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import org.jdom.Document; @@ -54,21 +53,26 @@ import java.util.*; */ public class CurrencyConverter implements Runnable { - /** - * The exchange rates table URL. - */ - private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; - /** * The exchange rates. */ private static final Map<String, String> EXCHANGE_RATES = new TreeMap<String, String>(); + /** + * The exchange rates table URL. + */ + private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; + /** * The bot. */ private final Mobibot bot; + /** + * The last exchange rates table publication date. + */ + private String pubDate = ""; + /** * The actual currency query. */ @@ -79,11 +83,6 @@ public class CurrencyConverter implements Runnable */ private String sender; - /** - * The last exchange rates table publication date. - */ - private String pubDate = ""; - /** * Creates a new {@link CurrencyConverter} instance. * @@ -165,8 +164,7 @@ public class CurrencyConverter implements Runnable NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' + cmds[1].toUpperCase() + " = " + NumberFormat.getCurrencyInstance(Locale.US).format((amt * to) / from) - .substring(1) + ' ' + cmds[3].toUpperCase() - ); + .substring(1) + ' ' + cmds[3].toUpperCase()); } catch (NullPointerException ignored) { diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 36ef5ca..8dcce8b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -1,7 +1,7 @@ /* - * @(#)DeliciousPoster.java + * DeliciousPoster.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import del.icio.us.Delicious; diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/Dice.java index 7ffdc9f..7a324f4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/Dice.java @@ -1,7 +1,7 @@ /* - * @(#)Dice.java + * Dice.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,7 @@ public class Dice /** * Disables the default constructor. * - * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + * @throws UnsupportedOperationException If the constructor is called. */ private Dice() throws UnsupportedOperationException @@ -71,8 +71,7 @@ public class Dice bot.send(bot.getChannel(), sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils - .bold(playerTotal) - ); + .bold(playerTotal)); i = r.nextInt(6) + 1; y = r.nextInt(6) + 1; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 91ac6db..e28ae8c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -1,7 +1,7 @@ /* - * @(#)EntriesMgr.java + * EntriesMgr.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -75,7 +75,7 @@ public class EntriesMgr /** * Disables the default constructor. * - * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + * @throws UnsupportedOperationException If the constructor is called. */ private EntriesMgr() throws UnsupportedOperationException @@ -83,6 +83,55 @@ public class EntriesMgr throw new UnsupportedOperationException("Illegal constructor call."); } + /** + * Loads the backlogs. + * + * @param file The file containing the backlogs. + * @param history The history list. + * + * @throws FileNotFoundException If the file was not found. + * @throws FeedException If an error occurred while reading the feed. + */ + public static void loadBacklogs(String file, List<String> history) + throws FileNotFoundException, FeedException + { + history.clear(); + + final SyndFeedInput input = new SyndFeedInput(); + + InputStreamReader reader = null; + + try + { + reader = new InputStreamReader(new FileInputStream(new File(file))); + + final SyndFeed feed = input.build(reader); + + final List items = feed.getEntries(); + SyndEntry item; + + for (int i = items.size() - 1; i >= 0; i--) + { + item = (SyndEntryImpl) items.get(i); + history.add(item.getTitle()); + } + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException ignore) + { + ; // Do nothing + } + } + } + } + /** * Loads the current entries. * @@ -167,55 +216,6 @@ public class EntriesMgr return today; } - /** - * Loads the backlogs. - * - * @param file The file containing the backlogs. - * @param history The history list. - * - * @throws FileNotFoundException If the file was not found. - * @throws FeedException If an error occurred while reading the feed. - */ - public static void loadBacklogs(String file, List<String> history) - throws FileNotFoundException, FeedException - { - history.clear(); - - final SyndFeedInput input = new SyndFeedInput(); - - InputStreamReader reader = null; - - try - { - reader = new InputStreamReader(new FileInputStream(new File(file))); - - final SyndFeed feed = input.build(reader); - - final List items = feed.getEntries(); - SyndEntry item; - - for (int i = items.size() - 1; i >= 0; i--) - { - item = (SyndEntryImpl) items.get(i); - history.add(item.getTitle()); - } - } - finally - { - if (reader != null) - { - try - { - reader.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - } - } - /** * Saves the entries. * @@ -260,8 +260,7 @@ public class EntriesMgr buff = new StringBuffer( "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' - + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>" - ); + + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); if (entry.getCommentsCount() > 0) { diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index d7b26d8..1fe3eae 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -1,7 +1,7 @@ /* - * @(#)EntryComment.java + * EntryComment.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import java.io.Serializable; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index d734f6e..ceee3f3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -1,7 +1,7 @@ /* - * @(#)EntryLink.java + * EntryLink.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import com.sun.syndication.feed.synd.SyndCategoryImpl; diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index ffc91e9..f12f88d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -1,7 +1,7 @@ /* - * @(#)FeedReader.java + * FeedReader.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import com.sun.syndication.feed.synd.SyndEntry; diff --git a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java index 7a01981..4032836 100644 --- a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java @@ -1,7 +1,7 @@ /* - * @(#)GoogleSearch.java + * GoogleSearch.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import org.json.JSONArray; diff --git a/src/main/java/net/thauvin/erik/mobibot/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/Lookup.java index c3a20b1..d42c2a3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/Lookup.java @@ -1,7 +1,7 @@ /* - * @(#)Lookup.java + * Lookup.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import org.apache.commons.net.WhoisClient; @@ -59,7 +58,7 @@ public class Lookup /** * Disables the default constructor. * - * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + * @throws UnsupportedOperationException If the constructor is called. */ private Lookup() throws UnsupportedOperationException diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index b779157..77308f3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1,7 +1,7 @@ /* - * @(#)Mobibot.java + * Mobibot.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -83,21 +83,11 @@ public class Mobibot extends PircBot private static final int DEFAULT_TELL_MAX_DAYS = 7; - /** - * The number of days message are kept. - */ - private int tellMaxDays = DEFAULT_TELL_MAX_DAYS; - /** * The default {@link Commands#TELL_CMD) message max queue size. */ private static final int DEFAULT_TELL_MAX_SIZE = 50; - /** - * The maximum number of {@link Commands#TELL_CMD} messages allowed. - */ - private int tellMaxSize = DEFAULT_TELL_MAX_SIZE; - /** * The double tab indent (8 spaces). */ @@ -280,6 +270,16 @@ public class Mobibot extends PircBot */ private String identNick = ""; + /** + * The number of days message are kept. + */ + private int tellMaxDays = DEFAULT_TELL_MAX_DAYS; + + /** + * The maximum number of {@link Commands#TELL_CMD} messages allowed. + */ + private int tellMaxSize = DEFAULT_TELL_MAX_SIZE; + /** * Today's date. */ @@ -1301,8 +1301,7 @@ public class Mobibot extends PircBot send(sender, DOUBLE_INDENT + Utils .bold(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " + Commands.VERSION_CMD) - ); + + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); } } } @@ -1401,8 +1400,7 @@ public class Mobibot extends PircBot send(sender, "Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size() + (isTellEnabled() && isOp(sender) ? ", Messages: " + tellMessages.size() : "") + ']', - isPrivate - ); + isPrivate); } /** @@ -2268,8 +2266,7 @@ public class Mobibot extends PircBot private void recap(String sender, String message, boolean isAction) { recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") - + message - ); + + message); if (recap.size() > MAX_RECAP) { @@ -2321,8 +2318,7 @@ public class Mobibot extends PircBot { send(nickname, Utils.bold("You") + " wanted me to remind you: " + Colors.REVERSE + message - .getMessage() + Colors.REVERSE, true - ); + .getMessage() + Colors.REVERSE, true); message.setIsReceived(); message.setIsNotified(); @@ -2335,8 +2331,7 @@ public class Mobibot extends PircBot send(nickname, message.getSender() + " wanted me to tell you: " + Colors.REVERSE + message .getMessage() + Colors.REVERSE, - true - ); + true); message.setIsReceived(); @@ -2350,8 +2345,7 @@ public class Mobibot extends PircBot "Your message " + Colors.REVERSE + "[ID " + message.getId() + ']' + Colors.REVERSE + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF .format(message.getReceived()), - true - ); + true); message.setIsNotified(); @@ -2471,8 +2465,7 @@ public class Mobibot extends PircBot send(sender, Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - true - ); + true); } } else @@ -2500,8 +2493,7 @@ public class Mobibot extends PircBot Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId() + ", DELIVERED]", - true - ); + true); } else @@ -2509,8 +2501,7 @@ public class Mobibot extends PircBot send(sender, Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() + ", QUEUED]", - true - ); + true); } send(sender, DOUBLE_INDENT + message.getMessage(), true); @@ -2527,8 +2518,7 @@ public class Mobibot extends PircBot send(sender, DOUBLE_INDENT + Utils .bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id|" - + Commands.TELL_ALL_CMD + '>') - ); + + Commands.TELL_ALL_CMD + '>')); send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days."); } } @@ -2789,8 +2779,7 @@ public class Mobibot extends PircBot send(sender, "To view more, try: " + Utils .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), - isPrivate - ); + isPrivate); break; } diff --git a/src/main/java/net/thauvin/erik/mobibot/Quote.java b/src/main/java/net/thauvin/erik/mobibot/Quote.java index b626988..4838d48 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Quote.java +++ b/src/main/java/net/thauvin/erik/mobibot/Quote.java @@ -1,7 +1,7 @@ /* - * @(#)Quote.java + * Quote.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import org.jibble.pircbot.Colors; diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 7caf1ec..d17fb03 100644 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,7 +1,7 @@ /* - * @(#)ReleaseInfo.java + * ReleaseInfo.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -82,6 +82,16 @@ public class ReleaseInfo return buildDate; } + /** + * Get buildNumber (set during build process to 0). + * + * @return int buildNumber + */ + public static int getBuildNumber() + { + return 0; + } + /** * Get project (set during build process to "mobibot"). * @@ -102,14 +112,4 @@ public class ReleaseInfo return version; } - /** - * Get buildNumber (set during build process to 0). - * - * @return int buildNumber - */ - public static int getBuildNumber() - { - return 0; - } - } diff --git a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java index 50d57e6..a2bd7fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java @@ -1,7 +1,7 @@ /* - * @(#)StockQuote.java + * StockQuote.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import com.Ostermiller.util.CSVParser; diff --git a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java index c99a9ab..e19c1d2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java +++ b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java @@ -4,20 +4,20 @@ import javax.swing.*; /** * This is the 3rd version of SwingWorker (also known as SwingWorker 3), an abstract class that you subclass to perform - * GUI-related work in a dedicated thread. For instructions on and examples of using this class, see: - * <p> + * GUI-related work in a dedicated thread. For instructions on and examples of using this class, see: + * <p/> * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html - * </p> - * Note that the API changed slightly in the 3rd version: You must now invoke start() on the SwingWorker after creating - * it. + * <p/> + * Note that the API changed slightly in the 3rd version: + * You must now invoke start() on the SwingWorker after creating it. */ @SuppressWarnings("ALL") public abstract class SwingWorker { - private Object value; // see getValue(), setValue() - private ThreadVar threadVar; + private Object value; // see getValue(), setValue() + /** * Start a thread that will call the <code>construct</code> method and then exit. */ @@ -67,19 +67,6 @@ public abstract class SwingWorker */ public abstract Object construct(); - /** - * A new method that interrupts the worker thread. Call this method to force the worker to stop what it's doing. - */ - public void interrupt() - { - Thread t = threadVar.get(); - if (t != null) - { - t.interrupt(); - } - threadVar.clear(); - } - /** * Return the value created by the <code>construct</code> method. Returns null if either the constructing thread or * the current thread was interrupted before a value was produced. @@ -127,6 +114,19 @@ public abstract class SwingWorker value = x; } + /** + * A new method that interrupts the worker thread. Call this method to force the worker to stop what it's doing. + */ + public void interrupt() + { + Thread t = threadVar.get(); + if (t != null) + { + t.interrupt(); + } + threadVar.clear(); + } + /** * Start the worker thread. */ @@ -151,14 +151,14 @@ public abstract class SwingWorker thread = t; } - synchronized Thread get() - { - return thread; - } - synchronized void clear() { thread = null; } + + synchronized Thread get() + { + return thread; + } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 77ebdf9..48b3cbd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -1,7 +1,7 @@ /* - * @(#)TellMessage.java + * TellMessage.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import java.io.Serializable; @@ -47,23 +46,24 @@ import java.util.Date; */ public class TellMessage implements Serializable { + @SuppressWarnings({"UnusedDeclaration"}) private static final long serialVersionUID = 1L; - private final String sender; - - private final String recipient; - - private final String message; - private final String id; + private final String message; + final private Date queued; - private Date received; + private final String recipient; + + private final String sender; + + private boolean isNotified; private boolean isReceived; - private boolean isNotified; + private Date received; /** * Create a new message. diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 9e9b4aa..d6b9b2b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -1,7 +1,7 @@ /* - * @(#)TellMessagesMgr.java + * TellMessagesMgr.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import org.apache.commons.logging.impl.Log4JLogger; @@ -54,7 +53,7 @@ public class TellMessagesMgr /** * Disables the default constructor. * - * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + * @throws UnsupportedOperationException If the constructor is called. */ private TellMessagesMgr() throws UnsupportedOperationException @@ -62,6 +61,35 @@ public class TellMessagesMgr throw new UnsupportedOperationException("Illegal constructor call."); } + /** + * Cleans the messages queue + * + * @param tellMessages The messages list. + * @param tellMaxDays The maximum number of days to keep messages for. + * + * @return <code>True</code> if the queue was cleaned. + */ + public static boolean cleanTellMessages(List<TellMessage> tellMessages, int tellMaxDays) + { + final Calendar maxDate = Calendar.getInstance(); + final Date today = new Date(); + boolean cleaned = false; + + for (final TellMessage message : tellMessages) + { + maxDate.setTime(message.getQueued()); + maxDate.add(Calendar.DATE, tellMaxDays); + + if (maxDate.getTime().before(today)) + { + tellMessages.remove(message); + cleaned = true; + } + } + + return cleaned; + } + /** * Loads the messages. * @@ -79,7 +107,6 @@ public class TellMessagesMgr try { - if (logger.isDebugEnabled()) { logger.debug("Loading the messages."); @@ -140,27 +167,4 @@ public class TellMessagesMgr logger.error("Unable to save messages queue.", e); } } - - /** - * Cleans the messages queue - * - * @param tellMessages The messages list. - * @param tellMaxDays The maximum number of days to keep messages for. - */ - public static void cleanTellMessages(List<TellMessage> tellMessages, int tellMaxDays) - { - final Calendar maxDate = Calendar.getInstance(); - final Date today = new Date(); - - for (final TellMessage message : tellMessages) - { - maxDate.setTime(message.getQueued()); - maxDate.add(Calendar.DATE, tellMaxDays); - - if (maxDate.getTime().before(today)) - { - tellMessages.remove(message); - } - } - } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/Twitter.java index 049a15f..63aa811 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/Twitter.java @@ -1,7 +1,7 @@ /* - * @(#)Twitter.java + * Twitter.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import twitter4j.Status; @@ -47,26 +46,6 @@ import twitter4j.conf.ConfigurationBuilder; */ public class Twitter implements Runnable { - /** - * The bot. - */ - private final Mobibot bot; - - /** - * The Twitter consumer secret. - */ - private final String consumerSecret; - - /** - * The Twitter consumer key. - */ - private final String consumerKey; - - /** - * The Twitter message. - */ - private final String message; - /** * The Twitter access token. */ @@ -77,6 +56,26 @@ public class Twitter implements Runnable */ private final String accessTokenSecret; + /** + * The bot. + */ + private final Mobibot bot; + + /** + * The Twitter consumer key. + */ + private final String consumerKey; + + /** + * The Twitter consumer secret. + */ + private final String consumerSecret; + + /** + * The Twitter message. + */ + private final String message; + /** * The nick of the person who sent the message. */ @@ -122,8 +121,7 @@ public class Twitter implements Runnable bot.send(sender, "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status - .getId() - ); + .getId()); } catch (Exception e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 3fa40a7..04fcf26 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -9,15 +9,9 @@ import java.io.BufferedReader; import java.io.InputStreamReader; /** - * The <code>TwitterOAuth</code> class. - * <p> - * Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> to register your bot. - * </p> - * Then execute: - * <p> - * <code>java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret></code> - * </p> - * and follow the prompts/instructions. + * The <code>TwitterOAuth</code> class. <p> Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> + * to register your bot. </p> Then execute: <p> <code>java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth + * <consumerKey> <consumerSecret></code> </p> and follow the prompts/instructions. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @author <a href="http://twitter4j.org/en/code-examples.html#oauth">http://twitter4j.org/en/code-examples.html#oauth</a> diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 8e65826..8eb4862 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -1,7 +1,7 @@ /* - * @(#)Utils.java + * Utils.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import org.jibble.pircbot.Colors; @@ -58,20 +57,20 @@ public class Utils */ public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); - /** - * The UTC (yyyy-MM-dd HH:mm) simple date format. - */ - static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - /** * The ISO (YYYY-MM-DD) simple date format. */ static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); + /** + * The UTC (yyyy-MM-dd HH:mm) simple date format. + */ + static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + /** * Disables the default constructor. * - * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + * @throws UnsupportedOperationException If the constructor is called. */ private Utils() throws UnsupportedOperationException @@ -79,200 +78,6 @@ public class Utils throw new UnsupportedOperationException("Illegal constructor call."); } - /** - * Converts XML/XHTML entities to plain text. - * - * @param str The string to unescape. - * - * @return The unescaped string. - */ - public static String unescapeXml(String str) - { - String s = str.replaceAll("&", "&"); - s = s.replaceAll("<", "<"); - s = s.replaceAll(">", ">"); - s = s.replaceAll(""", "\""); - s = s.replaceAll("'", "'"); - s = s.replaceAll("'", "'"); - - return s; - } - - /** - * Copies a file. - * - * @param in The source file. - * @param out The destination file. - * - * @throws java.io.IOException If the file could not be copied. - */ - @SuppressWarnings("UnusedDeclaration") - public static void copyFile(File in, File out) - throws IOException - { - FileChannel inChannel = null; - FileChannel outChannel = null; - FileInputStream input = null; - FileOutputStream output = null; - - try - { - input = new FileInputStream(in); - output = new FileOutputStream(out); - - inChannel = input.getChannel(); - outChannel = output.getChannel(); - - inChannel.transferTo(0L, inChannel.size(), outChannel); - } - finally - { - try - { - if (inChannel != null) - { - inChannel.close(); - } - - if (input != null) - { - input.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - - try - { - if (outChannel != null) - { - outChannel.close(); - } - - if (output != null) - { - output.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - } - } - - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - public static String internetTime() - { - final Calendar gc = Calendar.getInstance(); - - final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); - int hh = gc.get(Calendar.HOUR_OF_DAY); - final int mm = gc.get(Calendar.MINUTE); - final int ss = gc.get(Calendar.SECOND); - - hh -= offset; // GMT - hh += 1; // BMT - - long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); - - if (beats >= 1000) - { - beats -= (long) 1000; - } - else if (beats < 0) - { - beats += (long) 1000; - } - - if (beats < 10) - { - return ("@00" + String.valueOf(beats)); - } - else if (beats < 100) - { - return ("@0" + String.valueOf(beats)); - } - - return ('@' + String.valueOf(beats)); - } - - /** - * Returns a property as an int. - * - * @param property The port property value. - * @param def The default property value. - * - * @return The port or default value if invalid. - */ - public static int getIntProperty(String property, int def) - { - int prop; - - try - { - prop = Integer.parseInt(property); - } - catch (NumberFormatException ignore) - { - prop = def; - } - - return prop; - } - - /** - * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. - * - * @param location The File or URL location. - * @param isUrl Set to true if the location is a URL - * - * @return The location ending with a slash. - */ - public static String ensureDir(String location, boolean isUrl) - { - if (isUrl) - { - if (location.charAt(location.length() - 1) == '/') - { - return location; - } - else - { - return location + '/'; - } - } - else - { - if (location.charAt(location.length() - 1) == File.separatorChar) - { - return location; - } - else - { - return location + File.separatorChar; - } - } - } - - /** - * Returns true if the given string is valid. - * - * @param s The string to validate. - * - * @return true if the string is non-empty and not null, false otherwise. - */ - public static boolean isValidString(String s) - { - return (s != null) && (s.trim().length() > 0); - } - /** * Makes the given int bold. * @@ -285,16 +90,6 @@ public class Utils return Colors.BOLD + i + Colors.BOLD; } - /** - * Returns today's date. - * - * @return Today's date in {@link #ISO_SDF ISO} format. - */ - public static String today() - { - return ISO_SDF.format(Calendar.getInstance().getTime()); - } - /** * Builds an entry's comment for display on the channel. * @@ -385,4 +180,208 @@ public class Utils { return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); } + + /** + * Copies a file. + * + * @param in The source file. + * @param out The destination file. + * + * @throws java.io.IOException If the file could not be copied. + */ + @SuppressWarnings("UnusedDeclaration") + public static void copyFile(File in, File out) + throws IOException + { + FileChannel inChannel = null; + FileChannel outChannel = null; + FileInputStream input = null; + FileOutputStream output = null; + + try + { + input = new FileInputStream(in); + output = new FileOutputStream(out); + + inChannel = input.getChannel(); + outChannel = output.getChannel(); + + inChannel.transferTo(0L, inChannel.size(), outChannel); + } + finally + { + try + { + if (inChannel != null) + { + inChannel.close(); + } + + if (input != null) + { + input.close(); + } + } + catch (Exception ignore) + { + ; // Do nothing + } + + try + { + if (outChannel != null) + { + outChannel.close(); + } + + if (output != null) + { + output.close(); + } + } + catch (Exception ignore) + { + ; // Do nothing + } + } + } + + /** + * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. + * + * @param location The File or URL location. + * @param isUrl Set to true if the location is a URL + * + * @return The location ending with a slash. + */ + public static String ensureDir(String location, boolean isUrl) + { + if (isUrl) + { + if (location.charAt(location.length() - 1) == '/') + { + return location; + } + else + { + return location + '/'; + } + } + else + { + if (location.charAt(location.length() - 1) == File.separatorChar) + { + return location; + } + else + { + return location + File.separatorChar; + } + } + } + + /** + * Returns a property as an int. + * + * @param property The port property value. + * @param def The default property value. + * + * @return The port or default value if invalid. + */ + public static int getIntProperty(String property, int def) + { + int prop; + + try + { + prop = Integer.parseInt(property); + } + catch (NumberFormatException ignore) + { + prop = def; + } + + return prop; + } + + /** + * Returns the current Internet (beat) Time. + * + * @return The Internet Time string. + */ + public static String internetTime() + { + final Calendar gc = Calendar.getInstance(); + + final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); + int hh = gc.get(Calendar.HOUR_OF_DAY); + final int mm = gc.get(Calendar.MINUTE); + final int ss = gc.get(Calendar.SECOND); + + hh -= offset; // GMT + hh += 1; // BMT + + long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); + + if (beats >= 1000) + { + beats -= (long) 1000; + } + else if (beats < 0) + { + beats += (long) 1000; + } + + if (beats < 10) + { + return ("@00" + String.valueOf(beats)); + } + else if (beats < 100) + { + return ("@0" + String.valueOf(beats)); + } + + return ('@' + String.valueOf(beats)); + } + + /** + * Returns true if the given string is valid. + * + * @param s The string to validate. + * + * @return true if the string is non-empty and not null, false otherwise. + */ + public static boolean isValidString(String s) + { + return (s != null) && (s.trim().length() > 0); + } + + /** + * Returns today's date. + * + * @return Today's date in {@link #ISO_SDF ISO} format. + */ + public static String today() + { + return ISO_SDF.format(Calendar.getInstance().getTime()); + } + + /** + * Converts XML/XHTML entities to plain text. + * + * @param str The string to unescape. + * + * @return The unescaped string. + */ + public static String unescapeXml(String str) + { + String s = str.replaceAll("&", "&"); + s = s.replaceAll("<", "<"); + s = s.replaceAll(">", ">"); + s = s.replaceAll(""", "\""); + s = s.replaceAll("'", "'"); + s = s.replaceAll("'", "'"); + + return s; + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/War.java index ec2ba9f..7a2d6c4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/War.java @@ -1,7 +1,7 @@ /* - * @(#)War.java + * War.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -58,7 +58,7 @@ public class War /** * Disables the default constructor. * - * @throws UnsupportedOperationException if an error occurred. if the constructor is called. + * @throws UnsupportedOperationException If the constructor is called. */ private War() throws UnsupportedOperationException diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/Weather.java index 105e770..a232cee 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/Weather.java @@ -1,7 +1,7 @@ /* - * @(#)Weather.java + * Weather.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package net.thauvin.erik.mobibot; import net.sf.jweather.metar.Metar; @@ -65,6 +64,11 @@ public class Weather implements Runnable */ private final Mobibot bot; + /** + * The private message flag. + */ + private final boolean isPrivate; + /** * The nick of the person who sent the message. */ @@ -75,11 +79,6 @@ public class Weather implements Runnable */ private final String station; - /** - * The private message flag. - */ - private final boolean isPrivate; - /** * Creates a new {@link Weather} instance. * @@ -114,8 +113,7 @@ public class Weather implements Runnable bot.send(sender, "At: " + Utils.UTC_SDF.format(metar.getDate()) + " UTC (" + ( ((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + " minutes ago)", - isPrivate - ); + isPrivate); result = metar.getWindSpeedInMPH(); @@ -124,8 +122,7 @@ public class Weather implements Runnable bot.send(sender, "Wind Speed: " + result + " mph, " + metar.getWindSpeedInKnots() + " knots, " + metar .getWindSpeedInMPS() + " m/s", - isPrivate - ); + isPrivate); } result = metar.getVisibility(); @@ -135,8 +132,7 @@ public class Weather implements Runnable bot.send(sender, "Visibility: " + (metar.getVisibilityLessThan() ? "< " : "") + NUMBER_FORMAT.format(result) + " mi, " + metar.getVisibilityInKilometers() + " km", - isPrivate - ); + isPrivate); } result = metar.getPressure(); diff --git a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java index 26eccfe..228987c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java @@ -1,7 +1,7 @@ /* - * @(#)Time.java + * WorldTime.java * - * Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,16 +48,16 @@ import java.util.TreeMap; */ public class WorldTime { - /** - * The countries supported by the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. - */ - private static final Map<String, String> COUNTRIES_MAP = new TreeMap<String, String>(); - /** * The beats (Internet Time) keyword. */ private static final String BEATS_KEYWORD = ".beats"; + /** + * The countries supported by the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. + */ + private static final Map<String, String> COUNTRIES_MAP = new TreeMap<String, String>(); + /** * The date/time format for the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. */ From 80921e2aea53c12fbca49d1f7eb16f800146b8df Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Sat, 29 Aug 2015 12:27:18 -0700 Subject: [PATCH 015/842] Fixed currencies display, added help for currency rates keyword. --- buildnum.properties | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 51106 -> 52266 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- mobibot.ipr | 369 +------ mobibot.iws | 911 ++++++++++-------- .../erik/mobibot/CurrencyConverter.java | 106 +- .../net/thauvin/erik/mobibot/Mobibot.java | 2 + .../net/thauvin/erik/mobibot/ReleaseInfo.java | 136 +-- 8 files changed, 670 insertions(+), 860 deletions(-) diff --git a/buildnum.properties b/buildnum.properties index 8ff75c6..f54bac3 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Fri Apr 25 18:08:16 PDT 2014 +#Sat Aug 29 12:08:20 PDT 2015 build.num.last=0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3c7abdf12790879c06b07176de29647f77aa4129..b5166dad4d90021f6a0b45268c0755719f1d5cd4 100644 GIT binary patch delta 36092 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�Jr@&0WohZaG>b`<cng`t!H?Ty4bs<p9$HTFtjjbG<9ozhEWIh^X2R)fh*Q+_ z<i+-8Gr-ICvkljWG|7*=Z!;U1-NWOD$mU;OWu{TRm}FX<<?{-iq79Dj%3+Tixlszr z6K5nyZ^Aze<nmJj1p~3lL++`D$4P8Pr*RTF?ta?}u%bNcv`Mx^{3V*y(H!u~sf^{i zMBA7i%HWY`raxJR9sl7$*V*vCy26;q)}QUe>E}+k*E~?mF6i6W5L!h%;!1apPcsX9 zO4{c;npto5>-p9EiITq3%&c)$h5m}m)U=X}EfcTf9EaX9o&8gq%&%C1%D`*h3sYT6 zHYDzdNVTgst@bcezYq`BeCe^!%$MoZ({}P2J>yn>T`zF&zdK9ZnuuwoT9>(NqZ@J@ ztTQ_=FtQI+75W6|_r3QeT^dsi>1eR{Ybs!jr{mzP{B0;`#J1^(o!!Hvy?nv2YR4P% zKF>!lJpH6k+VK@h2?qwI+zu=EZIAlhsw&CqlVnJy!-9KOZmyp!;?q-Dg08KaWWHiu zhs!tCcL$TSkLA6ZBzk?;A7g%IPkx!on|SZAN#<<fvbOzQHI)l?U+90^Tu+q-ZeuS4 z%->RL28UVWmW<v-oXRlCXZrVZ^W(bM)>2H@HMPyPsx7s;Bf7*Isp|BCk4nKFz7hXi z4HZ0)`SIp(Vy~lA%`OIERet)bEW!L6#g@vB&)Rp~Nib$DTBoBsujAw3*z$Y+SDxBv z#wPKe+Usmlk%-?RkkjFw9|@jtWsnqT%G;6)Z3Z{vr`clD*G0}S<c~85uClY89YsjX z>3^aK1>s@`Nf4LLgZVq)|58KY@(y~dOc!G>_UJzuSPMD*0J+-CCm7biHw3uwqXHNI z=^{t?=5P_dj@c7#=<O~hn=1i#v}Q>(g1l-9$|M4~)fD2WFqq=IYLc*S3f562KwiBf zFd|9;iy8$Dj8E_);mJ~H!Y-(O-3%Rk=CBDw1ZLIP3JIEPD8&7esIFL2R&K6gK)#kj z44scYcdXS$<kZ37egVw?J_(&+iX&gJ5CL;*R|?5Hps<<@tfPWV83mwHM?wTXacbL# zpk-u8V1y%RlS2en@@k-c8jR04FGh3NUllR1B$j_3_%tp>)Uy(U!@S_)!eO6QLDW%7 z3L<KJ`qSCqVRZyvR8K~%&zn4wTcE+`;OcB3#zA-57$RU`z0fty&Km7buvNE}2n1am z(0w%onj<!RA{a4>!|t&|Y|;%{2q=o8O(GC9#W<+8b3Fo%y2!4a+hh1>s$wY)Tg@8@ zMJZviRaHd2fsfTWTR`V~Bp{63xdfS%F%yOh;<UtAXi_`_ly9Wt%)zdLL_xOT)e%g5 zBPtxWY!I*ypa3qHgih{(4lac#&jh1fH4RKq9IJ{zFcyhnL)e}PqBog>z(x`RnrseB zFNA;*_`v)8eh84?ss)N06%bGyn212I!7)~EM!=~?9Rw7IfFTfUalqOw2q+8+qlv&H zT8lrp!p6D7>4vxSS{MXV!Gk+m2#8{Gz>|lG3UgVl!y#}j&}bGm2zR6xr#?7Fq#ovJ z0MdM+0CpmB0IH9M?AS^)Q(&j;K?1QcG>l#7XQO5ck-HC>CRIQ}!vOO)c3!dwwxbY5 z;hmwt{0$C^wBBnLc8>TIC1K7qI}-;YVs*hP-n2G59)%c@02n+s<OFnN1DYxc*AKK# zPAh^lDG_PuI8GO}OZ?aWxmw1OAy^Y^;x9q0=!Pp~)q@VeuSn-|5bF&-4FR!xTNx0T z1J&b!$$6qiM&J1kP+Ek{xcdxZr$DdlyaGod8QykSE*gomwgL$&abS88^ok~!?QiiG z;oRY%Upj3HRWu~Tn=-JGIA+=nFi@;7G6CC$7=t}{1b&O)$N15kpi8hpI0V%1Qi!|? zV13sWc?I`N1;>7lgG{;fi@eAb+?6U0JYI#s^j2-eD*CR4Aaxw@SRIIL)sr!X^F>Y` Kj)i*c)BgZOtS&DA delta 34803 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 78e97c5..0196fc2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Apr 19 12:47:30 PDT 2014 +#Sat Aug 29 12:01:59 PDT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip diff --git a/mobibot.ipr b/mobibot.ipr index fdac789..78cb19a 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -132,335 +132,46 @@ </component> <component name="IdProvider" IDEtalkID="ADF4C98C99466E4EF52C8D21B83FF938" /> <component name="InspectionProjectProfileManager"> - <profiles> - <profile version="1.0" is_locked="false"> - <option name="myName" value="Default" /> - <option name="myLocal" value="false" /> - <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="TOP_LEVEL_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="INNER_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="METHOD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> - </value> - </option> - <option name="FIELD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="IGNORE_DEPRECATED" value="false" /> - <option name="IGNORE_JAVADOC_PERIOD" value="true" /> - <option name="IGNORE_DUPLICATED_THROWS" value="false" /> - <option name="IGNORE_POINT_TO_ITSELF" value="false" /> - <option name="myAdditionalJavadocTags" value="created" /> - </inspection_tool> - <inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false"> - <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" /> - <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" /> - </inspection_tool> - </profile> - <profile version="1.0" is_locked="false"> - <option name="myName" value="Project Default" /> - <option name="myLocal" value="false" /> - <inspection_tool class="AbstractClassExtendsConcreteClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AbstractClassWithoutAbstractMethods" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AbstractMethodCallInConstructor" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AbstractMethodOverridesAbstractMethod" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AbstractMethodOverridesConcreteMethod" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="Annotation" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AnnotationClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AssertAsName" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AssertStatement" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AssignmentToCatchBlockParameter" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AssignmentToCollectionFieldFromParameter" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignorePrivateMethods" value="true" /> - </inspection_tool> - <inspection_tool class="AssignmentToMethodParameter" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreTransformationOfOriginalParameter" value="false" /> - </inspection_tool> - <inspection_tool class="AssignmentUsedAsCondition" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="AutoBoxing" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreAddedToCollection" value="false" /> - </inspection_tool> - <inspection_tool class="BreakStatementWithLabelJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="BusyWait" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="CStyleArrayDeclaration" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="CastThatLosesPrecision" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreIntegerCharCasts" value="false" /> - </inspection_tool> - <inspection_tool class="ChannelResource" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="insideTryAllowed" value="false" /> - </inspection_tool> - <inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ClassEscapesItsScope" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ClassInTopLevelPackage" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ClassMayBeInterface" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ClassNameDiffersFromFileName" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ClassReferencesSubclass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ClassWithMultipleLoggers" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="loggerNamesString" value="java.util.logging.Logger,org.slf4j.Logger,org.apache.commons.logging.Log,org.apache.log4j.Logger" /> - </inspection_tool> - <inspection_tool class="CollectionsMustHaveInitialCapacity" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="CompareToUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ConfusingFloatingPointLiteral" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ConfusingMainMethod" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ConstantDeclaredInAbstractClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ConstantDeclaredInInterface" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ConstantNamingConvention" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="onlyCheckImmutables" value="false" /> - <option name="m_regex" value="[A-Z_\d]*" /> - <option name="m_minLength" value="5" /> - <option name="m_maxLength" value="32" /> - </inspection_tool> - <inspection_tool class="ContinueStatementJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ContinueStatementWithLabel" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="DeclareCollectionAsInterface" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreLocalVariables" value="false" /> - <option name="ignorePrivateMethodsAndFields" value="false" /> - </inspection_tool> - <inspection_tool class="DefaultNotLastCaseInSwitchJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="DollarSignInName" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="DoubleCheckedLocking" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreOnVolatileVariables" value="false" /> - </inspection_tool> - <inspection_tool class="EmptyCatchBlockJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="EmptyClass" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignorableAnnotations"> - <value /> - </option> - <option name="ignoreClassWithParameterization" value="false" /> - <option name="ignoreThrowables" value="true" /> - </inspection_tool> - <inspection_tool class="EmptyFinallyBlockJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="EmptySynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="EnumAsName" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="EnumClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="EqualsAndHashcode" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="EqualsUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ExceptionFromCatchWhichDoesntWrap" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreGetMessage" value="false" /> - <option name="ignoreCantWrap" value="false" /> - </inspection_tool> - <inspection_tool class="FallthruInSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="FieldAccessedSynchronizedAndUnsynchronized" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="countGettersAndSetters" value="false" /> - </inspection_tool> - <inspection_tool class="FieldHidesSuperclassField" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreInvisibleFields" value="true" /> - </inspection_tool> - <inspection_tool class="FieldMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="FinalClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="FinalMethodInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="FloatingPointEquality" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ForLoopReplaceableByWhile" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreLoopsWithoutConditions" value="false" /> - </inspection_tool> - <inspection_tool class="HashCodeUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="IOResource" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoredTypesString" value="java.io.ByteArrayOutputStream,java.io.ByteArrayInputStream,java.io.StringBufferInputStream,java.io.CharArrayWriter,java.io.CharArrayReader,java.io.StringWriter,java.io.StringReader" /> - <option name="insideTryAllowed" value="false" /> - </inspection_tool> - <inspection_tool class="IncrementDecrementUsedAsExpression" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="InnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="InnerClassOnInterface" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreInnerInterfaces" value="false" /> - </inspection_tool> - <inspection_tool class="InnerClassVariableHidesOuterClassVariable" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreInvisibleFields" value="true" /> - </inspection_tool> - <inspection_tool class="InstanceVariableInitialization" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignorePrimitives" value="false" /> - </inspection_tool> - <inspection_tool class="InstanceVariableUninitializedUse" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignorePrimitives" value="false" /> - <option name="annotationNamesString" value="" /> - </inspection_tool> - <inspection_tool class="InstanceofCatchParameter" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="InstanceofInterfaces" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="InstanceofThis" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="IntegerDivisionInFloatingPointContext" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="InterfaceNamingConvention" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_regex" value="[A-Z][A-Za-z\d]*" /> - <option name="m_minLength" value="8" /> - <option name="m_maxLength" value="64" /> - </inspection_tool> - <inspection_tool class="JDBCResource" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="insideTryAllowed" value="false" /> - </inspection_tool> - <inspection_tool class="JUnitTestNG" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="JavaLangReflect" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="LengthOneStringsInConcatenation" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="LiteralAsArgToStringEquals" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="REPORT_VARIABLES" value="true" /> - <option name="REPORT_PARAMETERS" value="true" /> - </inspection_tool> - <inspection_tool class="LocalVariableHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreInvisibleFields" value="true" /> - <option name="m_ignoreStaticMethods" value="true" /> - </inspection_tool> - <inspection_tool class="LongLiteralsEndingWithLowercaseL" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MarkerInterface" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MethodMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_onlyPrivateOrFinal" value="false" /> - <option name="m_ignoreEmptyMethods" value="true" /> - </inspection_tool> - <inspection_tool class="MethodNamesDifferOnlyByCase" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MethodOverloadsParentMethod" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MethodOverridesPrivateMethod" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MethodOverridesStaticMethod" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_requireAnnotationsFirst" value="true" /> - </inspection_tool> - <inspection_tool class="MisspelledCompareTo" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MisspelledHashcode" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MultipleDeclaration" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreForLoopDeclarations" value="true" /> - </inspection_tool> - <inspection_tool class="MultipleTopLevelClassesInFile" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MultipleTypedDeclaration" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="MultiplyOrDivideByPowerOfTwo" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="checkDivision" value="false" /> - </inspection_tool> - <inspection_tool class="NativeMethods" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NestedConditionalExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NestedSwitchStatementJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NestedSynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NonExceptionNameEndsWithException" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NonSerializableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NonStaticFinalLogger" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="loggerClassName" value="java.util.logging.Logger" /> - </inspection_tool> - <inspection_tool class="NonStaticInnerClassInSecureContext" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NonSynchronizedMethodOverridesSynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="NoopMethodInAbstractClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ObsoleteCollection" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreRequiredObsoleteCollectionTypes" value="false" /> - </inspection_tool> - <inspection_tool class="OctalAndDecimalIntegersMixed" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="PackageVisibleField" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="PackageVisibleInnerClass" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreEnums" value="false" /> - <option name="ignoreInterfaces" value="false" /> - </inspection_tool> - <inspection_tool class="ParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreInvisibleFields" value="true" /> - <option name="m_ignoreStaticMethodParametersHidingInstanceFields" value="false" /> - <option name="m_ignoreForConstructors" value="false" /> - <option name="m_ignoreForPropertySetters" value="false" /> - <option name="m_ignoreForAbstractMethods" value="false" /> - </inspection_tool> - <inspection_tool class="ParameterNamingConvention" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_regex" value="[a-z][A-Za-z\d]*" /> - <option name="m_minLength" value="1" /> - <option name="m_maxLength" value="20" /> - </inspection_tool> - <inspection_tool class="ProtectedField" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="PublicField" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreEnums" value="false" /> - <option name="ignorableAnnotations"> - <value /> - </option> - </inspection_tool> - <inspection_tool class="PublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ReadObjectAndWriteObjectPrivate" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="ReadResolveAndWriteReplaceProtected" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="RefusedBequest" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignoreEmptySuperMethods" value="false" /> - </inspection_tool> - <inspection_tool class="ReturnOfCollectionField" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignorePrivateMethods" value="true" /> - </inspection_tool> - <inspection_tool class="ReturnThis" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="SerializableWithUnconstructableAncestor" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="SimplifiableIfStatement" enabled="false" level="WARNING" enabled_by_default="false" /> - <inspection_tool class="Singleton" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="SocketResource" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="insideTryAllowed" value="false" /> - </inspection_tool> - <inspection_tool class="StandardVariableNames" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="StaticCollection" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreWeakCollections" value="false" /> - </inspection_tool> - <inspection_tool class="StaticInheritance" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="StaticMethodNamingConvention" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_regex" value="[a-z][A-Za-z\d]*" /> - <option name="m_minLength" value="4" /> - <option name="m_maxLength" value="32" /> - </inspection_tool> - <inspection_tool class="StaticNonFinalField" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="StaticVariableInitialization" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignorePrimitives" value="false" /> - </inspection_tool> - <inspection_tool class="StaticVariableUninitializedUse" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignorePrimitives" value="false" /> - </inspection_tool> - <inspection_tool class="StringBufferMustHaveInitialCapacity" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="SubtractionInCompareTo" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="SwitchStatementWithConfusingDeclaration" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="SwitchStatementsWithoutDefault" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreFullyCoveredEnums" value="true" /> - </inspection_tool> - <inspection_tool class="SynchronizeOnThis" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="SystemOutErr" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="TailRecursionJS" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="TextLabelInSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="TransientFieldInNonSerializableClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="UNCHECKED_WARNING" enabled="false" level="WARNING" enabled_by_default="false" /> - <inspection_tool class="UncheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreStaticFieldAccesses" value="false" /> - <option name="m_ignoreStaticMethodCalls" value="false" /> - <option name="m_ignoreStaticAccessFromStaticContext" value="false" /> - </inspection_tool> - <inspection_tool class="UnnecessaryFinalOnParameter" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="onlyWarnOnAbstractMethods" value="false" /> - </inspection_tool> - <inspection_tool class="UnnecessaryFullyQualifiedName" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="m_ignoreJavadoc" value="false" /> - </inspection_tool> - <inspection_tool class="UnnecessaryInterfaceModifier" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="UnnecessarySemicolon" enabled="false" level="WARNING" enabled_by_default="false" /> - <inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="UnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false"> - <option name="ADD_MAINS_TO_ENTRIES" value="true" /> - <option name="ADD_APPLET_TO_ENTRIES" value="true" /> - <option name="ADD_SERVLET_TO_ENTRIES" value="true" /> - <option name="ADD_NONJAVA_TO_ENTRIES" value="true" /> - </inspection_tool> - <inspection_tool class="UnusedImport" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="UtilityClassWithoutPrivateConstructor" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="ignorableAnnotations"> - <value /> - </option> - <option name="ignoreClassesWithOnlyMain" value="false" /> - </inspection_tool> - <inspection_tool class="VarargParameter" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="WaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="WeakerAccess" enabled="false" level="WARNING" enabled_by_default="false"> - <option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" /> - <option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="true" /> - <option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" /> - </inspection_tool> - <inspection_tool class="ZeroLengthArrayInitialization" enabled="true" level="WARNING" enabled_by_default="true" /> - </profile> - </profiles> - <option name="PROJECT_PROFILE" /> - <option name="USE_PROJECT_PROFILE" value="false" /> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="TOP_LEVEL_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="INNER_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="METHOD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> + </value> + </option> + <option name="FIELD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="IGNORE_DEPRECATED" value="false" /> + <option name="IGNORE_JAVADOC_PERIOD" value="true" /> + <option name="IGNORE_DUPLICATED_THROWS" value="false" /> + <option name="IGNORE_POINT_TO_ITSELF" value="false" /> + <option name="myAdditionalJavadocTags" value="created" /> + </inspection_tool> + <inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false"> + <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" /> + <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" /> + </inspection_tool> + </profile> + <option name="PROJECT_PROFILE" value="Project Default" /> + <option name="USE_PROJECT_PROFILE" value="true" /> <version value="1.0" /> </component> <component name="JavadocGenerationManager"> diff --git a/mobibot.iws b/mobibot.iws index 05024de..142310f 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -34,30 +34,11 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.jar" afterPath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.jar" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iml" afterPath="$PROJECT_DIR$/mobibot.iml" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/buildnum.properties" afterPath="$PROJECT_DIR$/buildnum.properties" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.properties" afterPath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.properties" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.ipr" afterPath="$PROJECT_DIR$/mobibot.ipr" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> </list> @@ -95,9 +76,6 @@ <option name="OVERRIDE_EXISTING_TAG_FOR_PROJECT" value="false" /> <option name="TAG_AFTER_PROJECT_COMMIT_NAME" value="v0_4_6" /> </component> - <component name="DaemonCodeAnalyzer"> - <disable_hints /> - </component> <component name="DebuggerManager"> <ui_properties converted="true" /> <breakpoint_any default_suspend_policy="SuspendAll" default_condition_enabled="true" converted="true"> @@ -139,6 +117,47 @@ <breakpoint_rules converted="true" /> </component> <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> + <component name="ExternalProjectsManager"> + <system id="GRADLE"> + <state> + <task path="$PROJECT_DIR$"> + <activation /> + </task> + <projects_view> + <tree_state> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ExternalProjectsStructure$RootNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ProjectNode" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ExternalProjectsStructure$RootNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="Tasks" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.TasksNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="other" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.TasksNode$1" /> + </PATH_ELEMENT> + </PATH> + </tree_state> + </projects_view> + </state> + </system> + </component> <component name="FavoritesManager"> <favorites_list name="mobibot" /> </component> @@ -167,41 +186,11 @@ </component> <component name="FileEditorManager"> <leaf> - <file leaf-file-name="FeedReader.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1216" max-vertical-offset="1456"> - <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="GoogleSearch.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="560" max-vertical-offset="1520"> - <caret line="74" column="49" selection-start-line="74" selection-start-column="49" selection-end-line="74" selection-end-column="49" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Lookup.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="112" max-vertical-offset="2080"> - <caret line="43" column="2" selection-start-line="43" selection-start-column="2" selection-end-line="43" selection-end-column="2" /> - <folding /> - </state> - </provider> - </entry> - </file> <file leaf-file-name="CurrencyConverter.java" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1552" max-vertical-offset="3184"> - <caret line="132" column="36" selection-start-line="132" selection-start-column="36" selection-end-line="132" selection-end-column="36" /> + <state vertical-scroll-proportion="0.0"> + <caret line="148" column="12" selection-start-line="148" selection-start-column="12" selection-end-line="148" selection-end-column="12" /> <folding> <element signature="imports" expanded="true" /> </folding> @@ -209,23 +198,54 @@ </provider> </entry> </file> + <file leaf-file-name="Mobibot.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-16.034483"> + <caret line="1214" column="139" selection-start-line="1214" selection-start-column="139" selection-end-line="1214" selection-end-column="139" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="mobibot.properties" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="18" column="17" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> + <folding /> + </state> + </provider> + </entry> + </file> <file leaf-file-name="EntryComment.java" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1968"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> <folding> - <element signature="imports" expanded="true" /> + <element signature="e#2506#2507#0" expanded="false" /> + <element signature="e#2544#2545#0" expanded="false" /> + <element signature="e#2646#2647#0" expanded="false" /> + <element signature="e#2667#2668#0" expanded="false" /> + <element signature="e#2832#2833#0" expanded="false" /> + <element signature="e#2861#2862#0" expanded="false" /> + <element signature="e#3011#3012#0" expanded="false" /> + <element signature="e#3029#3030#0" expanded="false" /> + <element signature="e#3159#3160#0" expanded="false" /> + <element signature="e#3177#3178#0" expanded="false" /> + <element signature="e#3321#3322#0" expanded="false" /> + <element signature="e#3344#3345#0" expanded="false" /> </folding> </state> </provider> </entry> </file> - <file leaf-file-name="Commands.java" pinned="false" current-in-tab="true"> + <file leaf-file-name="Commands.java" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.96961063" vertical-offset="2931" max-vertical-offset="4160"> - <caret line="247" column="37" selection-start-line="247" selection-start-column="37" selection-end-line="247" selection-end-column="37" /> + <state vertical-scroll-proportion="0.0"> + <caret line="87" column="80" selection-start-line="87" selection-start-column="47" selection-end-line="87" selection-end-column="47" /> <folding> <element signature="e#0#5577#0" expanded="true" /> </folding> @@ -233,22 +253,25 @@ </provider> </entry> </file> - <file leaf-file-name="Quote.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="512" max-vertical-offset="1280"> - <caret line="70" column="47" selection-start-line="70" selection-start-column="47" selection-end-line="70" selection-end-column="47" /> - <folding /> - </state> - </provider> - </entry> - </file> <file leaf-file-name="SwingWorker.java" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1728" max-vertical-offset="2432"> + <state vertical-scroll-proportion="0.0"> <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> - <folding /> + <folding> + <element signature="e#838#839#0" expanded="false" /> + <element signature="e#859#860#0" expanded="false" /> + <element signature="e#2201#2202#0" expanded="false" /> + <element signature="e#2220#2221#0" expanded="false" /> + <element signature="e#2353#2354#0" expanded="false" /> + <element signature="e#2369#2370#0" expanded="false" /> + <element signature="e#2950#2951#0" expanded="false" /> + <element signature="e#2969#2970#0" expanded="false" /> + <element signature="e#3002#3003#0" expanded="false" /> + <element signature="e#3024#3025#0" expanded="false" /> + <element signature="e#3057#3058#0" expanded="false" /> + <element signature="e#3079#3080#0" expanded="false" /> + </folding> </state> </provider> </entry> @@ -256,8 +279,31 @@ <file leaf-file-name="ReleaseInfo.java" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1040" max-vertical-offset="1184"> + <state vertical-scroll-proportion="0.0"> <caret line="112" column="5" selection-start-line="112" selection-start-column="5" selection-end-line="112" selection-end-column="5" /> + <folding> + <element signature="e#3167#3168#0" expanded="true" /> + <element signature="e#3188#3189#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="build.gradle" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/build.gradle"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="72" column="90" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + </file> + <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="true"> + <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.030739672"> + <caret line="2" column="17" selection-start-line="2" selection-start-column="17" selection-end-line="2" selection-end-column="17" /> <folding /> </state> </provider> @@ -266,9 +312,12 @@ <file leaf-file-name="StockQuote.java" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1936" max-vertical-offset="2096"> + <state vertical-scroll-proportion="0.0"> <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> + <folding> + <element signature="e#0#4638#0" expanded="false" /> + <element signature="imports" expanded="false" /> + </folding> </state> </provider> </entry> @@ -281,6 +330,7 @@ </FindUsagesManager> </component> <component name="Git.Settings"> + <option name="ROOT_SYNC" value="DONT_SYNC" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> </component> <component name="GitLogSettings"> @@ -723,6 +773,9 @@ <list> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> + <option value="$PROJECT_DIR$/buildnum.properties" /> </list> </option> </component> @@ -735,9 +788,17 @@ <property name="poll-interval" value="500" /> <property name="external-in-idea" value="false" /> </component> + <component name="JsBuildToolGruntFileManager" detection-done="true" /> <component name="JsGulpfileManager"> <detection-done>true</detection-done> </component> + <component name="MavenImportPreferences"> + <option name="generalSettings"> + <MavenGeneralSettings> + <option name="mavenHome" value="Bundled (Maven 3)" /> + </MavenGeneralSettings> + </option> + </component> <component name="MavenProjectNavigator"> <treeState /> </component> @@ -745,6 +806,9 @@ <option name="LAST_EDITED_MODULE_NAME" value="mobibot" /> <option name="LAST_EDITED_TAB_NAME" value="Libraries (Classpath)" /> </component> + <component name="NamedScopeManager"> + <order /> + </component> <component name="PackagesPane"> <subPane> <PATH> @@ -784,7 +848,7 @@ <option name="x" value="1592" /> <option name="y" value="-8" /> <option name="width" value="1616" /> - <option name="height" value="1216" /> + <option name="height" value="1204" /> </component> <component name="ProjectInspectionProfilesVisibleTreeState"> <entry key="Default"> @@ -1629,6 +1693,7 @@ </PATH> </subPane> </pane> + <pane id="Scratches" /> <pane id="Favorites" /> </panes> </component> @@ -1677,14 +1742,14 @@ <property name="options.lastSelected" value="copyright.profiles" /> <property name="project.structure.side.proportion" value="0.2" /> <property name="MemberChooser.copyJavadoc" value="false" /> - <property name="project.structure.last.edited" value="Modules" /> + <property name="project.structure.last.edited" value="SDKs" /> <property name="vcs_file_view_treeWidth4" value="100" /> <property name="project.structure.proportion" value="0.15" /> <property name="vcs_file_view_treeWidth1" value="101" /> <property name="vcs_file_view_treeWidth0" value="100" /> <property name="vcs_file_view_treeWidth3" value="101" /> <property name="vcs_file_view_treeWidth2" value="100" /> - <property name="last_opened_file_path" value="K:/TestGradle/TestGradle.ipr" /> + <property name="last_opened_file_path" value="C:/Program Files/Git/bin/git.exe" /> <property name="GoToClass.toSaveIncludeLibraries" value="false" /> <property name="cvs_file_history_treeWidth0" value="135" /> <property name="cvs_file_history_treeWidth1" value="135" /> @@ -1711,6 +1776,8 @@ <property name="OverrideImplement.overriding.sorted" value="false" /> <property name="LayoutCode.rearrangeEntries" value="true" /> <property name="aspect.path.notification.shown" value="true" /> + <property name="settings.editor.selected.configurable" value="vcs.Git" /> + <property name="settings.editor.splitter.proportion" value="0.2" /> </component> <component name="RecentsManager"> <key name="CopyClassDialog.RECENTS_KEY"> @@ -1739,27 +1806,7 @@ <option name="referencePos" value="0" /> <option name="showLabels" value="true" /> </component> - <component name="RunManager" selected="Application.Mobibot"> - <configuration default="false" name="mobibot [compileJava]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="compileJava" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> - <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> - <method /> - </configuration> + <component name="RunManager" selected="Gradle.mobibot [release]"> <configuration default="false" name="mobibot [distZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> <option name="executionName" /> @@ -1820,34 +1867,40 @@ <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> <method /> </configuration> - <configuration default="true" type="Remote" factoryName="Remote"> - <option name="USE_SOCKET_TRANSPORT" value="true" /> - <option name="SERVER_MODE" value="false" /> - <option name="SHMEM_ADDRESS" value="javadebug" /> - <option name="HOST" value="localhost" /> - <option name="PORT" value="5005" /> + <configuration default="false" name="mobibot [deploy]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="deploy" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> <method /> </configuration> - <configuration default="true" type="JUnit" factoryName="JUnit"> - <extension name="coverage" enabled="false" merge="false" runner="idea" /> - <extension name="snapshooter" /> - <module name="" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="PACKAGE_NAME" /> - <option name="MAIN_CLASS_NAME" /> - <option name="METHOD_NAME" /> - <option name="TEST_OBJECT" /> - <option name="VM_PARAMETERS" /> - <option name="PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <option name="TEST_SEARCH_SCOPE"> - <value defaultName="wholeProject" /> - </option> - <envs /> - <patterns /> + <configuration default="false" name="mobibot [release]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="release" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> <method /> </configuration> <configuration default="true" type="AndroidRunConfigurationType" factoryName="Android Application"> @@ -1870,87 +1923,34 @@ <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> <method /> </configuration> - <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> - <option name="INTERPRETER_OPTIONS" value="" /> - <option name="WORKING_DIRECTORY" value="" /> - <option name="PARENT_ENVS" value="true" /> - <envs /> + <configuration default="true" type="AndroidTestRunConfigurationType" factoryName="Android Tests"> <module name="" /> - <option name="SCRIPT_NAME" value="" /> - <option name="PARAMETERS" value="" /> + <option name="TESTING_TYPE" value="0" /> + <option name="INSTRUMENTATION_RUNNER_CLASS" value="" /> + <option name="METHOD_NAME" value="" /> + <option name="CLASS_NAME" value="" /> + <option name="PACKAGE_NAME" value="" /> + <option name="TARGET_SELECTION_MODE" value="EMULATOR" /> + <option name="USE_LAST_SELECTED_DEVICE" value="false" /> + <option name="PREFERRED_AVD" value="" /> + <option name="USE_COMMAND_LINE" value="true" /> + <option name="COMMAND_LINE" value="" /> + <option name="WIPE_USER_DATA" value="false" /> + <option name="DISABLE_BOOT_ANIMATION" value="false" /> + <option name="NETWORK_SPEED" value="full" /> + <option name="NETWORK_LATENCY" value="none" /> + <option name="CLEAR_LOGCAT" value="false" /> + <option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" /> + <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> <method /> </configuration> - <configuration default="true" type="WebLogic Instance" factoryName="Local" ALTERNATIVE_JRE_ENABLED="false"> - <option name="PORT" value="7001" /> - <deployment /> - <server-settings> - <option name="DOMAIN_PATH" value="" /> - <option name="SERVER_NAME" value="myserver" /> - <option name="DOMAIN_NAME" value="mydomain" /> - <option name="ADMIN_SERVER_HOST" value="localhost" /> - <option name="ADMIN_SERVER_PORT" value="7001" /> - <option name="TARGET_TYPE_NAME" value="TYPE_SERVER" /> - <option name="CLUSTER_NAME" value="mycluster" /> - <option name="CONNECT_TO_MANAGED_SERVER" value="false" /> - <option name="USERNAME" value="weblogic" /> - <option name="PASSWORD" value="weblogic" /> - </server-settings> - <predefined_log_file id="WEBLOGIC_DOMAIN_LOG_FILE" enabled="true" /> - <predefined_log_file id="WEBLOGIC_SERVER_LOG_FILE" enabled="true" /> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <method /> - </configuration> - <configuration default="true" type="GrailsRunConfigurationType" factoryName="Grails"> - <module name="" /> - <setting name="vmparams" value="" /> - <setting name="cmdLine" value="run-app" /> - <setting name="depsClasspath" value="false" /> - <setting name="passParentEnv" value="true" /> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <setting name="launchBrowser" value="false" /> - <method /> - </configuration> - <configuration default="true" type="JarApplication" factoryName="JAR Application"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="GroovyScriptRunConfiguration" factoryName="Groovy"> - <module name="" /> - <setting name="path" value="" /> - <setting name="vmparams" value="" /> - <setting name="params" value="" /> - <setting name="workDir" value="file://$PROJECT_DIR$" /> - <setting name="debug" value="false" /> - <method /> - </configuration> - <configuration default="true" type="TestNG" factoryName="TestNG"> - <extension name="coverage" enabled="false" merge="false" runner="idea" /> - <extension name="snapshooter" /> - <module name="" /> + <configuration default="true" type="Applet" factoryName="Applet"> + <module /> + <option name="HTML_USED" value="false" /> + <option name="WIDTH" value="400" /> + <option name="HEIGHT" value="300" /> + <option name="POLICY_FILE" value="C:/IntelliJ-IDEA/bin/appletviewer.policy" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="SUITE_NAME" /> - <option name="PACKAGE_NAME" /> - <option name="MAIN_CLASS_NAME" /> - <option name="METHOD_NAME" /> - <option name="GROUP_NAME" /> - <option name="TEST_OBJECT" value="CLASS" /> - <option name="VM_PARAMETERS" /> - <option name="PARAMETERS" /> - <option name="WORKING_DIRECTORY" /> - <option name="OUTPUT_DIRECTORY" /> - <option name="ANNOTATION_TYPE" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <option name="TEST_SEARCH_SCOPE"> - <value defaultName="wholeProject" /> - </option> - <option name="USE_DEFAULT_REPORTERS" value="false" /> - <option name="PROPERTIES_FILE" /> - <envs /> - <properties /> - <listeners /> <method /> </configuration> <configuration default="true" type="Application" factoryName="Application"> @@ -1968,9 +1968,6 @@ <envs /> <method /> </configuration> - <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js"> - <method /> - </configuration> <configuration default="true" type="BashConfigurationType" factoryName="Bash"> <option name="INTERPRETER_OPTIONS" value="" /> <option name="INTERPRETER_PATH" value="" /> @@ -1982,18 +1979,33 @@ <envs /> <method /> </configuration> - <configuration default="true" type="FlexUnitRunConfigurationType" factoryName="FlexUnit" appDescriptorForEmulator="Android" class_name="" emulatorAdlOptions="" method_name="" package_name="" scope="Class"> - <option name="BCName" value="" /> - <option name="launcherParameters"> - <LauncherParameters> - <option name="browser" value="a7bb68e0-33c0-4d6f-a81a-aac1fdb870c8" /> - <option name="launcherType" value="OSDefault" /> - <option name="newPlayerInstance" value="false" /> - <option name="playerPath" value="FlashPlayerDebugger.exe" /> - </LauncherParameters> - </option> - <option name="moduleName" value="" /> - <option name="trusted" value="true" /> + <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="PARENT_ENVS" value="true" /> + <envs /> + <module name="" /> + <option name="SCRIPT_NAME" value="" /> + <option name="PARAMETERS" value="" /> + <method /> + </configuration> + <configuration default="true" type="CucumberJavaRunConfigurationType" factoryName="Cucumber java"> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <option name="myFilePath" /> + <option name="GLUE" /> + <option name="myNameFilter" /> + <option name="myGeneratedName" /> + <option name="MAIN_CLASS_NAME" /> + <option name="VM_PARAMETERS" /> + <option name="PROGRAM_PARAMETERS" /> + <option name="WORKING_DIRECTORY" /> + <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> + <option name="ALTERNATIVE_JRE_PATH" /> + <option name="ENABLE_SWING_INSPECTOR" value="false" /> + <option name="ENV_VARIABLES" /> + <option name="PASS_PARENT_ENVS" value="true" /> + <module name="" /> + <envs /> <method /> </configuration> <configuration default="true" type="FlashRunConfigurationType" factoryName="Flash App"> @@ -2031,28 +2043,18 @@ <option name="usbDebugPort" value="7936" /> <method /> </configuration> - <configuration default="true" type="AndroidTestRunConfigurationType" factoryName="Android Tests"> - <module name="" /> - <option name="TESTING_TYPE" value="0" /> - <option name="INSTRUMENTATION_RUNNER_CLASS" value="" /> - <option name="METHOD_NAME" value="" /> - <option name="CLASS_NAME" value="" /> - <option name="PACKAGE_NAME" value="" /> - <option name="TARGET_SELECTION_MODE" value="EMULATOR" /> - <option name="USE_LAST_SELECTED_DEVICE" value="false" /> - <option name="PREFERRED_AVD" value="" /> - <option name="USE_COMMAND_LINE" value="true" /> - <option name="COMMAND_LINE" value="" /> - <option name="WIPE_USER_DATA" value="false" /> - <option name="DISABLE_BOOT_ANIMATION" value="false" /> - <option name="NETWORK_SPEED" value="full" /> - <option name="NETWORK_LATENCY" value="none" /> - <option name="CLEAR_LOGCAT" value="false" /> - <option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" /> - <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> - <method /> - </configuration> - <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug"> + <configuration default="true" type="FlexUnitRunConfigurationType" factoryName="FlexUnit" appDescriptorForEmulator="Android" class_name="" emulatorAdlOptions="" method_name="" package_name="" scope="Class"> + <option name="BCName" value="" /> + <option name="launcherParameters"> + <LauncherParameters> + <option name="browser" value="a7bb68e0-33c0-4d6f-a81a-aac1fdb870c8" /> + <option name="launcherType" value="OSDefault" /> + <option name="newPlayerInstance" value="false" /> + <option name="playerPath" value="FlashPlayerDebugger.exe" /> + </LauncherParameters> + </option> + <option name="moduleName" value="" /> + <option name="trusted" value="true" /> <method /> </configuration> <configuration default="true" type="GradleRunConfiguration" factoryName="Gradle"> @@ -2071,38 +2073,133 @@ </ExternalSystemSettings> <method /> </configuration> - <configuration default="true" type="Applet" factoryName="Applet"> + <configuration default="true" type="GrailsRunConfigurationType" factoryName="Grails"> <module name="" /> - <option name="MAIN_CLASS_NAME" /> - <option name="HTML_FILE_NAME" /> - <option name="HTML_USED" value="false" /> - <option name="WIDTH" value="400" /> - <option name="HEIGHT" value="300" /> - <option name="POLICY_FILE" value="C:/IntelliJ-IDEA/bin/appletviewer.policy" /> - <option name="VM_PARAMETERS" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> + <setting name="vmparams" value="" /> + <setting name="cmdLine" value="run-app" /> + <setting name="depsClasspath" value="false" /> + <setting name="passParentEnv" value="true" /> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <setting name="launchBrowser" value="false" /> <method /> </configuration> - <configuration default="true" type="CucumberJavaRunConfigurationType" factoryName="Cucumber java"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <option name="myFilePath" /> - <option name="GLUE" /> - <option name="myNameFilter" /> - <option name="myGeneratedName" /> - <option name="MAIN_CLASS_NAME" /> - <option name="VM_PARAMETERS" /> - <option name="PROGRAM_PARAMETERS" /> - <option name="WORKING_DIRECTORY" /> + <configuration default="true" type="GroovyScriptRunConfiguration" factoryName="Groovy"> + <module name="" /> + <setting name="path" value="" /> + <setting name="vmparams" value="" /> + <setting name="params" value="" /> + <setting name="workDir" value="file://$PROJECT_DIR$" /> + <setting name="debug" value="false" /> + <method /> + </configuration> + <configuration default="true" type="JUnit" factoryName="JUnit"> + <extension name="coverage" enabled="false" merge="false" runner="idea" /> + <extension name="snapshooter" /> + <module name="" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH" /> - <option name="ENABLE_SWING_INSPECTOR" value="false" /> + <option name="PACKAGE_NAME" /> + <option name="MAIN_CLASS_NAME" /> + <option name="METHOD_NAME" /> + <option name="TEST_OBJECT" /> + <option name="VM_PARAMETERS" /> + <option name="PARAMETERS" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> <option name="ENV_VARIABLES" /> <option name="PASS_PARENT_ENVS" value="true" /> + <option name="TEST_SEARCH_SCOPE"> + <value defaultName="wholeProject" /> + </option> + <envs /> + <patterns /> + <method /> + </configuration> + <configuration default="true" type="JarApplication" factoryName="JAR Application"> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <envs /> + <method /> + </configuration> + <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug"> + <method /> + </configuration> + <configuration default="true" type="Remote" factoryName="Remote"> + <option name="USE_SOCKET_TRANSPORT" value="true" /> + <option name="SERVER_MODE" value="false" /> + <option name="SHMEM_ADDRESS" value="javadebug" /> + <option name="HOST" value="localhost" /> + <option name="PORT" value="5005" /> + <method /> + </configuration> + <configuration default="true" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot"> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <module name="" /> <envs /> <method /> </configuration> + <configuration default="true" type="TestNG" factoryName="TestNG"> + <extension name="coverage" enabled="false" merge="false" runner="idea" /> + <extension name="snapshooter" /> + <module name="" /> + <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> + <option name="ALTERNATIVE_JRE_PATH" /> + <option name="SUITE_NAME" /> + <option name="PACKAGE_NAME" /> + <option name="MAIN_CLASS_NAME" /> + <option name="METHOD_NAME" /> + <option name="GROUP_NAME" /> + <option name="TEST_OBJECT" value="CLASS" /> + <option name="VM_PARAMETERS" /> + <option name="PARAMETERS" /> + <option name="WORKING_DIRECTORY" /> + <option name="OUTPUT_DIRECTORY" /> + <option name="ANNOTATION_TYPE" /> + <option name="ENV_VARIABLES" /> + <option name="PASS_PARENT_ENVS" value="true" /> + <option name="TEST_SEARCH_SCOPE"> + <value defaultName="wholeProject" /> + </option> + <option name="USE_DEFAULT_REPORTERS" value="false" /> + <option name="PROPERTIES_FILE" /> + <envs /> + <properties /> + <listeners /> + <method /> + </configuration> + <configuration default="true" type="WebLogic Instance" factoryName="Local" ALTERNATIVE_JRE_ENABLED="false"> + <option name="PORT" value="7001" /> + <deployment /> + <server-settings> + <option name="DOMAIN_PATH" value="" /> + <option name="SERVER_NAME" value="myserver" /> + <option name="DOMAIN_NAME" value="mydomain" /> + <option name="ADMIN_SERVER_HOST" value="localhost" /> + <option name="ADMIN_SERVER_PORT" value="7001" /> + <option name="TARGET_TYPE_NAME" value="TYPE_SERVER" /> + <option name="CLUSTER_NAME" value="mycluster" /> + <option name="CONNECT_TO_MANAGED_SERVER" value="false" /> + <option name="USERNAME" value="weblogic" /> + <option name="PASSWORD" value="weblogic" /> + </server-settings> + <predefined_log_file id="WEBLOGIC_DOMAIN_LOG_FILE" enabled="true" /> + <predefined_log_file id="WEBLOGIC_SERVER_LOG_FILE" enabled="true" /> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <method /> + </configuration> + <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js"> + <node-options /> + <gulpfile /> + <tasks /> + <arguments /> + <pass-parent-envs>true</pass-parent-envs> + <envs /> + <method /> + </configuration> + <configuration default="true" type="osgi.bnd.run" factoryName="Run Launcher"> + <method /> + </configuration> + <configuration default="true" type="osgi.bnd.run" factoryName="Test Launcher (JUnit)"> + <method /> + </configuration> <configuration default="false" name="Mobibot" type="Application" factoryName="Application" show_console_on_std_out="true" show_console_on_std_err="true"> <log_file path="$PROJECT_DIR$/mobibot.log" checked="true" skipped="true" show_all="false" alias="K:\java\mobibot\mobibot.log" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> @@ -2144,20 +2241,22 @@ <ConfigurationWrapper RunnerId="Run" /> <method /> </configuration> - <list size="6"> + <list size="7"> <item index="0" class="java.lang.String" itemvalue="Application.Mobibot" /> <item index="1" class="java.lang.String" itemvalue="Application.TwitterOAuth" /> - <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> - <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> - <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> - <item index="5" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> + <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> + <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> + <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> + <item index="5" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> + <item index="6" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> </list> <recent_temporary> - <list size="4"> - <item index="0" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> - <item index="1" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> - <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> - <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [compileJava]" /> + <list size="5"> + <item index="0" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> + <item index="1" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> + <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> + <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> + <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> </list> </recent_temporary> </component> @@ -2318,11 +2417,13 @@ <workItem from="1422608665004" duration="181000" /> <workItem from="1422608953671" duration="213000" /> <workItem from="1422609958367" duration="95000" /> + <workItem from="1440871303975" duration="34000" /> + <workItem from="1440873148254" duration="1946000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="255390000" /> + <option name="totallyTimeSpent" value="257370000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2345,31 +2446,31 @@ </todo-panel> </component> <component name="ToolWindowManager"> - <frame x="1592" y="-8" width="1616" height="1216" extended-state="6" /> + <frame x="1592" y="-8" width="1616" height="1204" extended-state="0" /> <editor active="true" /> <layout> - <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> + <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43685687" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32692307" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.19625" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> - <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.2751678" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.2750234" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18055555" sideWeight="0.5289929" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> + <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3988658" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> + <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> @@ -2395,9 +2496,9 @@ <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3295562" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3988658" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43761814" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> + <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> + <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> </layout> <layout-to-restore> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> @@ -2470,9 +2571,6 @@ <option name="myLimit" value="2678400000" /> </component> <component name="VcsManagerConfiguration"> - <option name="myTodoPanelSettings"> - <TodoPanelSettings /> - </option> <option name="PERFORM_UPDATE_IN_BACKGROUND" value="false" /> <option name="PERFORM_COMMIT_IN_BACKGROUND" value="false" /> <option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="true" /> @@ -2548,190 +2646,186 @@ <component name="editorHistoryManager"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1936" max-vertical-offset="2096"> + <state vertical-scroll-proportion="0.0"> <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> + <folding> + <element signature="e#0#4638#0" expanded="false" /> + <element signature="imports" expanded="false" /> + </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="4012"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="6613"> + <state vertical-scroll-proportion="0.0"> <caret line="295" column="19" selection-start-line="292" selection-start-column="4" selection-end-line="295" selection-end-column="17" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/mobibot.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="221" max-vertical-offset="374"> + <state vertical-scroll-proportion="0.0"> <caret line="13" column="1" selection-start-line="13" selection-start-column="1" selection-end-line="13" selection-end-column="1" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="408" max-vertical-offset="1462"> + <state vertical-scroll-proportion="0.0"> <caret line="24" column="42" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/buildnum.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="51" max-vertical-offset="153"> + <state vertical-scroll-proportion="0.0"> <caret line="3" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2346"> + <state vertical-scroll-proportion="0.0"> <caret line="59" column="13" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2244" max-vertical-offset="2720"> + <state vertical-scroll-proportion="0.0"> <caret line="132" column="88" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="4029" max-vertical-offset="7837"> + <state vertical-scroll-proportion="0.0"> <caret line="237" column="63" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/mobibot.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="374"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/licenses/OstermillerUtil License.txt"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="0"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="9" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/README.txt"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-8.16" vertical-offset="0" max-vertical-offset="391"> + <state vertical-scroll-proportion="-8.16"> <caret line="12" column="4" selection-start-line="12" selection-start-column="4" selection-end-line="12" selection-end-column="4" /> </state> </provider> </entry> <entry file="file://K:/Gradle/build.gradle"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.5271318" vertical-offset="17" max-vertical-offset="1462"> + <state vertical-scroll-proportion="0.5271318"> <caret line="29" column="49" selection-start-line="29" selection-start-column="38" selection-end-line="29" selection-end-column="38" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/.gitignore"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.3242972" vertical-offset="0" max-vertical-offset="996"> + <state vertical-scroll-proportion="0.3242972"> <caret line="19" column="12" selection-start-line="19" selection-start-column="12" selection-end-line="19" selection-end-column="12" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/licenses/License.txt"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.035789475" vertical-offset="0" max-vertical-offset="950"> + <state vertical-scroll-proportion="0.035789475"> <caret line="2" column="23" selection-start-line="2" selection-start-column="23" selection-end-line="2" selection-end-column="23" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/website/simple.css"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.73846155" vertical-offset="11" max-vertical-offset="986"> + <state vertical-scroll-proportion="0.73846155"> <caret line="43" column="83" selection-start-line="43" selection-start-column="1" selection-end-line="43" selection-end-column="1" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/website/index.html"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.3747495" vertical-offset="922" max-vertical-offset="1920"> + <state vertical-scroll-proportion="0.3747495"> <caret line="81" column="55" selection-start-line="0" selection-start-column="0" selection-end-line="114" selection-end-column="0" /> </state> </provider> </entry> <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/User.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.33333334" vertical-offset="984" max-vertical-offset="2448"> + <state vertical-scroll-proportion="0.33333334"> <caret line="88" column="18" selection-start-line="88" selection-start-column="18" selection-end-line="88" selection-end-column="18" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153"> - <caret line="3" column="0" selection-start-line="3" selection-start-column="0" selection-end-line="3" selection-end-column="0" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/settings.gradle"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="272"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/com/sun/syndication/feed/synd/SyndEntry.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="2575" max-vertical-offset="6290"> + <state vertical-scroll-proportion="0.0"> <caret line="184" column="9" selection-start-line="184" selection-start-column="9" selection-end-line="184" selection-end-column="9" /> </state> </provider> </entry> <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/PircBot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.3323077" vertical-offset="39751" max-vertical-offset="50932"> + <state vertical-scroll-proportion="0.3323077"> <caret line="2424" column="18" selection-start-line="2424" selection-start-column="18" selection-end-line="2424" selection-end-column="18" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/mobibot.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="374"> + <state vertical-scroll-proportion="0.0"> <caret line="16" column="7" selection-start-line="16" selection-start-column="7" selection-end-line="16" selection-end-column="7" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="391" max-vertical-offset="1343"> + <state vertical-scroll-proportion="0.0"> <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1332" max-vertical-offset="6358"> + <state vertical-scroll-proportion="0.0"> <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="848" max-vertical-offset="1785"> + <state vertical-scroll-proportion="0.0"> <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2125"> + <state vertical-scroll-proportion="0.0"> <caret line="77" column="57" selection-start-line="77" selection-start-column="57" selection-end-line="77" selection-end-column="57" /> <folding /> </state> @@ -2739,98 +2833,77 @@ </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-10.75" vertical-offset="923" max-vertical-offset="2669"> + <state vertical-scroll-proportion="-10.75"> <caret line="109" column="19" selection-start-line="109" selection-start-column="17" selection-end-line="109" selection-end-column="19" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-31.52" vertical-offset="1031" max-vertical-offset="2091"> - <caret line="107" column="5" selection-start-line="107" selection-start-column="5" selection-end-line="107" selection-end-column="5" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.32041883" vertical-offset="0" max-vertical-offset="955"> - <caret line="18" column="17" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/properties/fetcher.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="102"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/properties/log4j.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="408"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/log4j.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.38931298" vertical-offset="0" max-vertical-offset="524"> + <state vertical-scroll-proportion="0.38931298"> <caret line="12" column="67" selection-start-line="12" selection-start-column="42" selection-end-line="12" selection-end-column="42" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1200" max-vertical-offset="3376"> + <state vertical-scroll-proportion="0.0"> <caret line="75" column="5" selection-start-line="75" selection-start-column="5" selection-end-line="75" selection-end-column="5" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="5152" max-vertical-offset="6368"> + <state vertical-scroll-proportion="0.0"> <caret line="322" column="40" selection-start-line="322" selection-start-column="40" selection-end-line="322" selection-end-column="40" /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="41216" max-vertical-offset="45456"> - <caret line="2576" column="47" selection-start-line="2576" selection-start-column="41" selection-end-line="2576" selection-end-column="47" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="6112"> + <state vertical-scroll-proportion="0.0"> <caret line="33" column="7" selection-start-line="33" selection-start-column="0" selection-end-line="33" selection-end-column="7" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2128"> + <state vertical-scroll-proportion="0.0"> <caret line="55" column="74" selection-start-line="55" selection-start-column="45" selection-end-line="55" selection-end-column="74" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="37" max-vertical-offset="1056"> + <state vertical-scroll-proportion="0.0"> <caret line="49" column="45" selection-start-line="49" selection-start-column="45" selection-end-line="49" selection-end-column="45" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1568" max-vertical-offset="2560"> + <state vertical-scroll-proportion="0.0"> <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="176" max-vertical-offset="1200"> + <state vertical-scroll-proportion="0.0"> <caret line="17" column="24" selection-start-line="17" selection-start-column="24" selection-end-line="17" selection-end-column="24" /> <folding /> </state> @@ -2838,90 +2911,164 @@ </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="560" max-vertical-offset="1520"> + <state vertical-scroll-proportion="0.0"> <caret line="74" column="49" selection-start-line="74" selection-start-column="49" selection-end-line="74" selection-end-column="49" /> - <folding /> + <folding> + <element signature="e#0#3762#0" expanded="false" /> + <element signature="imports" expanded="false" /> + </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="112" max-vertical-offset="2080"> + <state vertical-scroll-proportion="0.0"> <caret line="43" column="2" selection-start-line="43" selection-start-column="2" selection-end-line="43" selection-end-column="2" /> - <folding /> + <folding> + <element signature="e#0#4204#0" expanded="false" /> + <element signature="imports" expanded="false" /> + </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="512" max-vertical-offset="1280"> + <state vertical-scroll-proportion="0.0"> <caret line="70" column="47" selection-start-line="70" selection-start-column="47" selection-end-line="70" selection-end-column="47" /> - <folding /> + <folding> + <element signature="e#0#3499#0" expanded="false" /> + <element signature="imports" expanded="false" /> + </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="464" max-vertical-offset="1456"> + <state vertical-scroll-proportion="0.0"> <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1680"> - <caret line="112" column="5" selection-start-line="112" selection-start-column="5" selection-end-line="112" selection-end-column="5" /> - <folding /> + <folding> + <element signature="e#0#3642#0" expanded="false" /> + <element signature="imports" expanded="false" /> + </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1152" max-vertical-offset="2160"> + <state vertical-scroll-proportion="0.0"> <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> + <folding> + <element signature="e#0#4638#0" expanded="false" /> + <element signature="imports" expanded="false" /> + </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1328" max-vertical-offset="2320"> + <state vertical-scroll-proportion="0.0"> <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="1552" max-vertical-offset="3200"> - <caret line="132" column="36" selection-start-line="132" selection-start-column="36" selection-end-line="132" selection-end-column="36" /> <folding> - <element signature="imports" expanded="true" /> + <element signature="e#838#839#0" expanded="false" /> + <element signature="e#859#860#0" expanded="false" /> + <element signature="e#2201#2202#0" expanded="false" /> + <element signature="e#2220#2221#0" expanded="false" /> + <element signature="e#2353#2354#0" expanded="false" /> + <element signature="e#2369#2370#0" expanded="false" /> + <element signature="e#2950#2951#0" expanded="false" /> + <element signature="e#2969#2970#0" expanded="false" /> + <element signature="e#3002#3003#0" expanded="false" /> + <element signature="e#3024#3025#0" expanded="false" /> + <element signature="e#3057#3058#0" expanded="false" /> + <element signature="e#3079#3080#0" expanded="false" /> </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1968"> + <state vertical-scroll-proportion="0.0"> <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> <folding> - <element signature="imports" expanded="true" /> + <element signature="e#2506#2507#0" expanded="false" /> + <element signature="e#2544#2545#0" expanded="false" /> + <element signature="e#2646#2647#0" expanded="false" /> + <element signature="e#2667#2668#0" expanded="false" /> + <element signature="e#2832#2833#0" expanded="false" /> + <element signature="e#2861#2862#0" expanded="false" /> + <element signature="e#3011#3012#0" expanded="false" /> + <element signature="e#3029#3030#0" expanded="false" /> + <element signature="e#3159#3160#0" expanded="false" /> + <element signature="e#3177#3178#0" expanded="false" /> + <element signature="e#3321#3322#0" expanded="false" /> + <element signature="e#3344#3345#0" expanded="false" /> </folding> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.96961063" vertical-offset="2931" max-vertical-offset="4160"> - <caret line="247" column="37" selection-start-line="247" selection-start-column="37" selection-end-line="247" selection-end-column="37" /> + <state vertical-scroll-proportion="0.0"> + <caret line="87" column="80" selection-start-line="87" selection-start-column="47" selection-end-line="87" selection-end-column="47" /> <folding> <element signature="e#0#5577#0" expanded="true" /> </folding> </state> </provider> </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="148" column="12" selection-start-line="148" selection-start-column="12" selection-end-line="148" selection-end-column="12" /> + <folding> + <element signature="imports" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-16.034483"> + <caret line="1214" column="139" selection-start-line="1214" selection-start-column="139" selection-end-line="1214" selection-end-column="139" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="18" column="17" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="112" column="5" selection-start-line="112" selection-start-column="5" selection-end-line="112" selection-end-column="5" /> + <folding> + <element signature="e#3167#3168#0" expanded="true" /> + <element signature="e#3188#3189#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/build.gradle"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="72" column="90" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.030739672"> + <caret line="2" column="17" selection-start-line="2" selection-start-column="17" selection-end-line="2" selection-end-column="17" /> + <folding /> + </state> + </provider> + </entry> </component> <component name="ideajad"> <property name="annotate" value="false" /> diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index f5936c9..4996b63 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -98,7 +98,7 @@ public class CurrencyConverter implements Runnable */ public final void run() { - if (Utils.isValidString(sender) && Utils.isValidString(query)) + if (Utils.isValidString(sender)) { if (EXCHANGE_RATES.isEmpty()) { @@ -140,71 +140,76 @@ public class CurrencyConverter implements Runnable } } - if (!EXCHANGE_RATES.isEmpty()) + if (EXCHANGE_RATES.isEmpty()) { - if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) + bot.getLogger().debug("The exchange rate table is empty."); + bot.send(sender, "Sorry, but the exchange rate table is empty."); + } + else if (Utils.isValidString(query)) + { + if (!EXCHANGE_RATES.isEmpty()) { - final String[] cmds = query.split(" "); - - if (cmds.length == 4) + if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { - if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) - { - bot.send(sender, "You're kidding, right?"); - } - else - { - try - { - final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); - final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); - final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); + final String[] cmds = query.split(" "); - bot.send(bot.getChannel(), - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' + - cmds[1].toUpperCase() + " = " + - NumberFormat.getCurrencyInstance(Locale.US).format((amt * to) / from) - .substring(1) + ' ' + cmds[3].toUpperCase()); - } - catch (NullPointerException ignored) + if (cmds.length == 4) + { + if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) { - bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + bot.send(sender, "You're kidding, right?"); + } + else + { + try + { + final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); + final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); + final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); + + bot.send(bot.getChannel(), + NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' + + + cmds[1].toUpperCase() + " = " + + NumberFormat.getCurrencyInstance(Locale.US).format((amt * to) / from) + .substring(1) + ' ' + cmds[3].toUpperCase()); + } + catch (NullPointerException ignored) + { + bot.send(sender, + "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + } } } } - } - else if (query.equals(Commands.CURRENCY_RATES_KEYWORD)) - { - bot.send(sender, "Last Update: " + pubDate); - - final Iterator<String> it = EXCHANGE_RATES.keySet().iterator(); - String rate; - - final StringBuilder buff = new StringBuilder(0); - - while (it.hasNext()) + else if (query.equals(Commands.CURRENCY_RATES_KEYWORD)) { - rate = it.next(); - if (buff.length() > 0) + bot.send(sender, "Last Update: " + pubDate); + + final Iterator<String> it = EXCHANGE_RATES.keySet().iterator(); + String rate; + + final StringBuilder buff = new StringBuilder(0); + + while (it.hasNext()) { - buff.append(", "); + rate = it.next(); + if (buff.length() > 0) + { + buff.append(", "); + } + buff.append(rate).append(": ").append(EXCHANGE_RATES.get(rate)); } - buff.append(rate).append(": ").append(EXCHANGE_RATES.get(rate)); + + bot.send(sender, buff.toString()); + } - - bot.send(sender, buff.toString()); - - } - else - { - bot.helpResponse(sender, Commands.CURRENCY_CMD + ' ' + query); - bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); } } else { - bot.getLogger().debug("The exchange rate table is empty."); - bot.send(sender, "Sorry, but the exchange rate table is empty."); + bot.helpResponse(sender, Commands.CURRENCY_CMD + ' ' + query); + bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); } } } @@ -215,6 +220,7 @@ public class CurrencyConverter implements Runnable * @param sender The nick of the person who sent the message. * @param query The currency query. */ + public synchronized void setQuery(String sender, String query) { this.query = query; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 77308f3..c7e510d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1211,6 +1211,8 @@ public class Mobibot extends PircBot if (lcTopic.endsWith(Commands.CURRENCY_CMD)) { + send(sender, "For a listing of currency rates:"); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD) + ' ' + Commands.CURRENCY_RATES_KEYWORD); send(sender, "For a listing of supported currencies:"); send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD)); } diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java index d17fb03..2b79193 100644 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,115 +1,59 @@ -/* - * ReleaseInfo.java - * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Fri Apr 25 18:08:16 PDT 2014 */ +/* Creation date Sat Aug 29 12:08:20 PDT 2015 */ package net.thauvin.erik.mobibot; import java.util.Date; /** * This class provides information gathered from the build environment. - * + * * @author JReleaseInfo AntTask */ -public class ReleaseInfo -{ +public class ReleaseInfo { - /** - * buildDate (set during build process to 1398474496363L). - */ - private static final Date buildDate = new Date(1398474496363L); + /** + * Disables the default constructor. + * @throws UnsupportedOperationException if the constructor is called. + */ + private ReleaseInfo() throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } - /** - * project (set during build process to "mobibot"). - */ - private static final String project = "mobibot"; - /** - * version (set during build process to "0.6"). - */ - private static final String version = "0.6"; + /** buildDate (set during build process to 1440875300470L). */ + private static final Date buildDate = new Date(1440875300470L); - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException if the constructor is called. - */ - private ReleaseInfo() - throws UnsupportedOperationException - { - throw new UnsupportedOperationException("Illegal constructor call."); - } + /** + * Get buildDate (set during build process to Sat Aug 29 12:08:20 PDT 2015). + * @return Date buildDate + */ + public static Date getBuildDate() { return buildDate; } - /** - * Get buildDate (set during build process to Fri Apr 25 18:08:16 PDT 2014). - * - * @return Date buildDate - */ - public static Date getBuildDate() - { - return buildDate; - } - /** - * Get buildNumber (set during build process to 0). - * - * @return int buildNumber - */ - public static int getBuildNumber() - { - return 0; - } + /** project (set during build process to "mobibot"). */ + private static final String project = "mobibot"; - /** - * Get project (set during build process to "mobibot"). - * - * @return String project - */ - public static String getProject() - { - return project; - } + /** + * Get project (set during build process to "mobibot"). + * @return String project + */ + public static String getProject() { return project; } - /** - * Get version (set during build process to "0.6"). - * - * @return String version - */ - public static String getVersion() - { - return version; - } + + /** version (set during build process to "0.6"). */ + private static final String version = "0.6"; + + /** + * Get version (set during build process to "0.6"). + * @return String version + */ + public static String getVersion() { return version; } + + + /** + * Get buildNumber (set during build process to 0). + * @return int buildNumber + */ + public static int getBuildNumber() { return 0; } } From b7e6cbe8ca25c019a404999cd4921f7ac61cd769 Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Sat, 29 Aug 2015 12:54:58 -0700 Subject: [PATCH 016/842] Changed Quote module to Joke, using the Internet Chuck Norris Database. --- mobibot.iws | 288 +++++++----------- .../net/thauvin/erik/mobibot/Commands.java | 10 +- .../erik/mobibot/{Quote.java => Joke.java} | 20 +- .../net/thauvin/erik/mobibot/Mobibot.java | 18 +- 4 files changed, 138 insertions(+), 198 deletions(-) rename src/main/java/net/thauvin/erik/mobibot/{Quote.java => Joke.java} (80%) diff --git a/mobibot.iws b/mobibot.iws index 142310f..8f5dd81 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -34,13 +34,10 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.jar" afterPath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.jar" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/buildnum.properties" afterPath="$PROJECT_DIR$/buildnum.properties" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.properties" afterPath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.properties" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.ipr" afterPath="$PROJECT_DIR$/mobibot.ipr" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> + <change type="MOVED" beforePath="K:\java\mobibot\src\main\java\net\thauvin\erik\mobibot\Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> </list> <ignored path="$USER_HOME$/.griffon/" /> <ignored path="$USER_HOME$/.grails/" /> @@ -201,9 +198,11 @@ <file leaf-file-name="Mobibot.java" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-16.034483"> - <caret line="1214" column="139" selection-start-line="1214" selection-start-column="139" selection-end-line="1214" selection-end-column="139" /> - <folding /> + <state vertical-scroll-proportion="-13.275862"> + <caret line="1255" column="40" selection-start-line="1255" selection-start-column="40" selection-end-line="1255" selection-end-column="40" /> + <folding> + <element signature="e#0#68037#0" expanded="true" /> + </folding> </state> </provider> </entry> @@ -212,7 +211,7 @@ <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="18" column="17" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> + <caret line="18" column="16" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> <folding /> </state> </provider> @@ -223,20 +222,7 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> - <folding> - <element signature="e#2506#2507#0" expanded="false" /> - <element signature="e#2544#2545#0" expanded="false" /> - <element signature="e#2646#2647#0" expanded="false" /> - <element signature="e#2667#2668#0" expanded="false" /> - <element signature="e#2832#2833#0" expanded="false" /> - <element signature="e#2861#2862#0" expanded="false" /> - <element signature="e#3011#3012#0" expanded="false" /> - <element signature="e#3029#3030#0" expanded="false" /> - <element signature="e#3159#3160#0" expanded="false" /> - <element signature="e#3177#3178#0" expanded="false" /> - <element signature="e#3321#3322#0" expanded="false" /> - <element signature="e#3344#3345#0" expanded="false" /> - </folding> + <folding /> </state> </provider> </entry> @@ -245,9 +231,9 @@ <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="87" column="80" selection-start-line="87" selection-start-column="47" selection-end-line="87" selection-end-column="47" /> + <caret line="172" column="56" selection-start-line="172" selection-start-column="56" selection-end-line="172" selection-end-column="56" /> <folding> - <element signature="e#0#5577#0" expanded="true" /> + <element signature="e#0#5574#0" expanded="true" /> </folding> </state> </provider> @@ -258,20 +244,7 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> - <folding> - <element signature="e#838#839#0" expanded="false" /> - <element signature="e#859#860#0" expanded="false" /> - <element signature="e#2201#2202#0" expanded="false" /> - <element signature="e#2220#2221#0" expanded="false" /> - <element signature="e#2353#2354#0" expanded="false" /> - <element signature="e#2369#2370#0" expanded="false" /> - <element signature="e#2950#2951#0" expanded="false" /> - <element signature="e#2969#2970#0" expanded="false" /> - <element signature="e#3002#3003#0" expanded="false" /> - <element signature="e#3024#3025#0" expanded="false" /> - <element signature="e#3057#3058#0" expanded="false" /> - <element signature="e#3079#3080#0" expanded="false" /> - </folding> + <folding /> </state> </provider> </entry> @@ -280,10 +253,9 @@ <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="112" column="5" selection-start-line="112" selection-start-column="5" selection-end-line="112" selection-end-column="5" /> + <caret line="49" column="5" selection-start-line="49" selection-start-column="5" selection-end-line="49" selection-end-column="5" /> <folding> - <element signature="e#3167#3168#0" expanded="true" /> - <element signature="e#3188#3189#0" expanded="true" /> + <element signature="e#1599#1600#0" expanded="true" /> </folding> </state> </provider> @@ -293,31 +265,28 @@ <entry file="file://$PROJECT_DIR$/build.gradle"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="72" column="90" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> + <caret line="72" column="0" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="true"> + <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/buildnum.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.030739672"> - <caret line="2" column="17" selection-start-line="2" selection-start-column="17" selection-end-line="2" selection-end-column="17" /> + <state vertical-scroll-proportion="0.0"> + <caret line="2" column="16" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="StockQuote.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> + <file leaf-file-name="Joke.java" pinned="false" current-in-tab="true"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding> - <element signature="e#0#4638#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> + <state vertical-scroll-proportion="0.554007"> + <caret line="102" column="91" selection-start-line="102" selection-start-column="91" selection-end-line="102" selection-end-column="91" /> + <folding /> </state> </provider> </entry> @@ -772,10 +741,12 @@ <option name="CHANGED_PATHS"> <list> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> <option value="$PROJECT_DIR$/buildnum.properties" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> + <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> </list> </option> </component> @@ -1543,46 +1514,6 @@ <sortByType /> </navigator> <panes> - <pane id="Scope"> - <subPane subId="All"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="src"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - <subPane subId="Project Files"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </pane> <pane id="PackagesPane"> <subPane> <PATH> @@ -1611,6 +1542,7 @@ </PATH> </subPane> </pane> + <pane id="Scratches" /> <pane id="ProjectPane"> <subPane> <PATH> @@ -1693,7 +1625,46 @@ </PATH> </subPane> </pane> - <pane id="Scratches" /> + <pane id="Scope"> + <subPane subId="All"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="src"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + <subPane subId="Project Files"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + </pane> <pane id="Favorites" /> </panes> </component> @@ -1806,7 +1777,7 @@ <option name="referencePos" value="0" /> <option name="showLabels" value="true" /> </component> - <component name="RunManager" selected="Gradle.mobibot [release]"> + <component name="RunManager" selected="Gradle.mobibot [deploy]"> <configuration default="false" name="mobibot [distZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> <option name="executionName" /> @@ -2419,11 +2390,12 @@ <workItem from="1422609958367" duration="95000" /> <workItem from="1440871303975" duration="34000" /> <workItem from="1440873148254" duration="1946000" /> + <workItem from="1440876283433" duration="1663000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="257370000" /> + <option name="totallyTimeSpent" value="259033000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2447,7 +2419,7 @@ </component> <component name="ToolWindowManager"> <frame x="1592" y="-8" width="1616" height="1204" extended-state="0" /> - <editor active="true" /> + <editor active="false" /> <layout> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> @@ -2461,8 +2433,7 @@ <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.19625" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.2750234" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="true" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18055555" sideWeight="0.5289929" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> @@ -2499,6 +2470,7 @@ <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.2750234" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> </layout> <layout-to-restore> <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> @@ -2648,10 +2620,7 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding> - <element signature="e#0#4638#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> + <folding /> </state> </provider> </entry> @@ -2931,17 +2900,6 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="70" column="47" selection-start-line="70" selection-start-column="47" selection-end-line="70" selection-end-column="47" /> - <folding> - <element signature="e#0#3499#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> @@ -2957,10 +2915,7 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding> - <element signature="e#0#4638#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> + <folding /> </state> </provider> </entry> @@ -2968,20 +2923,7 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> - <folding> - <element signature="e#838#839#0" expanded="false" /> - <element signature="e#859#860#0" expanded="false" /> - <element signature="e#2201#2202#0" expanded="false" /> - <element signature="e#2220#2221#0" expanded="false" /> - <element signature="e#2353#2354#0" expanded="false" /> - <element signature="e#2369#2370#0" expanded="false" /> - <element signature="e#2950#2951#0" expanded="false" /> - <element signature="e#2969#2970#0" expanded="false" /> - <element signature="e#3002#3003#0" expanded="false" /> - <element signature="e#3024#3025#0" expanded="false" /> - <element signature="e#3057#3058#0" expanded="false" /> - <element signature="e#3079#3080#0" expanded="false" /> - </folding> + <folding /> </state> </provider> </entry> @@ -2989,30 +2931,7 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> - <folding> - <element signature="e#2506#2507#0" expanded="false" /> - <element signature="e#2544#2545#0" expanded="false" /> - <element signature="e#2646#2647#0" expanded="false" /> - <element signature="e#2667#2668#0" expanded="false" /> - <element signature="e#2832#2833#0" expanded="false" /> - <element signature="e#2861#2862#0" expanded="false" /> - <element signature="e#3011#3012#0" expanded="false" /> - <element signature="e#3029#3030#0" expanded="false" /> - <element signature="e#3159#3160#0" expanded="false" /> - <element signature="e#3177#3178#0" expanded="false" /> - <element signature="e#3321#3322#0" expanded="false" /> - <element signature="e#3344#3345#0" expanded="false" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="87" column="80" selection-start-line="87" selection-start-column="47" selection-end-line="87" selection-end-column="47" /> - <folding> - <element signature="e#0#5577#0" expanded="true" /> - </folding> + <folding /> </state> </provider> </entry> @@ -3026,18 +2945,10 @@ </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-16.034483"> - <caret line="1214" column="139" selection-start-line="1214" selection-start-column="139" selection-end-line="1214" selection-end-column="139" /> - <folding /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="18" column="17" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> + <caret line="18" column="16" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> <folding /> </state> </provider> @@ -3045,26 +2956,53 @@ <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="112" column="5" selection-start-line="112" selection-start-column="5" selection-end-line="112" selection-end-column="5" /> + <caret line="49" column="5" selection-start-line="49" selection-start-column="5" selection-end-line="49" selection-end-column="5" /> <folding> - <element signature="e#3167#3168#0" expanded="true" /> - <element signature="e#3188#3189#0" expanded="true" /> + <element signature="e#1599#1600#0" expanded="true" /> </folding> </state> </provider> </entry> + <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="2" column="16" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> + <folding /> + </state> + </provider> + </entry> <entry file="file://$PROJECT_DIR$/build.gradle"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="72" column="90" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> + <caret line="72" column="0" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> <folding /> </state> </provider> </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.030739672"> - <caret line="2" column="17" selection-start-line="2" selection-start-column="17" selection-end-line="2" selection-end-column="17" /> + <state vertical-scroll-proportion="0.0"> + <caret line="172" column="56" selection-start-line="172" selection-start-column="56" selection-end-line="172" selection-end-column="56" /> + <folding> + <element signature="e#0#5574#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-13.275862"> + <caret line="1255" column="40" selection-start-line="1255" selection-start-column="40" selection-end-line="1255" selection-end-column="40" /> + <folding> + <element signature="e#0#68037#0" expanded="true" /> + </folding> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.554007"> + <caret line="102" column="91" selection-start-line="102" selection-start-column="91" selection-end-line="102" selection-end-column="91" /> <folding /> </state> </provider> diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 9d8b08c..3c1a2bc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -127,6 +127,11 @@ public class Commands */ public static final String INFO_CMD = "info"; + /** + * The joke command. + */ + public static final String JOKE_CMD = "joke"; + /** * The link command. */ @@ -167,11 +172,6 @@ public class Commands */ public static final String PROPS_ARG = "properties"; - /** - * The quote command. - */ - public static final String QUOTE_CMD = "quote"; - /** * The recap command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Quote.java b/src/main/java/net/thauvin/erik/mobibot/Joke.java similarity index 80% rename from src/main/java/net/thauvin/erik/mobibot/Quote.java rename to src/main/java/net/thauvin/erik/mobibot/Joke.java index 4838d48..bb6776d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Quote.java +++ b/src/main/java/net/thauvin/erik/mobibot/Joke.java @@ -42,20 +42,20 @@ import java.net.URL; import java.net.URLConnection; /** - * Processes the {@link Commands#QUOTE_CMD} command. + * Processes the {@link Commands#JOKE_CMD} command. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-20 * @since 1.0 */ -public class Quote implements Runnable +public class Joke implements Runnable { /** * The I Heart Quotes URL. */ - private static final String QUOTE_URL = - "http://www.iheartquotes.com/api/v1/random?format=json&max_lines=1&source=esr+humorix_misc+humorix_stories+joel_on_software+macintosh+math+mav_flame+osp_rules+paul_graham+prog_style+subversion"; + private static final String JOKE_URL = + "http://api.icndb.com/jokes/random"; /** * The bot's instance. @@ -68,25 +68,25 @@ public class Quote implements Runnable private final String sender; /** - * Creates a new {@link StockQuote} instance. + * Creates a new {@link Joke} instance. * * @param bot The bot's instance. * @param sender The nick of the person who sent the message. */ - public Quote(Mobibot bot, String sender) + public Joke(Mobibot bot, String sender) { this.bot = bot; this.sender = sender; } /** - * Returns a random quote from <a href="http://iheartquotes.com/">I Heart Quote</a> + * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> */ public final void run() { try { - final URL url = new URL(QUOTE_URL); + final URL url = new URL(JOKE_URL); final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); @@ -100,13 +100,13 @@ public class Quote implements Runnable final JSONObject json = new JSONObject(sb.toString()); - bot.send(bot.getChannel(), Colors.CYAN + json.getString("quote") + Colors.CYAN); + bot.send(bot.getChannel(), Colors.CYAN + json.getJSONObject("value").get("joke") + Colors.CYAN); reader.close(); } catch (Exception e) { - bot.getLogger().warn("Unable to retrieve random quote.", e); + bot.getLogger().warn("Unable to retrieve random joke.", e); bot.send(sender, "An error has occurred: " + e.getMessage()); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c7e510d..d9c3dc3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1126,10 +1126,10 @@ public class Mobibot extends PircBot send(sender, "For a listing of the supported countries:"); send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TIME_CMD)); } - else if (lcTopic.endsWith(Commands.QUOTE_CMD)) + else if (lcTopic.endsWith(Commands.JOKE_CMD)) { - send(sender, "To retrieve a random quote:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.QUOTE_CMD)); + send(sender, "To retrieve a random joke:"); + send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.JOKE_CMD)); } else if (lcTopic.endsWith(Commands.STOCK_CMD)) { @@ -1212,7 +1212,9 @@ public class Mobibot extends PircBot if (lcTopic.endsWith(Commands.CURRENCY_CMD)) { send(sender, "For a listing of currency rates:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD) + ' ' + Commands.CURRENCY_RATES_KEYWORD); + send(sender, + DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD) + ' ' + + Commands.CURRENCY_RATES_KEYWORD); send(sender, "For a listing of supported currencies:"); send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD)); } @@ -1251,10 +1253,10 @@ public class Mobibot extends PircBot cmds.add(Commands.GOOGLE_CMD); cmds.add(Commands.IGNORE_CMD); cmds.add(Commands.INFO_CMD); + cmds.add(Commands.JOKE_CMD); cmds.add(Commands.LOOKUP_CMD); cmds.add(channel.substring(1)); cmds.add(Commands.HELP_POSTING_KEYWORD); - cmds.add(Commands.QUOTE_CMD); cmds.add(Commands.RECAP_CMD); cmds.add(Commands.STOCK_CMD); cmds.add(Commands.HELP_TAGS_KEYWORD); @@ -1795,10 +1797,10 @@ public class Mobibot extends PircBot { stockResponse(sender, args); } - // mobibot: quote - else if (cmd.startsWith(Commands.QUOTE_CMD)) + // mobibot: joke + else if (cmd.startsWith(Commands.JOKE_CMD)) { - new Thread(new Quote(this, sender)).start(); + new Thread(new Joke(this, sender)).start(); } // mobibot: calc else if (cmd.startsWith(Commands.CALC_CMD)) From 284d1f5dfea2ae271dd822bda1319186d06b52e9 Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Mon, 31 Aug 2015 12:36:40 -0700 Subject: [PATCH 017/842] Added Javascript encoding/decoding to joke module. --- buildnum.properties | 2 +- mobibot.iml | 42 +- mobibot.ipr | 136 ++-- mobibot.iws | 739 +++++++++--------- .../java/net/thauvin/erik/mobibot/Joke.java | 7 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 8 +- 6 files changed, 472 insertions(+), 462 deletions(-) diff --git a/buildnum.properties b/buildnum.properties index f54bac3..ec85d8a 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Sat Aug 29 12:08:20 PDT 2015 +#Mon Aug 31 12:19:55 PDT 2015 build.num.last=0 diff --git a/mobibot.iml b/mobibot.iml index bce15a1..a27a6a5 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6" relativePaths="false" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6" relativePaths="false" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/build/classes/main" /> + <output-test url="file://$MODULE_DIR$/build/classes/test" /> <exclude-output /> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> @@ -12,23 +14,23 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" exported="" name="Gradle: log4j-1.2.17" level="project" /> - <orderEntry type="library" exported="" name="Gradle: pircbot-1.5.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-codec-1.9" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-logging-1.1.3" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-net-1.4.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-cli-1.2" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-httpclient-3.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: oro-2.0.8" level="project" /> - <orderEntry type="library" exported="" name="Gradle: jdom-1.1.3" level="project" /> - <orderEntry type="library" exported="" name="Gradle: jsoup-1.7.3" level="project" /> - <orderEntry type="library" exported="" name="Gradle: rome-1.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: json-20140107" level="project" /> - <orderEntry type="library" exported="" name="Gradle: jweather-0.3.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: twitter4j-core-4.0.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: delicious-1.14" level="project" /> - <orderEntry type="library" exported="" name="Gradle: rome-fetcher-1.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: exp4j-0.3.11" level="project" /> - <orderEntry type="library" exported="" name="Gradle: utils-1.07.00" level="project" /> + <orderEntry type="library" exported="" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" exported="" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-codec:commons-codec:1.9" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-logging:commons-logging:1.1.3" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-net:commons-net:1.4.1" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-cli:commons-cli:1.2" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" exported="" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.jdom:jdom:1.1.3" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.jsoup:jsoup:1.7.3" level="project" /> + <orderEntry type="library" exported="" name="Gradle: rome:rome:1.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: rome:rome-fetcher:1.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.json:json:20140107" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" exported="" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: de.congrace:exp4j:0.3.11" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.twitter4j:twitter4j-core:4.0.1" level="project" /> + <orderEntry type="library" exported="" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 78cb19a..17973d7 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -392,7 +392,7 @@ </option> </component> <component name="libraryTable"> - <library name="Gradle: commons-cli-1.2"> + <library name="Gradle: commons-cli:commons-cli:1.2"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar!/" /> </CLASSES> @@ -401,7 +401,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/6c5459816530a1962ac18cd315cc775b1b384329/commons-cli-1.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-codec-1.9"> + <library name="Gradle: commons-codec:commons-codec:1.9"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/9ce04e34240f674bc72680f8b843b1457383161a/commons-codec-1.9.jar!/" /> </CLASSES> @@ -410,7 +410,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-httpclient-3.1"> + <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar!/" /> </CLASSES> @@ -419,7 +419,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-logging-1.1.3"> + <library name="Gradle: commons-logging:commons-logging:1.1.3"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f/commons-logging-1.1.3.jar!/" /> </CLASSES> @@ -428,7 +428,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/28bb0405fddaf04f15058fbfbe01fe2780d7d3b6/commons-logging-1.1.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-net-1.4.1"> + <library name="Gradle: commons-net:commons-net:1.4.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/abb932adb2c10790c1eaa4365d3ac2a1ac7cb700/commons-net-1.4.1.jar!/" /> </CLASSES> @@ -437,16 +437,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/4c85b39e7f03471338bf7d36558eefe1e463e3de/commons-net-1.4.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: delicious-1.14"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: exp4j-0.3.11"> + <library name="Gradle: de.congrace:exp4j:0.3.11"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/150e7b4a77af47b03a1b65be7a01bd663ea64420/exp4j-0.3.11.jar!/" /> </CLASSES> @@ -455,43 +446,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/61abb5ef010830519e47ac56faade402807b245b/exp4j-0.3.11-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: jdom-1.1.3"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: json-20140107"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: jsoup-1.7.3"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: jweather-0.3.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: log4j-1.2.17"> + <library name="Gradle: log4j:log4j:1.2.17"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar!/" /> </CLASSES> @@ -500,52 +455,52 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: oro-2.0.8"> + <library name="Gradle: net.sf.delicious-java:delicious:1.14"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: pircbot-1.5.0"> + <library name="Gradle: net.sourceforge.jweather:jweather:0.3.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: rome-1.0"> + <library name="Gradle: org.jdom:jdom:1.1.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: rome-fetcher-1.0"> + <library name="Gradle: org.json:json:20140107"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: twitter4j-core-4.0.1"> + <library name="Gradle: org.jsoup:jsoup:1.7.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: utils-1.07.00"> + <library name="Gradle: org.ostermiller:utils:1.07.00"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar!/" /> </CLASSES> @@ -554,5 +509,50 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.twitter4j:twitter4j-core:4.0.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: oro:oro:2.0.8"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: pircbot:pircbot:1.5.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: rome:rome-fetcher:1.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: rome:rome:1.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/" /> + </SOURCES> + </library> </component> </project> \ No newline at end of file diff --git a/mobibot.iws b/mobibot.iws index 8f5dd81..57acae2 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -34,10 +34,12 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/buildnum.properties" afterPath="$PROJECT_DIR$/buildnum.properties" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iml" afterPath="$PROJECT_DIR$/mobibot.iml" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.ipr" afterPath="$PROJECT_DIR$/mobibot.ipr" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> - <change type="MOVED" beforePath="K:\java\mobibot\src\main\java\net\thauvin\erik\mobibot\Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> </list> <ignored path="$USER_HOME$/.griffon/" /> <ignored path="$USER_HOME$/.grails/" /> @@ -122,6 +124,20 @@ </task> <projects_view> <tree_state> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ExternalProjectsStructure$RootNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="Run Configurations" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.RunConfigurationsNode" /> + </PATH_ELEMENT> + </PATH> <PATH> <PATH_ELEMENT> <option name="myItemId" value="" /> @@ -145,10 +161,6 @@ <option name="myItemId" value="Tasks" /> <option name="myItemType" value="com.intellij.openapi.externalSystem.view.TasksNode" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="other" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.TasksNode$1" /> - </PATH_ELEMENT> </PATH> </tree_state> </projects_view> @@ -183,109 +195,31 @@ </component> <component name="FileEditorManager"> <leaf> - <file leaf-file-name="CurrencyConverter.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <file leaf-file-name="Joke.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="148" column="12" selection-start-line="148" selection-start-column="12" selection-end-line="148" selection-end-column="12" /> - <folding> - <element signature="imports" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Mobibot.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-13.275862"> - <caret line="1255" column="40" selection-start-line="1255" selection-start-column="40" selection-end-line="1255" selection-end-column="40" /> - <folding> - <element signature="e#0#68037#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="mobibot.properties" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="18" column="16" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> + <caret line="103" column="44" selection-start-line="103" selection-start-column="44" selection-end-line="103" selection-end-column="44" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="EntryComment.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Commands.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="172" column="56" selection-start-line="172" selection-start-column="56" selection-end-line="172" selection-end-column="56" /> - <folding> - <element signature="e#0#5574#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="SwingWorker.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="ReleaseInfo.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="49" column="5" selection-start-line="49" selection-start-column="5" selection-end-line="49" selection-end-column="5" /> - <folding> - <element signature="e#1599#1600#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> <file leaf-file-name="build.gradle" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/build.gradle"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="72" column="0" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> + <caret line="28" column="53" selection-start-line="28" selection-start-column="53" selection-end-line="28" selection-end-column="53" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="false"> + <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="true"> <entry file="file://$PROJECT_DIR$/buildnum.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="2" column="16" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Joke.java" pinned="false" current-in-tab="true"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.554007"> - <caret line="102" column="91" selection-start-line="102" selection-start-column="91" selection-end-line="102" selection-end-column="91" /> + <state vertical-scroll-proportion="0.05574913"> + <caret line="2" column="17" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> <folding /> </state> </provider> @@ -441,79 +375,15 @@ <entry key="$PROJECT_DIR$"> <value> <list> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project and all projects it depends on." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="buildNeeded" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles classes 'test'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="testClasses" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="copyToDeploy" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs the unit tests." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="test" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="distTar" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Releases new version." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="release" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs all checks." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="check" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'test:java'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="compileTestJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Copies all needed files to the deploy directory." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="deploy" /> - </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles the outputs of this project." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="assemble" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Deletes the build directory." /> + <option name="description" value="Assembles the main distributions" /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="clean" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'main:java'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="compileJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Generates Javadoc API documentation for the main source code." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="javadoc" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="distZip" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles a jar archive containing the main classes." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="jar" /> + <option name="name" value="assembleMainDist" /> </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles and tests this project." /> @@ -526,28 +396,137 @@ <option name="name" value="buildDependents" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> + <option name="description" value="Assembles and tests this project and all projects it depends on." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="installApp" /> + <option name="name" value="buildNeeded" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Runs all checks." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="check" /> </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles classes 'main'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="classes" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Deletes the build directory." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="clean" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Compiles Java source 'main:java'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="compileJava" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Compiles Java source 'test:java'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="compileTestJava" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the components produced by root project 'mobibot'. [incubating]" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="components" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="copyToDeploy" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="copyToDeployLib" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Processes resources 'test:resources'." /> + <option name="description" value="Displays all dependencies declared in root project 'mobibot'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="dependencies" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the insight into a specific dependency in root project 'mobibot'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="dependencyInsight" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Copies all needed files to the deploy directory." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="deploy" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a distribution." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="distTar" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a distribution." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="distZip" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays a help message." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="help" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Initializes a new Gradle build. [incubating]" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="init" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="installApp" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Installs the project as a distribution as-is." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="installDist" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Assembles a jar archive containing the main classes." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="jar" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Generates Javadoc API documentation for the main source code." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="javadoc" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the configuration model of root project 'mobibot'. [incubating]" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="model" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Processes JVM resources 'main:resources'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="processResources" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Processes JVM resources 'test:resources'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="processTestResources" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Processes resources 'main:resources'." /> + <option name="description" value="Displays the sub-projects of root project 'mobibot'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="processResources" /> + <option name="name" value="projects" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the properties of root project 'mobibot'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="properties" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Releases new version." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="release" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Runs this project as a JVM application" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="run" /> </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Creates OS specific scripts to run the project as a JVM application." /> @@ -555,13 +534,23 @@ <option name="name" value="startScripts" /> </ExternalTaskPojo> <ExternalTaskPojo> + <option name="description" value="Displays the tasks runnable from root project 'mobibot'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="wrapper" /> + <option name="name" value="tasks" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Runs this project as a JVM application" /> + <option name="description" value="Runs the unit tests." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="run" /> + <option name="name" value="test" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Assembles classes 'test'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="testClasses" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="wrapper" /> </ExternalTaskPojo> </list> </value> @@ -570,7 +559,7 @@ </option> <option name="modificationStamps"> <map> - <entry key="$PROJECT_DIR$" value="2797270596074" /> + <entry key="$PROJECT_DIR$" value="2838985564788" /> </map> </option> <option name="projectBuildClasspath"> @@ -585,41 +574,23 @@ <ExternalModuleBuildClasspathPojo> <option name="entries"> <list> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/9ce04e34240f674bc72680f8b843b1457383161a/commons-codec-1.9.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/28bb0405fddaf04f15058fbfbe01fe2780d7d3b6/commons-logging-1.1.3-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f/commons-logging-1.1.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/4c85b39e7f03471338bf7d36558eefe1e463e3de/commons-net-1.4.1-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/abb932adb2c10790c1eaa4365d3ac2a1ac7cb700/commons-net-1.4.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/6c5459816530a1962ac18cd315cc775b1b384329/commons-cli-1.2-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/61abb5ef010830519e47ac56faade402807b245b/exp4j-0.3.11-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/150e7b4a77af47b03a1b65be7a01bd663ea64420/exp4j-0.3.11.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar" /> </list> </option> @@ -648,77 +619,108 @@ <option value="C:/gradle/src/cli" /> <option value="C:/gradle/src/code-quality" /> <option value="C:/gradle/src/core" /> - <option value="C:/gradle/src/core-impl" /> - <option value="C:/gradle/src/cpp" /> + <option value="C:/gradle/src/dependency-management" /> <option value="C:/gradle/src/diagnostics" /> <option value="C:/gradle/src/ear" /> <option value="C:/gradle/src/ide" /> + <option value="C:/gradle/src/ide-native" /> <option value="C:/gradle/src/internal-integ-testing" /> <option value="C:/gradle/src/internal-testing" /> <option value="C:/gradle/src/ivy" /> <option value="C:/gradle/src/jacoco" /> <option value="C:/gradle/src/javascript" /> <option value="C:/gradle/src/jetty" /> - <option value="C:/gradle/src/language-base" /> + <option value="C:/gradle/src/language-groovy" /> + <option value="C:/gradle/src/language-java" /> <option value="C:/gradle/src/language-jvm" /> + <option value="C:/gradle/src/language-native" /> + <option value="C:/gradle/src/language-scala" /> <option value="C:/gradle/src/launcher" /> <option value="C:/gradle/src/maven" /> <option value="C:/gradle/src/messaging" /> + <option value="C:/gradle/src/model-core" /> + <option value="C:/gradle/src/model-groovy" /> <option value="C:/gradle/src/native" /> <option value="C:/gradle/src/open-api" /> <option value="C:/gradle/src/osgi" /> + <option value="C:/gradle/src/platform-base" /> + <option value="C:/gradle/src/platform-jvm" /> + <option value="C:/gradle/src/platform-native" /> + <option value="C:/gradle/src/platform-play" /> + <option value="C:/gradle/src/plugin-development" /> + <option value="C:/gradle/src/plugin-use" /> <option value="C:/gradle/src/plugins" /> <option value="C:/gradle/src/publish" /> <option value="C:/gradle/src/reporting" /> <option value="C:/gradle/src/resources" /> + <option value="C:/gradle/src/resources-http" /> + <option value="C:/gradle/src/resources-s3" /> + <option value="C:/gradle/src/resources-sftp" /> <option value="C:/gradle/src/scala" /> <option value="C:/gradle/src/signing" /> <option value="C:/gradle/src/sonar" /> + <option value="C:/gradle/src/testing-native" /> <option value="C:/gradle/src/tooling-api" /> + <option value="C:/gradle/src/tooling-api-builders" /> <option value="C:/gradle/src/ui" /> <option value="C:/gradle/src/wrapper" /> - <option value="C:/gradle/lib/ant-1.9.3.jar" /> - <option value="C:/gradle/lib/ant-launcher-1.9.3.jar" /> - <option value="C:/gradle/lib/gradle-base-services-1.12.jar" /> - <option value="C:/gradle/lib/gradle-base-services-groovy-1.12.jar" /> - <option value="C:/gradle/lib/gradle-cli-1.12.jar" /> - <option value="C:/gradle/lib/gradle-core-1.12.jar" /> - <option value="C:/gradle/lib/gradle-docs-1.12.jar" /> - <option value="C:/gradle/lib/gradle-launcher-1.12.jar" /> - <option value="C:/gradle/lib/gradle-messaging-1.12.jar" /> - <option value="C:/gradle/lib/gradle-native-1.12.jar" /> - <option value="C:/gradle/lib/gradle-open-api-1.12.jar" /> - <option value="C:/gradle/lib/gradle-resources-1.12.jar" /> - <option value="C:/gradle/lib/gradle-tooling-api-1.12.jar" /> - <option value="C:/gradle/lib/gradle-ui-1.12.jar" /> - <option value="C:/gradle/lib/gradle-wrapper-1.12.jar" /> - <option value="C:/gradle/lib/groovy-all-1.8.6.jar" /> - <option value="C:/gradle/lib/ivy-2.2.0.jar" /> - <option value="C:/gradle/lib/plugins/ant-antlr-1.9.3.jar" /> - <option value="C:/gradle/lib/plugins/gradle-announce-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-antlr-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-comparison-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-init-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-code-quality-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-core-impl-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-cpp-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-diagnostics-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ear-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ide-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ivy-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jacoco-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-javascript-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jetty-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-base-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-jvm-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-maven-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-osgi-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-plugins-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-publish-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-reporting-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-scala-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-signing-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-sonar-1.12.jar" /> + <option value="C:/gradle/lib/ant-1.9.4.jar" /> + <option value="C:/gradle/lib/ant-launcher-1.9.4.jar" /> + <option value="C:/gradle/lib/gradle-base-services-2.4.jar" /> + <option value="C:/gradle/lib/gradle-base-services-groovy-2.4.jar" /> + <option value="C:/gradle/lib/gradle-cli-2.4.jar" /> + <option value="C:/gradle/lib/gradle-core-2.4.jar" /> + <option value="C:/gradle/lib/gradle-docs-2.4.jar" /> + <option value="C:/gradle/lib/gradle-launcher-2.4.jar" /> + <option value="C:/gradle/lib/gradle-messaging-2.4.jar" /> + <option value="C:/gradle/lib/gradle-model-core-2.4.jar" /> + <option value="C:/gradle/lib/gradle-model-groovy-2.4.jar" /> + <option value="C:/gradle/lib/gradle-native-2.4.jar" /> + <option value="C:/gradle/lib/gradle-open-api-2.4.jar" /> + <option value="C:/gradle/lib/gradle-resources-2.4.jar" /> + <option value="C:/gradle/lib/gradle-tooling-api-2.4.jar" /> + <option value="C:/gradle/lib/gradle-ui-2.4.jar" /> + <option value="C:/gradle/lib/gradle-wrapper-2.4.jar" /> + <option value="C:/gradle/lib/groovy-all-2.3.10.jar" /> + <option value="C:/gradle/lib/plugins/gradle-announce-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-antlr-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-build-comparison-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-build-init-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-code-quality-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-dependency-management-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-diagnostics-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ear-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ide-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ide-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ivy-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-jacoco-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-javascript-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-jetty-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-groovy-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-java-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-jvm-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-scala-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-maven-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-osgi-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-base-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-jvm-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-play-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-plugin-development-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-plugin-use-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-plugins-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-publish-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-reporting-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-resources-http-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-resources-s3-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-resources-sftp-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-scala-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-signing-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-sonar-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-testing-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-tooling-api-builders-2.4.jar" /> + <option value="C:/gradle/lib/plugins/ivy-2.2.0.jar" /> <option value="$PROJECT_DIR$/buildSrc/src/main/java" /> <option value="$PROJECT_DIR$/buildSrc/src/main/groovy" /> </list> @@ -742,11 +744,12 @@ <list> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <option value="$PROJECT_DIR$/buildnum.properties" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> + <option value="$PROJECT_DIR$/build.gradle" /> + <option value="$PROJECT_DIR$/buildnum.properties" /> </list> </option> </component> @@ -1514,30 +1517,42 @@ <sortByType /> </navigator> <panes> - <pane id="PackagesPane"> - <subPane> + <pane id="Scope"> + <subPane subId="All"> <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> </PATH> <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="net.thauvin.erik.mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> + <PATH_ELEMENT USER_OBJECT="src"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + <subPane subId="Project Files"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> </PATH> </subPane> @@ -1625,42 +1640,30 @@ </PATH> </subPane> </pane> - <pane id="Scope"> - <subPane subId="All"> + <pane id="PackagesPane"> + <subPane> <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> </PATH_ELEMENT> </PATH> <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="src"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - <subPane subId="Project Files"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="net.thauvin.erik.mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> </PATH_ELEMENT> </PATH> </subPane> @@ -1777,7 +1780,7 @@ <option name="referencePos" value="0" /> <option name="showLabels" value="true" /> </component> - <component name="RunManager" selected="Gradle.mobibot [deploy]"> + <component name="RunManager" selected="Gradle.mobibot [release]"> <configuration default="false" name="mobibot [distZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> <option name="executionName" /> @@ -2391,11 +2394,12 @@ <workItem from="1440871303975" duration="34000" /> <workItem from="1440873148254" duration="1946000" /> <workItem from="1440876283433" duration="1663000" /> + <workItem from="1441045012377" duration="2858000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="259033000" /> + <option name="totallyTimeSpent" value="261891000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2423,6 +2427,7 @@ <layout> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31898972" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> @@ -2431,9 +2436,9 @@ <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43685687" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32692307" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.19625" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> + <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.263125" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="true" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18055555" sideWeight="0.5289929" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> @@ -2448,7 +2453,6 @@ <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31983805" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="FLOATING" visible="true" weight="0.32969153" sideWeight="0.5" order="19" side_tool="true" content_ui="tabs" x="2155" y="309" width="663" height="473" /> <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3228745" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> @@ -2616,49 +2620,6 @@ <option name="myLastEditedConfigurable" /> </component> <component name="editorHistoryManager"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="295" column="19" selection-start-line="292" selection-start-column="4" selection-end-line="295" selection-end-column="17" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="13" column="1" selection-start-line="13" selection-start-column="1" selection-end-line="13" selection-end-column="1" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="24" column="42" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="3" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> @@ -2874,7 +2835,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="17" column="24" selection-start-line="17" selection-start-column="24" selection-end-line="17" selection-end-column="24" /> - <folding /> </state> </provider> </entry> @@ -2882,10 +2842,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="74" column="49" selection-start-line="74" selection-start-column="49" selection-end-line="74" selection-end-column="49" /> - <folding> - <element signature="e#0#3762#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> </state> </provider> </entry> @@ -2893,10 +2849,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="43" column="2" selection-start-line="43" selection-start-column="2" selection-end-line="43" selection-end-column="2" /> - <folding> - <element signature="e#0#4204#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> </state> </provider> </entry> @@ -2904,10 +2856,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> - <folding> - <element signature="e#0#3642#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> </state> </provider> </entry> @@ -2915,7 +2863,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> </state> </provider> </entry> @@ -2931,7 +2878,21 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> - <folding /> + <folding> + <element signature="imports" expanded="false" /> + <element signature="e#2506#2507#0" expanded="false" /> + <element signature="e#2544#2545#0" expanded="false" /> + <element signature="e#2646#2647#0" expanded="false" /> + <element signature="e#2667#2668#0" expanded="false" /> + <element signature="e#2832#2833#0" expanded="false" /> + <element signature="e#2861#2862#0" expanded="false" /> + <element signature="e#3011#3012#0" expanded="false" /> + <element signature="e#3029#3030#0" expanded="false" /> + <element signature="e#3159#3160#0" expanded="false" /> + <element signature="e#3177#3178#0" expanded="false" /> + <element signature="e#3321#3322#0" expanded="false" /> + <element signature="e#3344#3345#0" expanded="false" /> + </folding> </state> </provider> </entry> @@ -2940,7 +2901,9 @@ <state vertical-scroll-proportion="0.0"> <caret line="148" column="12" selection-start-line="148" selection-start-column="12" selection-end-line="148" selection-end-column="12" /> <folding> - <element signature="imports" expanded="true" /> + <element signature="e#0#6680#0" expanded="false" /> + <element signature="e#2789#2790#0" expanded="false" /> + <element signature="e#2810#2811#0" expanded="false" /> </folding> </state> </provider> @@ -2957,24 +2920,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="49" column="5" selection-start-line="49" selection-start-column="5" selection-end-line="49" selection-end-column="5" /> - <folding> - <element signature="e#1599#1600#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="2" column="16" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="72" column="0" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> <folding /> </state> </provider> @@ -2983,26 +2928,88 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="172" column="56" selection-start-line="172" selection-start-column="56" selection-end-line="172" selection-end-column="56" /> - <folding> - <element signature="e#0#5574#0" expanded="true" /> - </folding> + <folding /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-13.275862"> + <state vertical-scroll-proportion="0.0"> <caret line="1255" column="40" selection-start-line="1255" selection-start-column="40" selection-end-line="1255" selection-end-column="40" /> <folding> - <element signature="e#0#68037#0" expanded="true" /> + <element signature="e#0#68037#0" expanded="false" /> </folding> </state> </provider> </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/twitter4j/util/CharacterUtil.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.12295869"> + <caret line="22" column="19" selection-start-line="22" selection-start-column="19" selection-end-line="22" selection-end-column="19" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/org/apache/oro/text/regex/Util.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-4.8097982"> + <caret line="93" column="19" selection-start-line="93" selection-start-column="19" selection-end-line="93" selection-end-column="19" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/del/icio/us/DeliciousUtils.class"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="7.4814816"> + <caret line="15" column="13" selection-start-line="15" selection-start-column="13" selection-end-line="15" selection-end-column="13" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/org/apache/commons/codec/net/Utils.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="29" column="6" selection-start-line="29" selection-start-column="6" selection-end-line="29" selection-end-column="6" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/org/apache/commons/codec/net/URLCodec.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="47" column="13" selection-start-line="47" selection-start-column="13" selection-end-line="47" selection-end-column="13" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/org/jsoup/helper/StringUtil.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="8" column="19" selection-start-line="8" selection-start-column="19" selection-end-line="8" selection-end-column="19" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/build.gradle"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="28" column="53" selection-start-line="28" selection-start-column="53" selection-end-line="28" selection-end-column="53" /> + <folding /> + </state> + </provider> + </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.554007"> - <caret line="102" column="91" selection-start-line="102" selection-start-column="91" selection-end-line="102" selection-end-column="91" /> + <state vertical-scroll-proportion="0.0"> + <caret line="103" column="44" selection-start-line="103" selection-start-column="44" selection-end-line="103" selection-end-column="44" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.05574913"> + <caret line="2" column="17" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> <folding /> </state> </provider> diff --git a/src/main/java/net/thauvin/erik/mobibot/Joke.java b/src/main/java/net/thauvin/erik/mobibot/Joke.java index bb6776d..ce6f562 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/Joke.java @@ -54,8 +54,7 @@ public class Joke implements Runnable /** * The I Heart Quotes URL. */ - private static final String JOKE_URL = - "http://api.icndb.com/jokes/random"; + private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript"; /** * The bot's instance. @@ -100,7 +99,9 @@ public class Joke implements Runnable final JSONObject json = new JSONObject(sb.toString()); - bot.send(bot.getChannel(), Colors.CYAN + json.getJSONObject("value").get("joke") + Colors.CYAN); + bot.send(bot.getChannel(), + Colors.CYAN + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") + .replaceAll("\\\"", "\"") + Colors.CYAN); reader.close(); } diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 2b79193..1edd473 100644 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Sat Aug 29 12:08:20 PDT 2015 */ +/* Creation date Mon Aug 31 12:19:55 PDT 2015 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,11 +20,11 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1440875300470L). */ - private static final Date buildDate = new Date(1440875300470L); + /** buildDate (set during build process to 1441048795735L). */ + private static final Date buildDate = new Date(1441048795735L); /** - * Get buildDate (set during build process to Sat Aug 29 12:08:20 PDT 2015). + * Get buildDate (set during build process to Mon Aug 31 12:19:55 PDT 2015). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } From 12e3a578368cfaaeea20d9359d0ee9cfed483bfe Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Mon, 31 Aug 2015 12:36:40 -0700 Subject: [PATCH 018/842] Added Javascript encoding/decoding to joke module. --- buildnum.properties | 2 +- mobibot.iml | 42 +- mobibot.ipr | 136 ++-- mobibot.iws | 739 +++++++++--------- .../java/net/thauvin/erik/mobibot/Joke.java | 11 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 8 +- 6 files changed, 474 insertions(+), 464 deletions(-) diff --git a/buildnum.properties b/buildnum.properties index f54bac3..ec85d8a 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Sat Aug 29 12:08:20 PDT 2015 +#Mon Aug 31 12:19:55 PDT 2015 build.num.last=0 diff --git a/mobibot.iml b/mobibot.iml index bce15a1..a27a6a5 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6" relativePaths="false" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6" relativePaths="false" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/build/classes/main" /> + <output-test url="file://$MODULE_DIR$/build/classes/test" /> <exclude-output /> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> @@ -12,23 +14,23 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" exported="" name="Gradle: log4j-1.2.17" level="project" /> - <orderEntry type="library" exported="" name="Gradle: pircbot-1.5.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-codec-1.9" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-logging-1.1.3" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-net-1.4.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-cli-1.2" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-httpclient-3.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: oro-2.0.8" level="project" /> - <orderEntry type="library" exported="" name="Gradle: jdom-1.1.3" level="project" /> - <orderEntry type="library" exported="" name="Gradle: jsoup-1.7.3" level="project" /> - <orderEntry type="library" exported="" name="Gradle: rome-1.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: json-20140107" level="project" /> - <orderEntry type="library" exported="" name="Gradle: jweather-0.3.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: twitter4j-core-4.0.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: delicious-1.14" level="project" /> - <orderEntry type="library" exported="" name="Gradle: rome-fetcher-1.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: exp4j-0.3.11" level="project" /> - <orderEntry type="library" exported="" name="Gradle: utils-1.07.00" level="project" /> + <orderEntry type="library" exported="" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" exported="" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-codec:commons-codec:1.9" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-logging:commons-logging:1.1.3" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-net:commons-net:1.4.1" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-cli:commons-cli:1.2" level="project" /> + <orderEntry type="library" exported="" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" exported="" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.jdom:jdom:1.1.3" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.jsoup:jsoup:1.7.3" level="project" /> + <orderEntry type="library" exported="" name="Gradle: rome:rome:1.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: rome:rome-fetcher:1.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.json:json:20140107" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" exported="" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" exported="" name="Gradle: de.congrace:exp4j:0.3.11" level="project" /> + <orderEntry type="library" exported="" name="Gradle: org.twitter4j:twitter4j-core:4.0.1" level="project" /> + <orderEntry type="library" exported="" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 78cb19a..17973d7 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -392,7 +392,7 @@ </option> </component> <component name="libraryTable"> - <library name="Gradle: commons-cli-1.2"> + <library name="Gradle: commons-cli:commons-cli:1.2"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar!/" /> </CLASSES> @@ -401,7 +401,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/6c5459816530a1962ac18cd315cc775b1b384329/commons-cli-1.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-codec-1.9"> + <library name="Gradle: commons-codec:commons-codec:1.9"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/9ce04e34240f674bc72680f8b843b1457383161a/commons-codec-1.9.jar!/" /> </CLASSES> @@ -410,7 +410,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-httpclient-3.1"> + <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar!/" /> </CLASSES> @@ -419,7 +419,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-logging-1.1.3"> + <library name="Gradle: commons-logging:commons-logging:1.1.3"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f/commons-logging-1.1.3.jar!/" /> </CLASSES> @@ -428,7 +428,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/28bb0405fddaf04f15058fbfbe01fe2780d7d3b6/commons-logging-1.1.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-net-1.4.1"> + <library name="Gradle: commons-net:commons-net:1.4.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/abb932adb2c10790c1eaa4365d3ac2a1ac7cb700/commons-net-1.4.1.jar!/" /> </CLASSES> @@ -437,16 +437,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/4c85b39e7f03471338bf7d36558eefe1e463e3de/commons-net-1.4.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: delicious-1.14"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: exp4j-0.3.11"> + <library name="Gradle: de.congrace:exp4j:0.3.11"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/150e7b4a77af47b03a1b65be7a01bd663ea64420/exp4j-0.3.11.jar!/" /> </CLASSES> @@ -455,43 +446,7 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/61abb5ef010830519e47ac56faade402807b245b/exp4j-0.3.11-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: jdom-1.1.3"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: json-20140107"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: jsoup-1.7.3"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: jweather-0.3.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: log4j-1.2.17"> + <library name="Gradle: log4j:log4j:1.2.17"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar!/" /> </CLASSES> @@ -500,52 +455,52 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: oro-2.0.8"> + <library name="Gradle: net.sf.delicious-java:delicious:1.14"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: pircbot-1.5.0"> + <library name="Gradle: net.sourceforge.jweather:jweather:0.3.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: rome-1.0"> + <library name="Gradle: org.jdom:jdom:1.1.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: rome-fetcher-1.0"> + <library name="Gradle: org.json:json:20140107"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: twitter4j-core-4.0.1"> + <library name="Gradle: org.jsoup:jsoup:1.7.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: utils-1.07.00"> + <library name="Gradle: org.ostermiller:utils:1.07.00"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar!/" /> </CLASSES> @@ -554,5 +509,50 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.twitter4j:twitter4j-core:4.0.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: oro:oro:2.0.8"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: pircbot:pircbot:1.5.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: rome:rome-fetcher:1.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: rome:rome:1.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/" /> + </SOURCES> + </library> </component> </project> \ No newline at end of file diff --git a/mobibot.iws b/mobibot.iws index 8f5dd81..57acae2 100644 --- a/mobibot.iws +++ b/mobibot.iws @@ -34,10 +34,12 @@ </component> <component name="ChangeListManager"> <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/buildnum.properties" afterPath="$PROJECT_DIR$/buildnum.properties" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iml" afterPath="$PROJECT_DIR$/mobibot.iml" /> + <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.ipr" afterPath="$PROJECT_DIR$/mobibot.ipr" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> - <change type="MOVED" beforePath="K:\java\mobibot\src\main\java\net\thauvin\erik\mobibot\Quote.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> </list> <ignored path="$USER_HOME$/.griffon/" /> <ignored path="$USER_HOME$/.grails/" /> @@ -122,6 +124,20 @@ </task> <projects_view> <tree_state> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ExternalProjectsStructure$RootNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="Run Configurations" /> + <option name="myItemType" value="com.intellij.openapi.externalSystem.view.RunConfigurationsNode" /> + </PATH_ELEMENT> + </PATH> <PATH> <PATH_ELEMENT> <option name="myItemId" value="" /> @@ -145,10 +161,6 @@ <option name="myItemId" value="Tasks" /> <option name="myItemType" value="com.intellij.openapi.externalSystem.view.TasksNode" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="other" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.TasksNode$1" /> - </PATH_ELEMENT> </PATH> </tree_state> </projects_view> @@ -183,109 +195,31 @@ </component> <component name="FileEditorManager"> <leaf> - <file leaf-file-name="CurrencyConverter.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> + <file leaf-file-name="Joke.java" pinned="false" current-in-tab="false"> + <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="148" column="12" selection-start-line="148" selection-start-column="12" selection-end-line="148" selection-end-column="12" /> - <folding> - <element signature="imports" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Mobibot.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-13.275862"> - <caret line="1255" column="40" selection-start-line="1255" selection-start-column="40" selection-end-line="1255" selection-end-column="40" /> - <folding> - <element signature="e#0#68037#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="mobibot.properties" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="18" column="16" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> + <caret line="103" column="44" selection-start-line="103" selection-start-column="44" selection-end-line="103" selection-end-column="44" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="EntryComment.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Commands.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="172" column="56" selection-start-line="172" selection-start-column="56" selection-end-line="172" selection-end-column="56" /> - <folding> - <element signature="e#0#5574#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="SwingWorker.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="ReleaseInfo.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="49" column="5" selection-start-line="49" selection-start-column="5" selection-end-line="49" selection-end-column="5" /> - <folding> - <element signature="e#1599#1600#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - </file> <file leaf-file-name="build.gradle" pinned="false" current-in-tab="false"> <entry file="file://$PROJECT_DIR$/build.gradle"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> - <caret line="72" column="0" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> + <caret line="28" column="53" selection-start-line="28" selection-start-column="53" selection-end-line="28" selection-end-column="53" /> <folding /> </state> </provider> </entry> </file> - <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="false"> + <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="true"> <entry file="file://$PROJECT_DIR$/buildnum.properties"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="2" column="16" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="Joke.java" pinned="false" current-in-tab="true"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.554007"> - <caret line="102" column="91" selection-start-line="102" selection-start-column="91" selection-end-line="102" selection-end-column="91" /> + <state vertical-scroll-proportion="0.05574913"> + <caret line="2" column="17" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> <folding /> </state> </provider> @@ -441,79 +375,15 @@ <entry key="$PROJECT_DIR$"> <value> <list> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project and all projects it depends on." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="buildNeeded" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles classes 'test'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="testClasses" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="copyToDeploy" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs the unit tests." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="test" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="distTar" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Releases new version." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="release" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs all checks." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="check" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'test:java'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="compileTestJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Copies all needed files to the deploy directory." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="deploy" /> - </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles the outputs of this project." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="assemble" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Deletes the build directory." /> + <option name="description" value="Assembles the main distributions" /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="clean" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'main:java'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="compileJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Generates Javadoc API documentation for the main source code." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="javadoc" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a JVM application with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="distZip" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles a jar archive containing the main classes." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="jar" /> + <option name="name" value="assembleMainDist" /> </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles and tests this project." /> @@ -526,28 +396,137 @@ <option name="name" value="buildDependents" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> + <option name="description" value="Assembles and tests this project and all projects it depends on." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="installApp" /> + <option name="name" value="buildNeeded" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Runs all checks." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="check" /> </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Assembles classes 'main'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="classes" /> </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Deletes the build directory." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="clean" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Compiles Java source 'main:java'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="compileJava" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Compiles Java source 'test:java'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="compileTestJava" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the components produced by root project 'mobibot'. [incubating]" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="components" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="copyToDeploy" /> + </ExternalTaskPojo> <ExternalTaskPojo> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="copyToDeployLib" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Processes resources 'test:resources'." /> + <option name="description" value="Displays all dependencies declared in root project 'mobibot'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="dependencies" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the insight into a specific dependency in root project 'mobibot'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="dependencyInsight" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Copies all needed files to the deploy directory." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="deploy" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a distribution." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="distTar" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Bundles the project as a distribution." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="distZip" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays a help message." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="help" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Initializes a new Gradle build. [incubating]" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="init" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="installApp" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Installs the project as a distribution as-is." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="installDist" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Assembles a jar archive containing the main classes." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="jar" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Generates Javadoc API documentation for the main source code." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="javadoc" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the configuration model of root project 'mobibot'. [incubating]" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="model" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Processes JVM resources 'main:resources'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="processResources" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Processes JVM resources 'test:resources'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> <option name="name" value="processTestResources" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Processes resources 'main:resources'." /> + <option name="description" value="Displays the sub-projects of root project 'mobibot'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="processResources" /> + <option name="name" value="projects" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Displays the properties of root project 'mobibot'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="properties" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Releases new version." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="release" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Runs this project as a JVM application" /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="run" /> </ExternalTaskPojo> <ExternalTaskPojo> <option name="description" value="Creates OS specific scripts to run the project as a JVM application." /> @@ -555,13 +534,23 @@ <option name="name" value="startScripts" /> </ExternalTaskPojo> <ExternalTaskPojo> + <option name="description" value="Displays the tasks runnable from root project 'mobibot'." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="wrapper" /> + <option name="name" value="tasks" /> </ExternalTaskPojo> <ExternalTaskPojo> - <option name="description" value="Runs this project as a JVM application" /> + <option name="description" value="Runs the unit tests." /> <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="run" /> + <option name="name" value="test" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="description" value="Assembles classes 'test'." /> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="testClasses" /> + </ExternalTaskPojo> + <ExternalTaskPojo> + <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> + <option name="name" value="wrapper" /> </ExternalTaskPojo> </list> </value> @@ -570,7 +559,7 @@ </option> <option name="modificationStamps"> <map> - <entry key="$PROJECT_DIR$" value="2797270596074" /> + <entry key="$PROJECT_DIR$" value="2838985564788" /> </map> </option> <option name="projectBuildClasspath"> @@ -585,41 +574,23 @@ <ExternalModuleBuildClasspathPojo> <option name="entries"> <list> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/9ce04e34240f674bc72680f8b843b1457383161a/commons-codec-1.9.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/28bb0405fddaf04f15058fbfbe01fe2780d7d3b6/commons-logging-1.1.3-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f/commons-logging-1.1.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/4c85b39e7f03471338bf7d36558eefe1e463e3de/commons-net-1.4.1-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/abb932adb2c10790c1eaa4365d3ac2a1ac7cb700/commons-net-1.4.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/6c5459816530a1962ac18cd315cc775b1b384329/commons-cli-1.2-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/61abb5ef010830519e47ac56faade402807b245b/exp4j-0.3.11-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/150e7b4a77af47b03a1b65be7a01bd663ea64420/exp4j-0.3.11.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar" /> </list> </option> @@ -648,77 +619,108 @@ <option value="C:/gradle/src/cli" /> <option value="C:/gradle/src/code-quality" /> <option value="C:/gradle/src/core" /> - <option value="C:/gradle/src/core-impl" /> - <option value="C:/gradle/src/cpp" /> + <option value="C:/gradle/src/dependency-management" /> <option value="C:/gradle/src/diagnostics" /> <option value="C:/gradle/src/ear" /> <option value="C:/gradle/src/ide" /> + <option value="C:/gradle/src/ide-native" /> <option value="C:/gradle/src/internal-integ-testing" /> <option value="C:/gradle/src/internal-testing" /> <option value="C:/gradle/src/ivy" /> <option value="C:/gradle/src/jacoco" /> <option value="C:/gradle/src/javascript" /> <option value="C:/gradle/src/jetty" /> - <option value="C:/gradle/src/language-base" /> + <option value="C:/gradle/src/language-groovy" /> + <option value="C:/gradle/src/language-java" /> <option value="C:/gradle/src/language-jvm" /> + <option value="C:/gradle/src/language-native" /> + <option value="C:/gradle/src/language-scala" /> <option value="C:/gradle/src/launcher" /> <option value="C:/gradle/src/maven" /> <option value="C:/gradle/src/messaging" /> + <option value="C:/gradle/src/model-core" /> + <option value="C:/gradle/src/model-groovy" /> <option value="C:/gradle/src/native" /> <option value="C:/gradle/src/open-api" /> <option value="C:/gradle/src/osgi" /> + <option value="C:/gradle/src/platform-base" /> + <option value="C:/gradle/src/platform-jvm" /> + <option value="C:/gradle/src/platform-native" /> + <option value="C:/gradle/src/platform-play" /> + <option value="C:/gradle/src/plugin-development" /> + <option value="C:/gradle/src/plugin-use" /> <option value="C:/gradle/src/plugins" /> <option value="C:/gradle/src/publish" /> <option value="C:/gradle/src/reporting" /> <option value="C:/gradle/src/resources" /> + <option value="C:/gradle/src/resources-http" /> + <option value="C:/gradle/src/resources-s3" /> + <option value="C:/gradle/src/resources-sftp" /> <option value="C:/gradle/src/scala" /> <option value="C:/gradle/src/signing" /> <option value="C:/gradle/src/sonar" /> + <option value="C:/gradle/src/testing-native" /> <option value="C:/gradle/src/tooling-api" /> + <option value="C:/gradle/src/tooling-api-builders" /> <option value="C:/gradle/src/ui" /> <option value="C:/gradle/src/wrapper" /> - <option value="C:/gradle/lib/ant-1.9.3.jar" /> - <option value="C:/gradle/lib/ant-launcher-1.9.3.jar" /> - <option value="C:/gradle/lib/gradle-base-services-1.12.jar" /> - <option value="C:/gradle/lib/gradle-base-services-groovy-1.12.jar" /> - <option value="C:/gradle/lib/gradle-cli-1.12.jar" /> - <option value="C:/gradle/lib/gradle-core-1.12.jar" /> - <option value="C:/gradle/lib/gradle-docs-1.12.jar" /> - <option value="C:/gradle/lib/gradle-launcher-1.12.jar" /> - <option value="C:/gradle/lib/gradle-messaging-1.12.jar" /> - <option value="C:/gradle/lib/gradle-native-1.12.jar" /> - <option value="C:/gradle/lib/gradle-open-api-1.12.jar" /> - <option value="C:/gradle/lib/gradle-resources-1.12.jar" /> - <option value="C:/gradle/lib/gradle-tooling-api-1.12.jar" /> - <option value="C:/gradle/lib/gradle-ui-1.12.jar" /> - <option value="C:/gradle/lib/gradle-wrapper-1.12.jar" /> - <option value="C:/gradle/lib/groovy-all-1.8.6.jar" /> - <option value="C:/gradle/lib/ivy-2.2.0.jar" /> - <option value="C:/gradle/lib/plugins/ant-antlr-1.9.3.jar" /> - <option value="C:/gradle/lib/plugins/gradle-announce-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-antlr-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-comparison-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-init-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-code-quality-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-core-impl-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-cpp-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-diagnostics-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ear-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ide-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ivy-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jacoco-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-javascript-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jetty-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-base-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-jvm-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-maven-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-osgi-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-plugins-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-publish-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-reporting-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-scala-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-signing-1.12.jar" /> - <option value="C:/gradle/lib/plugins/gradle-sonar-1.12.jar" /> + <option value="C:/gradle/lib/ant-1.9.4.jar" /> + <option value="C:/gradle/lib/ant-launcher-1.9.4.jar" /> + <option value="C:/gradle/lib/gradle-base-services-2.4.jar" /> + <option value="C:/gradle/lib/gradle-base-services-groovy-2.4.jar" /> + <option value="C:/gradle/lib/gradle-cli-2.4.jar" /> + <option value="C:/gradle/lib/gradle-core-2.4.jar" /> + <option value="C:/gradle/lib/gradle-docs-2.4.jar" /> + <option value="C:/gradle/lib/gradle-launcher-2.4.jar" /> + <option value="C:/gradle/lib/gradle-messaging-2.4.jar" /> + <option value="C:/gradle/lib/gradle-model-core-2.4.jar" /> + <option value="C:/gradle/lib/gradle-model-groovy-2.4.jar" /> + <option value="C:/gradle/lib/gradle-native-2.4.jar" /> + <option value="C:/gradle/lib/gradle-open-api-2.4.jar" /> + <option value="C:/gradle/lib/gradle-resources-2.4.jar" /> + <option value="C:/gradle/lib/gradle-tooling-api-2.4.jar" /> + <option value="C:/gradle/lib/gradle-ui-2.4.jar" /> + <option value="C:/gradle/lib/gradle-wrapper-2.4.jar" /> + <option value="C:/gradle/lib/groovy-all-2.3.10.jar" /> + <option value="C:/gradle/lib/plugins/gradle-announce-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-antlr-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-build-comparison-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-build-init-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-code-quality-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-dependency-management-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-diagnostics-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ear-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ide-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ide-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-ivy-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-jacoco-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-javascript-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-jetty-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-groovy-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-java-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-jvm-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-language-scala-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-maven-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-osgi-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-base-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-jvm-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-platform-play-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-plugin-development-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-plugin-use-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-plugins-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-publish-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-reporting-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-resources-http-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-resources-s3-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-resources-sftp-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-scala-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-signing-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-sonar-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-testing-native-2.4.jar" /> + <option value="C:/gradle/lib/plugins/gradle-tooling-api-builders-2.4.jar" /> + <option value="C:/gradle/lib/plugins/ivy-2.2.0.jar" /> <option value="$PROJECT_DIR$/buildSrc/src/main/java" /> <option value="$PROJECT_DIR$/buildSrc/src/main/groovy" /> </list> @@ -742,11 +744,12 @@ <list> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <option value="$PROJECT_DIR$/buildnum.properties" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> + <option value="$PROJECT_DIR$/build.gradle" /> + <option value="$PROJECT_DIR$/buildnum.properties" /> </list> </option> </component> @@ -1514,30 +1517,42 @@ <sortByType /> </navigator> <panes> - <pane id="PackagesPane"> - <subPane> + <pane id="Scope"> + <subPane subId="All"> <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> </PATH> <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> + <PATH_ELEMENT USER_OBJECT="mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="net.thauvin.erik.mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> + <PATH_ELEMENT USER_OBJECT="src"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> + </PATH_ELEMENT> + </PATH> + </subPane> + <subPane subId="Project Files"> + <PATH> + <PATH_ELEMENT USER_OBJECT="Root"> + <option name="myItemId" value="" /> + <option name="myItemType" value="" /> </PATH_ELEMENT> </PATH> </subPane> @@ -1625,42 +1640,30 @@ </PATH> </subPane> </pane> - <pane id="Scope"> - <subPane subId="All"> + <pane id="PackagesPane"> + <subPane> <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> </PATH_ELEMENT> </PATH> <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="src"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - <subPane subId="Project Files"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> + <PATH_ELEMENT> + <option name="myItemId" value="net.thauvin.erik.mobibot" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> </PATH_ELEMENT> </PATH> </subPane> @@ -1777,7 +1780,7 @@ <option name="referencePos" value="0" /> <option name="showLabels" value="true" /> </component> - <component name="RunManager" selected="Gradle.mobibot [deploy]"> + <component name="RunManager" selected="Gradle.mobibot [release]"> <configuration default="false" name="mobibot [distZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <ExternalSystemSettings> <option name="executionName" /> @@ -2391,11 +2394,12 @@ <workItem from="1440871303975" duration="34000" /> <workItem from="1440873148254" duration="1946000" /> <workItem from="1440876283433" duration="1663000" /> + <workItem from="1441045012377" duration="2858000" /> </task> <servers /> </component> <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="259033000" /> + <option name="totallyTimeSpent" value="261891000" /> </component> <component name="TodoView" selected-index="0"> <todo-panel id="selected-file"> @@ -2423,6 +2427,7 @@ <layout> <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31898972" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> @@ -2431,9 +2436,9 @@ <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43685687" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32692307" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.19625" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> + <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.263125" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="true" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18055555" sideWeight="0.5289929" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> @@ -2448,7 +2453,6 @@ <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31983805" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="FLOATING" visible="true" weight="0.32969153" sideWeight="0.5" order="19" side_tool="true" content_ui="tabs" x="2155" y="309" width="663" height="473" /> <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3228745" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> @@ -2616,49 +2620,6 @@ <option name="myLastEditedConfigurable" /> </component> <component name="editorHistoryManager"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="295" column="19" selection-start-line="292" selection-start-column="4" selection-end-line="295" selection-end-column="17" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="13" column="1" selection-start-line="13" selection-start-column="1" selection-end-line="13" selection-end-column="1" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="24" column="42" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="3" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> @@ -2874,7 +2835,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="17" column="24" selection-start-line="17" selection-start-column="24" selection-end-line="17" selection-end-column="24" /> - <folding /> </state> </provider> </entry> @@ -2882,10 +2842,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="74" column="49" selection-start-line="74" selection-start-column="49" selection-end-line="74" selection-end-column="49" /> - <folding> - <element signature="e#0#3762#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> </state> </provider> </entry> @@ -2893,10 +2849,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="43" column="2" selection-start-line="43" selection-start-column="2" selection-end-line="43" selection-end-column="2" /> - <folding> - <element signature="e#0#4204#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> </state> </provider> </entry> @@ -2904,10 +2856,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> - <folding> - <element signature="e#0#3642#0" expanded="false" /> - <element signature="imports" expanded="false" /> - </folding> </state> </provider> </entry> @@ -2915,7 +2863,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - <folding /> </state> </provider> </entry> @@ -2931,7 +2878,21 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> - <folding /> + <folding> + <element signature="imports" expanded="false" /> + <element signature="e#2506#2507#0" expanded="false" /> + <element signature="e#2544#2545#0" expanded="false" /> + <element signature="e#2646#2647#0" expanded="false" /> + <element signature="e#2667#2668#0" expanded="false" /> + <element signature="e#2832#2833#0" expanded="false" /> + <element signature="e#2861#2862#0" expanded="false" /> + <element signature="e#3011#3012#0" expanded="false" /> + <element signature="e#3029#3030#0" expanded="false" /> + <element signature="e#3159#3160#0" expanded="false" /> + <element signature="e#3177#3178#0" expanded="false" /> + <element signature="e#3321#3322#0" expanded="false" /> + <element signature="e#3344#3345#0" expanded="false" /> + </folding> </state> </provider> </entry> @@ -2940,7 +2901,9 @@ <state vertical-scroll-proportion="0.0"> <caret line="148" column="12" selection-start-line="148" selection-start-column="12" selection-end-line="148" selection-end-column="12" /> <folding> - <element signature="imports" expanded="true" /> + <element signature="e#0#6680#0" expanded="false" /> + <element signature="e#2789#2790#0" expanded="false" /> + <element signature="e#2810#2811#0" expanded="false" /> </folding> </state> </provider> @@ -2957,24 +2920,6 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="49" column="5" selection-start-line="49" selection-start-column="5" selection-end-line="49" selection-end-column="5" /> - <folding> - <element signature="e#1599#1600#0" expanded="true" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="2" column="16" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="72" column="0" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" /> <folding /> </state> </provider> @@ -2983,26 +2928,88 @@ <provider selected="true" editor-type-id="text-editor"> <state vertical-scroll-proportion="0.0"> <caret line="172" column="56" selection-start-line="172" selection-start-column="56" selection-end-line="172" selection-end-column="56" /> - <folding> - <element signature="e#0#5574#0" expanded="true" /> - </folding> + <folding /> </state> </provider> </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-13.275862"> + <state vertical-scroll-proportion="0.0"> <caret line="1255" column="40" selection-start-line="1255" selection-start-column="40" selection-end-line="1255" selection-end-column="40" /> <folding> - <element signature="e#0#68037#0" expanded="true" /> + <element signature="e#0#68037#0" expanded="false" /> </folding> </state> </provider> </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/twitter4j/util/CharacterUtil.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.12295869"> + <caret line="22" column="19" selection-start-line="22" selection-start-column="19" selection-end-line="22" selection-end-column="19" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/org/apache/oro/text/regex/Util.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="-4.8097982"> + <caret line="93" column="19" selection-start-line="93" selection-start-column="19" selection-end-line="93" selection-end-column="19" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/del/icio/us/DeliciousUtils.class"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="7.4814816"> + <caret line="15" column="13" selection-start-line="15" selection-start-column="13" selection-end-line="15" selection-end-column="13" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/org/apache/commons/codec/net/Utils.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="29" column="6" selection-start-line="29" selection-start-column="6" selection-end-line="29" selection-end-column="6" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/org/apache/commons/codec/net/URLCodec.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="47" column="13" selection-start-line="47" selection-start-column="13" selection-end-line="47" selection-end-column="13" /> + <folding /> + </state> + </provider> + </entry> + <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/org/jsoup/helper/StringUtil.java"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="8" column="19" selection-start-line="8" selection-start-column="19" selection-end-line="8" selection-end-column="19" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/build.gradle"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.0"> + <caret line="28" column="53" selection-start-line="28" selection-start-column="53" selection-end-line="28" selection-end-column="53" /> + <folding /> + </state> + </provider> + </entry> <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.554007"> - <caret line="102" column="91" selection-start-line="102" selection-start-column="91" selection-end-line="102" selection-end-column="91" /> + <state vertical-scroll-proportion="0.0"> + <caret line="103" column="44" selection-start-line="103" selection-start-column="44" selection-end-line="103" selection-end-column="44" /> + <folding /> + </state> + </provider> + </entry> + <entry file="file://$PROJECT_DIR$/buildnum.properties"> + <provider selected="true" editor-type-id="text-editor"> + <state vertical-scroll-proportion="0.05574913"> + <caret line="2" column="17" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> <folding /> </state> </provider> diff --git a/src/main/java/net/thauvin/erik/mobibot/Joke.java b/src/main/java/net/thauvin/erik/mobibot/Joke.java index bb6776d..98579b6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/Joke.java @@ -1,5 +1,5 @@ /* - * Quote.java + * Joke.java * * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -52,10 +52,9 @@ public class Joke implements Runnable { /** - * The I Heart Quotes URL. + * The ICNDB URL. */ - private static final String JOKE_URL = - "http://api.icndb.com/jokes/random"; + private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript"; /** * The bot's instance. @@ -100,7 +99,9 @@ public class Joke implements Runnable final JSONObject json = new JSONObject(sb.toString()); - bot.send(bot.getChannel(), Colors.CYAN + json.getJSONObject("value").get("joke") + Colors.CYAN); + bot.send(bot.getChannel(), + Colors.CYAN + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") + .replaceAll("\\\"", "\"") + Colors.CYAN); reader.close(); } diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 2b79193..1edd473 100644 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Sat Aug 29 12:08:20 PDT 2015 */ +/* Creation date Mon Aug 31 12:19:55 PDT 2015 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,11 +20,11 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1440875300470L). */ - private static final Date buildDate = new Date(1440875300470L); + /** buildDate (set during build process to 1441048795735L). */ + private static final Date buildDate = new Date(1441048795735L); /** - * Get buildDate (set during build process to Sat Aug 29 12:08:20 PDT 2015). + * Get buildDate (set during build process to Mon Aug 31 12:19:55 PDT 2015). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } From 9d67b343549a052737e524b0cb51ba9f0295ba82 Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Thu, 22 Oct 2015 16:00:45 -0700 Subject: [PATCH 019/842] Fixed .gitignore --- .gitignore | 11 +- README.txt | 2 +- mobibot.iws | 3168 --------------------------------------------------- 3 files changed, 11 insertions(+), 3170 deletions(-) delete mode 100644 mobibot.iws diff --git a/.gitignore b/.gitignore index 477820a..065b527 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ +**/.idea/dictionaries +**/.idea/gradle.xml +**/.idea/libraries +**/.idea/tasks.xml +**/.idea/workspace.xml +*.iws +.DS_Store .classpath .gradle -.idea .nb-gradle .project .settings @@ -10,6 +16,7 @@ /dist /fetcher.properties /gen +/local.properties /log4j.properties /logs /mobibot.properties @@ -19,3 +26,5 @@ /test-output /weather.log CVS +Thumbs.db +ehthumbs.db \ No newline at end of file diff --git a/README.txt b/README.txt index 1288bbf..333fbf7 100644 --- a/README.txt +++ b/README.txt @@ -17,7 +17,7 @@ Some very basic instructions: java -jar mobibot.jar -h { twitter oauth token request } - java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> + java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> { launch } /usr/bin/nohup java -jar mobibot.jar & diff --git a/mobibot.iws b/mobibot.iws deleted file mode 100644 index 57acae2..0000000 --- a/mobibot.iws +++ /dev/null @@ -1,3168 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="AnalysisUIOptions"> - <option name="SCOPE_TYPE" value="8" /> - <option name="CUSTOM_SCOPE_NAME" value="Source" /> - </component> - <component name="CCaseConfig"> - <option name="checkoutReserved" value="false" /> - <option name="markExternalChangeAsUpToDate" value="true" /> - <option name="checkInUseHijack" value="true" /> - <option name="useUcmModel" value="true" /> - <option name="isOffline" value="false" /> - <option name="synchOutside" value="false" /> - <option name="isHistoryResticted" value="true" /> - <option name="useIdenticalSwitch" value="true" /> - <option name="synchActivitiesOnRefresh" value="true" /> - <option name="lastScr" value="" /> - <option name="scrTextFileName" value="" /> - <option name="historyRevisionsNumber" value="4" /> - </component> - <component name="ChangeBrowserSettings"> - <option name="MAIN_SPLITTER_PROPORTION" value="0.3" /> - <option name="MESSAGES_SPLITTER_PROPORTION" value="0.8" /> - <option name="USE_DATE_BEFORE_FILTER" value="false" /> - <option name="USE_DATE_AFTER_FILTER" value="false" /> - <option name="USE_CHANGE_BEFORE_FILTER" value="false" /> - <option name="USE_CHANGE_AFTER_FILTER" value="false" /> - <option name="DATE_BEFORE" value="" /> - <option name="DATE_AFTER" value="" /> - <option name="CHANGE_BEFORE" value="" /> - <option name="CHANGE_AFTER" value="" /> - <option name="USE_USER_FILTER" value="false" /> - <option name="USER" value="" /> - </component> - <component name="ChangeListManager"> - <list default="true" id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment=""> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" afterPath="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/buildnum.properties" afterPath="$PROJECT_DIR$/buildnum.properties" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iml" afterPath="$PROJECT_DIR$/mobibot.iml" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.ipr" afterPath="$PROJECT_DIR$/mobibot.ipr" /> - <change type="MODIFICATION" beforePath="$PROJECT_DIR$/mobibot.iws" afterPath="$PROJECT_DIR$/mobibot.iws" /> - </list> - <ignored path="$USER_HOME$/.griffon/" /> - <ignored path="$USER_HOME$/.grails/" /> - <ignored path="$PROJECT_DIR$/out/" /> - <ignored path="$PROJECT_DIR$/.gradle/" /> - <ignored path="$PROJECT_DIR$/build/" /> - <ignored path=".idea/dataSources.local.xml" /> - <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" /> - <option name="TRACKING_ENABLED" value="true" /> - <option name="SHOW_DIALOG" value="false" /> - <option name="HIGHLIGHT_CONFLICTS" value="true" /> - <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> - <option name="LAST_RESOLUTION" value="IGNORE" /> - </component> - <component name="ChangesViewManager" flattened_view="true" show_ignored="false" /> - <component name="Commander"> - <leftPanel /> - <rightPanel /> - <splitter proportion="0.5" /> - </component> - <component name="CreatePatchCommitExecutor"> - <option name="PATCH_PATH" value="" /> - </component> - <component name="Cvs2Configuration"> - <option name="SHOW_CHANGES_REVISION_SETTINGS"> - <DateOrRevisionSettings> - <option name="BRANCH" value="1.2" /> - <option name="USE_BRANCH" value="true" /> - </DateOrRevisionSettings> - </option> - <option name="UPDATE_KEYWORD_SUBSTITUTION" value="NONE" /> - <option name="TAG_AFTER_PROJECT_COMMIT" value="true" /> - <option name="OVERRIDE_EXISTING_TAG_FOR_PROJECT" value="false" /> - <option name="TAG_AFTER_PROJECT_COMMIT_NAME" value="v0_4_6" /> - </component> - <component name="DebuggerManager"> - <ui_properties converted="true" /> - <breakpoint_any default_suspend_policy="SuspendAll" default_condition_enabled="true" converted="true"> - <breakpoint> - <option name="NOTIFY_CAUGHT" value="true" /> - <option name="NOTIFY_UNCAUGHT" value="true" /> - <option name="ENABLED" value="false" /> - <option name="LOG_ENABLED" value="false" /> - <option name="LOG_EXPRESSION_ENABLED" value="false" /> - <option name="REMOVE_AFTER_HIT" value="false" /> - <option name="SUSPEND_POLICY" value="SuspendAll" /> - <option name="SUSPEND" value="true" /> - <option name="COUNT_FILTER_ENABLED" value="false" /> - <option name="COUNT_FILTER" value="0" /> - <option name="CONDITION_ENABLED" value="false" /> - <option name="CLASS_FILTERS_ENABLED" value="false" /> - <option name="INSTANCE_FILTERS_ENABLED" value="false" /> - <option name="CONDITION" value="" /> - <option name="LOG_MESSAGE" value="" /> - </breakpoint> - <breakpoint> - <option name="NOTIFY_CAUGHT" value="true" /> - <option name="NOTIFY_UNCAUGHT" value="true" /> - <option name="ENABLED" value="false" /> - <option name="LOG_ENABLED" value="false" /> - <option name="LOG_EXPRESSION_ENABLED" value="false" /> - <option name="REMOVE_AFTER_HIT" value="false" /> - <option name="SUSPEND_POLICY" value="SuspendAll" /> - <option name="SUSPEND" value="true" /> - <option name="COUNT_FILTER_ENABLED" value="false" /> - <option name="COUNT_FILTER" value="0" /> - <option name="CONDITION_ENABLED" value="false" /> - <option name="CLASS_FILTERS_ENABLED" value="false" /> - <option name="INSTANCE_FILTERS_ENABLED" value="false" /> - <option name="CONDITION" value="" /> - <option name="LOG_MESSAGE" value="" /> - </breakpoint> - </breakpoint_any> - <breakpoint_rules converted="true" /> - </component> - <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> - <component name="ExternalProjectsManager"> - <system id="GRADLE"> - <state> - <task path="$PROJECT_DIR$"> - <activation /> - </task> - <projects_view> - <tree_state> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ExternalProjectsStructure$RootNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="Run Configurations" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.RunConfigurationsNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ExternalProjectsStructure$RootNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ProjectNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ExternalProjectsStructure$RootNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.ProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="Tasks" /> - <option name="myItemType" value="com.intellij.openapi.externalSystem.view.TasksNode" /> - </PATH_ELEMENT> - </PATH> - </tree_state> - </projects_view> - </state> - </system> - </component> - <component name="FavoritesManager"> - <favorites_list name="mobibot" /> - </component> - <component name="FavoritesProjectViewPane"> - <subPane subId="mobibot"> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" /> - <option name="myItemType" value="com.intellij.ide.favoritesTreeView.FavoritesTreeNodeDescriptor" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </component> - <component name="FavoritesViewImpl"> - <favorites_list name="mobibot"> - <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" /> - <option name="IS_SHOW_MEMBERS" value="false" /> - <option name="IS_STRUCTURE_VIEW" value="false" /> - <option name="IS_SHOW_MODULES" value="true" /> - <option name="IS_FLATTEN_PACKAGES" value="false" /> - <option name="IS_ABBREVIATION_PACKAGE_NAMES" value="false" /> - <option name="IS_HIDE_EMPTY_MIDDLE_PACKAGES" value="false" /> - <option name="IS_SHOW_LIBRARY_CONTENTS" value="true" /> - </favorites_list> - <option name="myCurrentFavoritesList" value="mobibot" /> - </component> - <component name="FileEditorManager"> - <leaf> - <file leaf-file-name="Joke.java" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="103" column="44" selection-start-line="103" selection-start-column="44" selection-end-line="103" selection-end-column="44" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="build.gradle" pinned="false" current-in-tab="false"> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="28" column="53" selection-start-line="28" selection-start-column="53" selection-end-line="28" selection-end-column="53" /> - <folding /> - </state> - </provider> - </entry> - </file> - <file leaf-file-name="buildnum.properties" pinned="false" current-in-tab="true"> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.05574913"> - <caret line="2" column="17" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> - <folding /> - </state> - </provider> - </entry> - </file> - </leaf> - </component> - <component name="FindManager"> - <FindUsagesManager> - <setting name="OPEN_NEW_TAB" value="false" /> - </FindUsagesManager> - </component> - <component name="Git.Settings"> - <option name="ROOT_SYNC" value="DONT_SYNC" /> - <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> - </component> - <component name="GitLogSettings"> - <option name="myDateState"> - <MyDateState /> - </option> - </component> - <component name="GradleLocalSettings"> - <option name="tasksExpandState"> - <map> - <entry key="" value="true" /> - <entry key="mobibot/" value="true" /> - </map> - </option> - <option name="recentTasks"> - <list> - <ExternalTaskExecutionInfo> - <option name="executorId" value="Run" /> - <option name="settings"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="taskNames"> - <list> - <option value="deploy" /> - </list> - </option> - </ExternalSystemSettings> - </option> - </ExternalTaskExecutionInfo> - <ExternalTaskExecutionInfo> - <option name="executorId" value="Run" /> - <option name="settings"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="taskNames"> - <list> - <option value="copyToDeployLib" /> - </list> - </option> - </ExternalSystemSettings> - </option> - </ExternalTaskExecutionInfo> - <ExternalTaskExecutionInfo> - <option name="executorId" value="Run" /> - <option name="settings"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="taskNames"> - <list> - <option value="clean" /> - </list> - </option> - </ExternalSystemSettings> - </option> - </ExternalTaskExecutionInfo> - <ExternalTaskExecutionInfo> - <option name="executorId" value="Run" /> - <option name="settings"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="taskNames"> - <list> - <option value="distZip" /> - </list> - </option> - </ExternalSystemSettings> - </option> - </ExternalTaskExecutionInfo> - <ExternalTaskExecutionInfo> - <option name="executorId" value="Run" /> - <option name="settings"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="taskNames"> - <list> - <option value="release" /> - </list> - </option> - </ExternalSystemSettings> - </option> - </ExternalTaskExecutionInfo> - <ExternalTaskExecutionInfo> - <option name="executorId" value="Run" /> - <option name="settings"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="taskNames"> - <list> - <option value="compileJava" /> - </list> - </option> - </ExternalSystemSettings> - </option> - </ExternalTaskExecutionInfo> - <ExternalTaskExecutionInfo> - <option name="executorId" value="Run" /> - <option name="settings"> - <ExternalSystemSettings> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="taskNames"> - <list> - <option value="build" /> - </list> - </option> - </ExternalSystemSettings> - </option> - </ExternalTaskExecutionInfo> - </list> - </option> - <option name="availableProjects"> - <map> - <entry> - <key> - <ExternalProjectPojo> - <option name="name" value="mobibot" /> - <option name="path" value="$PROJECT_DIR$" /> - </ExternalProjectPojo> - </key> - <value> - <list> - <ExternalProjectPojo> - <option name="name" value="mobibot" /> - <option name="path" value="$PROJECT_DIR$" /> - </ExternalProjectPojo> - </list> - </value> - </entry> - </map> - </option> - <option name="availableTasks"> - <map> - <entry key="$PROJECT_DIR$"> - <value> - <list> - <ExternalTaskPojo> - <option name="description" value="Assembles the outputs of this project." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="assemble" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles the main distributions" /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="assembleMainDist" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="build" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project and all projects that depend on it." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="buildDependents" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles and tests this project and all projects it depends on." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="buildNeeded" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs all checks." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="check" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles classes 'main'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="classes" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Deletes the build directory." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="clean" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'main:java'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="compileJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Compiles Java source 'test:java'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="compileTestJava" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays the components produced by root project 'mobibot'. [incubating]" /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="components" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="copyToDeploy" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="copyToDeployLib" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays all dependencies declared in root project 'mobibot'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="dependencies" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays the insight into a specific dependency in root project 'mobibot'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="dependencyInsight" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Copies all needed files to the deploy directory." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="deploy" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a distribution." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="distTar" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Bundles the project as a distribution." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="distZip" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays a help message." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="help" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Initializes a new Gradle build. [incubating]" /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="init" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Installs the project as a JVM application along with libs and OS specific scripts." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="installApp" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Installs the project as a distribution as-is." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="installDist" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles a jar archive containing the main classes." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="jar" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Generates Javadoc API documentation for the main source code." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="javadoc" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays the configuration model of root project 'mobibot'. [incubating]" /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="model" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Processes JVM resources 'main:resources'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="processResources" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Processes JVM resources 'test:resources'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="processTestResources" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays the sub-projects of root project 'mobibot'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="projects" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays the properties of root project 'mobibot'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="properties" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Releases new version." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="release" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs this project as a JVM application" /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="run" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Creates OS specific scripts to run the project as a JVM application." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="startScripts" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Displays the tasks runnable from root project 'mobibot'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="tasks" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Runs the unit tests." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="test" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="description" value="Assembles classes 'test'." /> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="testClasses" /> - </ExternalTaskPojo> - <ExternalTaskPojo> - <option name="linkedExternalProjectPath" value="$PROJECT_DIR$" /> - <option name="name" value="wrapper" /> - </ExternalTaskPojo> - </list> - </value> - </entry> - </map> - </option> - <option name="modificationStamps"> - <map> - <entry key="$PROJECT_DIR$" value="2838985564788" /> - </map> - </option> - <option name="projectBuildClasspath"> - <map> - <entry key="$PROJECT_DIR$"> - <value> - <ExternalProjectBuildClasspathPojo> - <option name="modulesBuildClasspath"> - <map> - <entry key="$PROJECT_DIR$"> - <value> - <ExternalModuleBuildClasspathPojo> - <option name="entries"> - <list> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/9ce04e34240f674bc72680f8b843b1457383161a/commons-codec-1.9.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f/commons-logging-1.1.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/abb932adb2c10790c1eaa4365d3ac2a1ac7cb700/commons-net-1.4.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/150e7b4a77af47b03a1b65be7a01bd663ea64420/exp4j-0.3.11.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar" /> - </list> - </option> - <option name="path" value="$PROJECT_DIR$" /> - </ExternalModuleBuildClasspathPojo> - </value> - </entry> - <entry key="K:\java\mobibot"> - <value> - <ExternalModuleBuildClasspathPojo> - <option name="path" value="K:\java\mobibot" /> - </ExternalModuleBuildClasspathPojo> - </value> - </entry> - </map> - </option> - <option name="name" value="mobibot" /> - <option name="projectBuildClasspath"> - <list> - <option value="C:/gradle/src/announce" /> - <option value="C:/gradle/src/antlr" /> - <option value="C:/gradle/src/base-services" /> - <option value="C:/gradle/src/base-services-groovy" /> - <option value="C:/gradle/src/build-comparison" /> - <option value="C:/gradle/src/build-init" /> - <option value="C:/gradle/src/cli" /> - <option value="C:/gradle/src/code-quality" /> - <option value="C:/gradle/src/core" /> - <option value="C:/gradle/src/dependency-management" /> - <option value="C:/gradle/src/diagnostics" /> - <option value="C:/gradle/src/ear" /> - <option value="C:/gradle/src/ide" /> - <option value="C:/gradle/src/ide-native" /> - <option value="C:/gradle/src/internal-integ-testing" /> - <option value="C:/gradle/src/internal-testing" /> - <option value="C:/gradle/src/ivy" /> - <option value="C:/gradle/src/jacoco" /> - <option value="C:/gradle/src/javascript" /> - <option value="C:/gradle/src/jetty" /> - <option value="C:/gradle/src/language-groovy" /> - <option value="C:/gradle/src/language-java" /> - <option value="C:/gradle/src/language-jvm" /> - <option value="C:/gradle/src/language-native" /> - <option value="C:/gradle/src/language-scala" /> - <option value="C:/gradle/src/launcher" /> - <option value="C:/gradle/src/maven" /> - <option value="C:/gradle/src/messaging" /> - <option value="C:/gradle/src/model-core" /> - <option value="C:/gradle/src/model-groovy" /> - <option value="C:/gradle/src/native" /> - <option value="C:/gradle/src/open-api" /> - <option value="C:/gradle/src/osgi" /> - <option value="C:/gradle/src/platform-base" /> - <option value="C:/gradle/src/platform-jvm" /> - <option value="C:/gradle/src/platform-native" /> - <option value="C:/gradle/src/platform-play" /> - <option value="C:/gradle/src/plugin-development" /> - <option value="C:/gradle/src/plugin-use" /> - <option value="C:/gradle/src/plugins" /> - <option value="C:/gradle/src/publish" /> - <option value="C:/gradle/src/reporting" /> - <option value="C:/gradle/src/resources" /> - <option value="C:/gradle/src/resources-http" /> - <option value="C:/gradle/src/resources-s3" /> - <option value="C:/gradle/src/resources-sftp" /> - <option value="C:/gradle/src/scala" /> - <option value="C:/gradle/src/signing" /> - <option value="C:/gradle/src/sonar" /> - <option value="C:/gradle/src/testing-native" /> - <option value="C:/gradle/src/tooling-api" /> - <option value="C:/gradle/src/tooling-api-builders" /> - <option value="C:/gradle/src/ui" /> - <option value="C:/gradle/src/wrapper" /> - <option value="C:/gradle/lib/ant-1.9.4.jar" /> - <option value="C:/gradle/lib/ant-launcher-1.9.4.jar" /> - <option value="C:/gradle/lib/gradle-base-services-2.4.jar" /> - <option value="C:/gradle/lib/gradle-base-services-groovy-2.4.jar" /> - <option value="C:/gradle/lib/gradle-cli-2.4.jar" /> - <option value="C:/gradle/lib/gradle-core-2.4.jar" /> - <option value="C:/gradle/lib/gradle-docs-2.4.jar" /> - <option value="C:/gradle/lib/gradle-launcher-2.4.jar" /> - <option value="C:/gradle/lib/gradle-messaging-2.4.jar" /> - <option value="C:/gradle/lib/gradle-model-core-2.4.jar" /> - <option value="C:/gradle/lib/gradle-model-groovy-2.4.jar" /> - <option value="C:/gradle/lib/gradle-native-2.4.jar" /> - <option value="C:/gradle/lib/gradle-open-api-2.4.jar" /> - <option value="C:/gradle/lib/gradle-resources-2.4.jar" /> - <option value="C:/gradle/lib/gradle-tooling-api-2.4.jar" /> - <option value="C:/gradle/lib/gradle-ui-2.4.jar" /> - <option value="C:/gradle/lib/gradle-wrapper-2.4.jar" /> - <option value="C:/gradle/lib/groovy-all-2.3.10.jar" /> - <option value="C:/gradle/lib/plugins/gradle-announce-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-antlr-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-comparison-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-build-init-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-code-quality-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-dependency-management-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-diagnostics-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ear-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ide-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ide-native-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-ivy-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jacoco-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-javascript-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-jetty-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-groovy-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-java-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-jvm-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-native-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-language-scala-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-maven-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-osgi-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-platform-base-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-platform-jvm-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-platform-native-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-platform-play-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-plugin-development-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-plugin-use-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-plugins-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-publish-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-reporting-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-resources-http-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-resources-s3-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-resources-sftp-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-scala-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-signing-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-sonar-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-testing-native-2.4.jar" /> - <option value="C:/gradle/lib/plugins/gradle-tooling-api-builders-2.4.jar" /> - <option value="C:/gradle/lib/plugins/ivy-2.2.0.jar" /> - <option value="$PROJECT_DIR$/buildSrc/src/main/java" /> - <option value="$PROJECT_DIR$/buildSrc/src/main/groovy" /> - </list> - </option> - </ExternalProjectBuildClasspathPojo> - </value> - </entry> - </map> - </option> - </component> - <component name="HierarchyBrowserManager"> - <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" /> - <option name="SORT_ALPHABETICALLY" value="false" /> - <option name="HIDE_CLASSES_WHERE_METHOD_NOT_IMPLEMENTED" value="false" /> - </component> - <component name="HighlightingSettingsPerFile"> - <setting file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" root0="FORCE_HIGHLIGHTING" /> - </component> - <component name="IdeDocumentHistory"> - <option name="CHANGED_PATHS"> - <list> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Quote.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java" /> - <option value="$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java" /> - <option value="$PROJECT_DIR$/build.gradle" /> - <option value="$PROJECT_DIR$/buildnum.properties" /> - </list> - </option> - </component> - <component name="IntelliTail"> - <property name="buffer-wrap" value="false" /> - <property name="buffer-size" value="50" /> - <property name="external-editor" value="c:\Windows\System32\notepad.exe" /> - <file name="c:\progra~1\jetbra~1\intell~1.0\bin" filters-active="false" display-non-matching="false" /> - <property name="recent-files" value="" /> - <property name="poll-interval" value="500" /> - <property name="external-in-idea" value="false" /> - </component> - <component name="JsBuildToolGruntFileManager" detection-done="true" /> - <component name="JsGulpfileManager"> - <detection-done>true</detection-done> - </component> - <component name="MavenImportPreferences"> - <option name="generalSettings"> - <MavenGeneralSettings> - <option name="mavenHome" value="Bundled (Maven 3)" /> - </MavenGeneralSettings> - </option> - </component> - <component name="MavenProjectNavigator"> - <treeState /> - </component> - <component name="ModuleEditorState"> - <option name="LAST_EDITED_MODULE_NAME" value="mobibot" /> - <option name="LAST_EDITED_TAB_NAME" value="Libraries (Classpath)" /> - </component> - <component name="NamedScopeManager"> - <order /> - </component> - <component name="PackagesPane"> - <subPane> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot.ipr" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot.ipr" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="net.thauvin.erik.mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </component> - <component name="PerforceChangeBrowserSettings"> - <option name="USE_CLIENT_FILTER" value="true" /> - <option name="CLIENT" value="" /> - </component> - <component name="PerforceDirect.Settings"> - <option name="port" value="jasper:1666" /> - </component> - <component name="ProjectFrameBounds"> - <option name="x" value="1592" /> - <option name="y" value="-8" /> - <option name="width" value="1616" /> - <option name="height" value="1204" /> - </component> - <component name="ProjectInspectionProfilesVisibleTreeState"> - <entry key="Default"> - <profile-state> - <expanded-state> - <State> - <id /> - </State> - <State> - <id>Abstraction issues</id> - </State> - <State> - <id>Declaration redundancy</id> - </State> - <State> - <id>JUnit issues</id> - </State> - <State> - <id>JavaScript</id> - </State> - <State> - <id>JavaScript function metricsJavaScript</id> - </State> - <State> - <id>Performance issues</id> - </State> - </expanded-state> - <selected-state> - <State> - <id>WeakerAccess</id> - </State> - </selected-state> - </profile-state> - </entry> - <entry key="Erik's Default"> - <profile-state> - <expanded-state> - <State> - <id /> - </State> - <State> - <id>AOP</id> - </State> - <State> - <id>Abstraction issues</id> - </State> - <State> - <id>ActionScript specificJavaScript</id> - </State> - <State> - <id>Android</id> - </State> - <State> - <id>Android Lint</id> - </State> - <State> - <id>Annotations verifyingGroovy</id> - </State> - <State> - <id>Ant inspections</id> - </State> - <State> - <id>Application Server Specific Inspections</id> - </State> - <State> - <id>Assignment issues</id> - </State> - <State> - <id>Assignment issuesGroovy</id> - </State> - <State> - <id>Assignment issuesJavaScript</id> - </State> - <State> - <id>BPMN 2.0 configuration errors</id> - </State> - <State> - <id>Batch Applications Issues</id> - </State> - <State> - <id>Bean Validation issues</id> - </State> - <State> - <id>Bitwise operation issues</id> - </State> - <State> - <id>Bitwise operation issuesJavaScript</id> - </State> - <State> - <id>CDI(Contexts and Dependency Injection) issues</id> - </State> - <State> - <id>CFML</id> - </State> - <State> - <id>CSS</id> - </State> - <State> - <id>Class metrics</id> - </State> - <State> - <id>Class structure</id> - </State> - <State> - <id>Cloning issues</id> - </State> - <State> - <id>Code maturity issues</id> - </State> - <State> - <id>Code quality toolsJavaScript</id> - </State> - <State> - <id>Code style issues</id> - </State> - <State> - <id>Code style issuesCSS</id> - </State> - <State> - <id>Code style issuesJavaScript</id> - </State> - <State> - <id>CoffeeScript</id> - </State> - <State> - <id>Compiler issues</id> - </State> - <State> - <id>Concurrency annotation issues</id> - </State> - <State> - <id>Control FlowGroovy</id> - </State> - <State> - <id>Control flow issues</id> - </State> - <State> - <id>Control flow issuesJavaScript</id> - </State> - <State> - <id>Cucumber</id> - </State> - <State> - <id>Cucumber Java</id> - </State> - <State> - <id>DOM issuesJavaScript</id> - </State> - <State> - <id>Data flow issues</id> - </State> - <State> - <id>Data flow issuesGroovy</id> - </State> - <State> - <id>Data flow issuesJavaScript</id> - </State> - <State> - <id>Declaration redundancy</id> - </State> - <State> - <id>Declaration redundancyGroovy</id> - </State> - <State> - <id>DeclarationGroovy</id> - </State> - <State> - <id>Dependency issues</id> - </State> - <State> - <id>Encapsulation issues</id> - </State> - <State> - <id>Error handling</id> - </State> - <State> - <id>Error handlingGroovy</id> - </State> - <State> - <id>Error handlingJavaScript</id> - </State> - <State> - <id>Faces Model</id> - </State> - <State> - <id>Finalization issues</id> - </State> - <State> - <id>FlexUnit inspections</id> - </State> - <State> - <id>FreeMarker inspections</id> - </State> - <State> - <id>GPath inspectionsGroovy</id> - </State> - <State> - <id>GSPGrailsGroovy</id> - </State> - <State> - <id>General</id> - </State> - <State> - <id>GeneralCoffeeScript</id> - </State> - <State> - <id>GeneralJavaScript</id> - </State> - <State> - <id>Google App Engine</id> - </State> - <State> - <id>Google Web Toolkit issues</id> - </State> - <State> - <id>Gradle</id> - </State> - <State> - <id>GrailsGroovy</id> - </State> - <State> - <id>Groovy</id> - </State> - <State> - <id>Guice Inspections</id> - </State> - <State> - <id>HTML</id> - </State> - <State> - <id>Hibernate Issues</id> - </State> - <State> - <id>Imports</id> - </State> - <State> - <id>Inheritance issues</id> - </State> - <State> - <id>Initialization issues</id> - </State> - <State> - <id>Internationalization issues</id> - </State> - <State> - <id>Invalid elementsCSS</id> - </State> - <State> - <id>J2ME Plugin</id> - </State> - <State> - <id>J2ME issues</id> - </State> - <State> - <id>JBoss Seam issues</id> - </State> - <State> - <id>JPA issues</id> - </State> - <State> - <id>JSF annotated elements errors</id> - </State> - <State> - <id>JSP Inspections</id> - </State> - <State> - <id>JUnit issues</id> - </State> - <State> - <id>Java EE issues</id> - </State> - <State> - <id>Java language level issues</id> - </State> - <State> - <id>Java language level migration aids</id> - </State> - <State> - <id>JavaBeans issues</id> - </State> - <State> - <id>JavaFX</id> - </State> - <State> - <id>JavaScript</id> - </State> - <State> - <id>JavaScript function metricsJavaScript</id> - </State> - <State> - <id>JavaScript validity issuesJavaScript</id> - </State> - <State> - <id>Javadoc issues</id> - </State> - <State> - <id>Jpdl Model</id> - </State> - <State> - <id>LESS</id> - </State> - <State> - <id>Language Injection</id> - </State> - <State> - <id>Logging issues</id> - </State> - <State> - <id>Manifest</id> - </State> - <State> - <id>Maven</id> - </State> - <State> - <id>Memory issues</id> - </State> - <State> - <id>Method MetricsGroovy</id> - </State> - <State> - <id>Method metrics</id> - </State> - <State> - <id>Modularization issues</id> - </State> - <State> - <id>Naming ConventionsGroovy</id> - </State> - <State> - <id>Naming conventions</id> - </State> - <State> - <id>Naming conventionsJavaScript</id> - </State> - <State> - <id>Numeric issues</id> - </State> - <State> - <id>OSGi</id> - </State> - <State> - <id>OtherGroovy</id> - </State> - <State> - <id>Packaging issues</id> - </State> - <State> - <id>Pages Navigation Model</id> - </State> - <State> - <id>Pattern Validation</id> - </State> - <State> - <id>Performance issues</id> - </State> - <State> - <id>Play</id> - </State> - <State> - <id>Plugin DevKit</id> - </State> - <State> - <id>Portability issues</id> - </State> - <State> - <id>Potentially confusing code constructsGroovy</id> - </State> - <State> - <id>Potentially confusing code constructsJavaScript</id> - </State> - <State> - <id>Probable bugs</id> - </State> - <State> - <id>Probable bugsCSS</id> - </State> - <State> - <id>Probable bugsCoffeeScript</id> - </State> - <State> - <id>Probable bugsGradle</id> - </State> - <State> - <id>Probable bugsGroovy</id> - </State> - <State> - <id>Probable bugsJavaScript</id> - </State> - <State> - <id>Properties Files</id> - </State> - <State> - <id>RELAX NG</id> - </State> - <State> - <id>RESTful Web Service</id> - </State> - <State> - <id>Resource management issues</id> - </State> - <State> - <id>SASS/SCSS</id> - </State> - <State> - <id>SQL</id> - </State> - <State> - <id>Security issues</id> - </State> - <State> - <id>Serialization issues</id> - </State> - <State> - <id>Spelling</id> - </State> - <State> - <id>Spring BatchSpring Model</id> - </State> - <State> - <id>Spring DataSpring Model</id> - </State> - <State> - <id>Spring IntegrationSpring Model</id> - </State> - <State> - <id>Spring MVCSpring Model</id> - </State> - <State> - <id>Spring Model</id> - </State> - <State> - <id>Spring OSGiSpring Model</id> - </State> - <State> - <id>Spring SecuritySpring Model</id> - </State> - <State> - <id>Spring Web FlowSpring Model</id> - </State> - <State> - <id>Spring Web ServicesSpring Model</id> - </State> - <State> - <id>Struts</id> - </State> - <State> - <id>Struts 1Struts</id> - </State> - <State> - <id>Struts 2Struts</id> - </State> - <State> - <id>StyleGroovy</id> - </State> - <State> - <id>Tapestry inspections</id> - </State> - <State> - <id>TestNG</id> - </State> - <State> - <id>Threading issues</id> - </State> - <State> - <id>Threading issuesGroovy</id> - </State> - <State> - <id>UI Form Problems</id> - </State> - <State> - <id>Validity issuesGroovy</id> - </State> - <State> - <id>Velocity inspections</id> - </State> - <State> - <id>Verbose or redundant code constructs</id> - </State> - <State> - <id>Visibility issues</id> - </State> - <State> - <id>WSDL issues</id> - </State> - <State> - <id>Web Services</id> - </State> - <State> - <id>WebSocket issues</id> - </State> - <State> - <id>XML</id> - </State> - <State> - <id>XPath</id> - </State> - <State> - <id>XSLT</id> - </State> - <State> - <id>toString() issues</id> - </State> - </expanded-state> - <selected-state> - <State> - <id>Abstraction issues</id> - </State> - </selected-state> - </profile-state> - </entry> - <entry key="Project Default"> - <profile-state> - <expanded-state> - <State> - <id /> - </State> - <State> - <id>Android</id> - </State> - <State> - <id>Android Lint</id> - </State> - <State> - <id>CDI(Contexts and Dependency Injection) issues</id> - </State> - <State> - <id>Class metrics</id> - </State> - <State> - <id>Class structure</id> - </State> - <State> - <id>Cloning issues</id> - </State> - <State> - <id>Code style issues</id> - </State> - <State> - <id>Concurrency annotation issues</id> - </State> - <State> - <id>Control FlowGroovy</id> - </State> - <State> - <id>Declaration redundancy</id> - </State> - <State> - <id>DeclarationGroovy</id> - </State> - <State> - <id>Encapsulation issues</id> - </State> - <State> - <id>Error handling</id> - </State> - <State> - <id>Error handlingGroovy</id> - </State> - <State> - <id>Error handlingJavaScript</id> - </State> - <State> - <id>Finalization issues</id> - </State> - <State> - <id>Groovy</id> - </State> - <State> - <id>Inheritance issues</id> - </State> - <State> - <id>Initialization issues</id> - </State> - <State> - <id>J2ME issues</id> - </State> - <State> - <id>JPA issues</id> - </State> - <State> - <id>JUnit issues</id> - </State> - <State> - <id>Java EE issues</id> - </State> - <State> - <id>Java language level migration aids</id> - </State> - <State> - <id>JavaScript</id> - </State> - <State> - <id>Logging issues</id> - </State> - <State> - <id>Manifest</id> - </State> - <State> - <id>Memory issues</id> - </State> - <State> - <id>Naming ConventionsGroovy</id> - </State> - <State> - <id>Naming conventions</id> - </State> - <State> - <id>Performance issues</id> - </State> - <State> - <id>Probable bugs</id> - </State> - <State> - <id>Resource management issues</id> - </State> - <State> - <id>Security issues</id> - </State> - <State> - <id>Serialization issues</id> - </State> - <State> - <id>Threading issues</id> - </State> - <State> - <id>Threading issuesGroovy</id> - </State> - </expanded-state> - <selected-state> - <State> - <id>LocalCanBeFinal</id> - </State> - </selected-state> - </profile-state> - </entry> - </component> - <component name="ProjectLevelVcsManager" settingsEditedManually="true"> - <OptionsSetting value="true" id="Add" /> - <OptionsSetting value="true" id="Remove" /> - <OptionsSetting value="true" id="Checkin" /> - <OptionsSetting value="true" id="Checkout" /> - <OptionsSetting value="true" id="Update" /> - <OptionsSetting value="true" id="Status" /> - <OptionsSetting value="true" id="Edit" /> - <OptionsSetting value="true" id="Undo Check Out" /> - <OptionsSetting value="true" id="Compare with SourceSafe Version" /> - <OptionsSetting value="true" id="Get Latest Version" /> - <ConfirmationsSetting value="0" id="Add" /> - <ConfirmationsSetting value="0" id="Remove" /> - </component> - <component name="ProjectPane"> - <subPane> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="PsiDirectory:M:\java\mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </component> - <component name="ProjectReloadState"> - <option name="STATE" value="0" /> - </component> - <component name="ProjectView"> - <navigator currentView="ProjectPane" proportions="" version="1"> - <flattenPackages ProjectPane="true" /> - <showMembers /> - <showModules /> - <showLibraryContents /> - <hideEmptyPackages /> - <abbreviatePackageNames /> - <autoscrollToSource /> - <autoscrollFromSource /> - <sortByType /> - </navigator> - <panes> - <pane id="Scope"> - <subPane subId="All"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="src"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - <PATH_ELEMENT USER_OBJECT="net/thauvin/erik/mobibot"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - <subPane subId="Project Files"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </pane> - <pane id="Scratches" /> - <pane id="ProjectPane"> - <subPane> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="External Libraries" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ExternalLibrariesNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="src" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="src" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="main" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="src" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="main" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="java" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </pane> - <pane id="PackagesPane"> - <subPane> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> - </PATH_ELEMENT> - </PATH> - <PATH> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" /> - </PATH_ELEMENT> - <PATH_ELEMENT> - <option name="myItemId" value="net.thauvin.erik.mobibot" /> - <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageElementNode" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </pane> - <pane id="Favorites" /> - </panes> - </component> - <component name="PropertiesComponent"> - <property name="cvs_file_history_treeOrder6" value="6" /> - <property name="vcs_file_view_flatOrder5" value="5" /> - <property name="cvs_file_history_treeOrder5" value="5" /> - <property name="options.splitter.main.proportions" value="0.3" /> - <property name="cvs_file_history_treeOrder2" value="2" /> - <property name="cvs_file_history_treeOrder1" value="1" /> - <property name="cvs_file_history_treeOrder4" value="4" /> - <property name="RunManagerConfig.showSettingsBeforeRunnig" value="true" /> - <property name="cvs_file_history_treeOrder3" value="3" /> - <property name="recentsLimit" value="5" /> - <property name="vcs_file_view_treeOrder2" value="2" /> - <property name="MemberChooser.sorted" value="false" /> - <property name="vcs_file_view_treeOrder3" value="3" /> - <property name="vcs_file_view_treeOrder0" value="0" /> - <property name="vcs_file_view_treeOrder1" value="1" /> - <property name="vcs_file_view_treeOrder4" value="4" /> - <property name="vcs_file_view_flatWidth5" value="81" /> - <property name="cvs_file_history_flatWidth6" value="135" /> - <property name="WebServerToolWindowFactoryState" value="false" /> - <property name="cvs_file_history_flatWidth4" value="135" /> - <property name="cvs_file_history_flatWidth5" value="136" /> - <property name="vcs_file_view_flatOrder0" value="0" /> - <property name="cvs_file_history_flatWidth2" value="135" /> - <property name="FullScreen" value="false" /> - <property name="cvs_file_history_flatWidth3" value="136" /> - <property name="cvs_file_history_treeOrder0" value="0" /> - <property name="vcs_file_view_flatWidth0" value="81" /> - <property name="vcs_file_view_flatOrder2" value="2" /> - <property name="cvs_file_history_flatWidth0" value="135" /> - <property name="vcs_file_view_flatWidth1" value="81" /> - <property name="GoToClass.includeLibraries" value="false" /> - <property name="vcs_file_view_flatWidth2" value="81" /> - <property name="cvs_file_history_flatWidth1" value="135" /> - <property name="vcs_file_view_flatOrder1" value="1" /> - <property name="vcs_file_view_flatWidth3" value="81" /> - <property name="vcs_file_view_flatOrder4" value="4" /> - <property name="options.splitter.details.proportions" value="0.2" /> - <property name="vcs_file_view_flatOrder3" value="3" /> - <property name="vcs_file_view_flatWidth4" value="82" /> - <property name="GoToFile.includeJavaFiles" value="false" /> - <property name="RunManagerConfig.compileBeforeRunning" value="true" /> - <property name="options.lastSelected" value="copyright.profiles" /> - <property name="project.structure.side.proportion" value="0.2" /> - <property name="MemberChooser.copyJavadoc" value="false" /> - <property name="project.structure.last.edited" value="SDKs" /> - <property name="vcs_file_view_treeWidth4" value="100" /> - <property name="project.structure.proportion" value="0.15" /> - <property name="vcs_file_view_treeWidth1" value="101" /> - <property name="vcs_file_view_treeWidth0" value="100" /> - <property name="vcs_file_view_treeWidth3" value="101" /> - <property name="vcs_file_view_treeWidth2" value="100" /> - <property name="last_opened_file_path" value="C:/Program Files/Git/bin/git.exe" /> - <property name="GoToClass.toSaveIncludeLibraries" value="false" /> - <property name="cvs_file_history_treeWidth0" value="135" /> - <property name="cvs_file_history_treeWidth1" value="135" /> - <property name="cvs_file_history_treeWidth2" value="135" /> - <property name="cvs_file_history_treeWidth3" value="136" /> - <property name="cvs_file_history_treeWidth4" value="135" /> - <property name="MemberChooser.showClasses" value="true" /> - <property name="cvs_file_history_treeWidth5" value="136" /> - <property name="cvs_file_history_treeWidth6" value="135" /> - <property name="cvs_file_history_flatOrder4" value="4" /> - <property name="cvs_file_history_flatOrder5" value="5" /> - <property name="cvs_file_history_flatOrder2" value="2" /> - <property name="cvs_file_history_flatOrder3" value="3" /> - <property name="cvs_file_history_flatOrder0" value="0" /> - <property name="cvs_file_history_flatOrder1" value="1" /> - <property name="cvs_file_history_flatOrder6" value="6" /> - <property name="restartRequiresConfirmation" value="true" /> - <property name="options.searchVisible" value="true" /> - <property name="dynamic.classpath" value="false" /> - <property name="LayoutCode.rearrangeEntriesJava" value="true" /> - <property name="LayoutCode.rearrangeEntriesGroovy" value="false" /> - <property name="LayoutCode.rearrangeEntriesHTML" value="false" /> - <property name="OverrideImplement.combined" value="true" /> - <property name="OverrideImplement.overriding.sorted" value="false" /> - <property name="LayoutCode.rearrangeEntries" value="true" /> - <property name="aspect.path.notification.shown" value="true" /> - <property name="settings.editor.selected.configurable" value="vcs.Git" /> - <property name="settings.editor.splitter.proportion" value="0.2" /> - </component> - <component name="RecentsManager"> - <key name="CopyClassDialog.RECENTS_KEY"> - <recent name="net.thauvin.erik.mobibot" /> - </key> - <key name="MoveMembersDialog.RECENTS_KEY"> - <recent name="net.thauvin.erik.mobibot.Utils" /> - <recent name="net.thauvin.erik.mobibot.Commands" /> - <recent name="net.thauvin.erik.mobibot.War" /> - <recent name="net.thauvin.erik.mobibot.Time" /> - <recent name="net.thauvin.erik.mobibot.EntriesMgr" /> - </key> - <key name="MoveFile.RECENT_KEYS"> - <recent name="K:\java\mobibot\src\main\resources\META-INF" /> - <recent name="K:\java\mobibot\src\main\java\net\thauvin\erik" /> - </key> - </component> - <component name="Regex"> - <option name="pos1" value="218" /> - <option name="pos2" value="218" /> - <option name="pos3" value="116" /> - <option name="pos4" value="444" /> - <option name="pos5" value="117" /> - <option name="autoUpdate" value="true" /> - <option name="referenceOn" value="false" /> - <option name="referencePos" value="0" /> - <option name="showLabels" value="true" /> - </component> - <component name="RunManager" selected="Gradle.mobibot [release]"> - <configuration default="false" name="mobibot [distZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="distZip" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> - <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> - <method /> - </configuration> - <configuration default="false" name="mobibot [copyToDeployLib]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="copyToDeployLib" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> - <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> - <method /> - </configuration> - <configuration default="false" name="mobibot [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="clean" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <RunnerSettings RunnerId="ExternalSystemTaskRunner" /> - <ConfigurationWrapper RunnerId="ExternalSystemTaskRunner" /> - <method /> - </configuration> - <configuration default="false" name="mobibot [deploy]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="deploy" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <method /> - </configuration> - <configuration default="false" name="mobibot [release]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value="release" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <method /> - </configuration> - <configuration default="true" type="AndroidRunConfigurationType" factoryName="Android Application"> - <module name="" /> - <option name="ACTIVITY_CLASS" value="" /> - <option name="MODE" value="default_activity" /> - <option name="DEPLOY" value="true" /> - <option name="ARTIFACT_NAME" value="" /> - <option name="TARGET_SELECTION_MODE" value="EMULATOR" /> - <option name="USE_LAST_SELECTED_DEVICE" value="false" /> - <option name="PREFERRED_AVD" value="" /> - <option name="USE_COMMAND_LINE" value="true" /> - <option name="COMMAND_LINE" value="" /> - <option name="WIPE_USER_DATA" value="false" /> - <option name="DISABLE_BOOT_ANIMATION" value="false" /> - <option name="NETWORK_SPEED" value="full" /> - <option name="NETWORK_LATENCY" value="none" /> - <option name="CLEAR_LOGCAT" value="false" /> - <option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" /> - <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> - <method /> - </configuration> - <configuration default="true" type="AndroidTestRunConfigurationType" factoryName="Android Tests"> - <module name="" /> - <option name="TESTING_TYPE" value="0" /> - <option name="INSTRUMENTATION_RUNNER_CLASS" value="" /> - <option name="METHOD_NAME" value="" /> - <option name="CLASS_NAME" value="" /> - <option name="PACKAGE_NAME" value="" /> - <option name="TARGET_SELECTION_MODE" value="EMULATOR" /> - <option name="USE_LAST_SELECTED_DEVICE" value="false" /> - <option name="PREFERRED_AVD" value="" /> - <option name="USE_COMMAND_LINE" value="true" /> - <option name="COMMAND_LINE" value="" /> - <option name="WIPE_USER_DATA" value="false" /> - <option name="DISABLE_BOOT_ANIMATION" value="false" /> - <option name="NETWORK_SPEED" value="full" /> - <option name="NETWORK_LATENCY" value="none" /> - <option name="CLEAR_LOGCAT" value="false" /> - <option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" /> - <option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" /> - <method /> - </configuration> - <configuration default="true" type="Applet" factoryName="Applet"> - <module /> - <option name="HTML_USED" value="false" /> - <option name="WIDTH" value="400" /> - <option name="HEIGHT" value="300" /> - <option name="POLICY_FILE" value="C:/IntelliJ-IDEA/bin/appletviewer.policy" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <method /> - </configuration> - <configuration default="true" type="Application" factoryName="Application"> - <extension name="coverage" enabled="false" merge="false" runner="idea" /> - <option name="MAIN_CLASS_NAME" /> - <option name="VM_PARAMETERS" /> - <option name="PROGRAM_PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="ENABLE_SWING_INSPECTOR" value="false" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <module name="" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="BashConfigurationType" factoryName="Bash"> - <option name="INTERPRETER_OPTIONS" value="" /> - <option name="INTERPRETER_PATH" value="" /> - <option name="WORKING_DIRECTORY" value="" /> - <option name="PARENT_ENVS" value="true" /> - <option name="SCRIPT_NAME" value="" /> - <option name="PARAMETERS" value="" /> - <module name="" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="BatchConfigurationType" factoryName="Batch"> - <option name="INTERPRETER_OPTIONS" value="" /> - <option name="WORKING_DIRECTORY" value="" /> - <option name="PARENT_ENVS" value="true" /> - <envs /> - <module name="" /> - <option name="SCRIPT_NAME" value="" /> - <option name="PARAMETERS" value="" /> - <method /> - </configuration> - <configuration default="true" type="CucumberJavaRunConfigurationType" factoryName="Cucumber java"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <option name="myFilePath" /> - <option name="GLUE" /> - <option name="myNameFilter" /> - <option name="myGeneratedName" /> - <option name="MAIN_CLASS_NAME" /> - <option name="VM_PARAMETERS" /> - <option name="PROGRAM_PARAMETERS" /> - <option name="WORKING_DIRECTORY" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="ENABLE_SWING_INSPECTOR" value="false" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <module name="" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="FlashRunConfigurationType" factoryName="Flash App"> - <option name="BCName" value="" /> - <option name="IOSSimulatorSdkPath" value="" /> - <option name="adlOptions" value="" /> - <option name="airProgramParameters" value="" /> - <option name="appDescriptorForEmulator" value="Android" /> - <option name="debugTransport" value="USB" /> - <option name="debuggerSdkRaw" value="BC SDK" /> - <option name="emulator" value="NexusOne" /> - <option name="emulatorAdlOptions" value="" /> - <option name="fastPackaging" value="true" /> - <option name="fullScreenHeight" value="0" /> - <option name="fullScreenWidth" value="0" /> - <option name="launchUrl" value="false" /> - <option name="launcherParameters"> - <LauncherParameters> - <option name="browser" value="a7bb68e0-33c0-4d6f-a81a-aac1fdb870c8" /> - <option name="launcherType" value="OSDefault" /> - <option name="newPlayerInstance" value="false" /> - <option name="playerPath" value="FlashPlayerDebugger.exe" /> - </LauncherParameters> - </option> - <option name="mobileRunTarget" value="Emulator" /> - <option name="moduleName" value="" /> - <option name="overriddenMainClass" value="" /> - <option name="overriddenOutputFileName" value="" /> - <option name="overrideMainClass" value="false" /> - <option name="runTrusted" value="true" /> - <option name="screenDpi" value="0" /> - <option name="screenHeight" value="0" /> - <option name="screenWidth" value="0" /> - <option name="url" value="http://" /> - <option name="usbDebugPort" value="7936" /> - <method /> - </configuration> - <configuration default="true" type="FlexUnitRunConfigurationType" factoryName="FlexUnit" appDescriptorForEmulator="Android" class_name="" emulatorAdlOptions="" method_name="" package_name="" scope="Class"> - <option name="BCName" value="" /> - <option name="launcherParameters"> - <LauncherParameters> - <option name="browser" value="a7bb68e0-33c0-4d6f-a81a-aac1fdb870c8" /> - <option name="launcherType" value="OSDefault" /> - <option name="newPlayerInstance" value="false" /> - <option name="playerPath" value="FlashPlayerDebugger.exe" /> - </LauncherParameters> - </option> - <option name="moduleName" value="" /> - <option name="trusted" value="true" /> - <method /> - </configuration> - <configuration default="true" type="GradleRunConfiguration" factoryName="Gradle"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list /> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <method /> - </configuration> - <configuration default="true" type="GrailsRunConfigurationType" factoryName="Grails"> - <module name="" /> - <setting name="vmparams" value="" /> - <setting name="cmdLine" value="run-app" /> - <setting name="depsClasspath" value="false" /> - <setting name="passParentEnv" value="true" /> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <setting name="launchBrowser" value="false" /> - <method /> - </configuration> - <configuration default="true" type="GroovyScriptRunConfiguration" factoryName="Groovy"> - <module name="" /> - <setting name="path" value="" /> - <setting name="vmparams" value="" /> - <setting name="params" value="" /> - <setting name="workDir" value="file://$PROJECT_DIR$" /> - <setting name="debug" value="false" /> - <method /> - </configuration> - <configuration default="true" type="JUnit" factoryName="JUnit"> - <extension name="coverage" enabled="false" merge="false" runner="idea" /> - <extension name="snapshooter" /> - <module name="" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="PACKAGE_NAME" /> - <option name="MAIN_CLASS_NAME" /> - <option name="METHOD_NAME" /> - <option name="TEST_OBJECT" /> - <option name="VM_PARAMETERS" /> - <option name="PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <option name="TEST_SEARCH_SCOPE"> - <value defaultName="wholeProject" /> - </option> - <envs /> - <patterns /> - <method /> - </configuration> - <configuration default="true" type="JarApplication" factoryName="JAR Application"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug"> - <method /> - </configuration> - <configuration default="true" type="Remote" factoryName="Remote"> - <option name="USE_SOCKET_TRANSPORT" value="true" /> - <option name="SERVER_MODE" value="false" /> - <option name="SHMEM_ADDRESS" value="javadebug" /> - <option name="HOST" value="localhost" /> - <option name="PORT" value="5005" /> - <method /> - </configuration> - <configuration default="true" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <module name="" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="TestNG" factoryName="TestNG"> - <extension name="coverage" enabled="false" merge="false" runner="idea" /> - <extension name="snapshooter" /> - <module name="" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="SUITE_NAME" /> - <option name="PACKAGE_NAME" /> - <option name="MAIN_CLASS_NAME" /> - <option name="METHOD_NAME" /> - <option name="GROUP_NAME" /> - <option name="TEST_OBJECT" value="CLASS" /> - <option name="VM_PARAMETERS" /> - <option name="PARAMETERS" /> - <option name="WORKING_DIRECTORY" /> - <option name="OUTPUT_DIRECTORY" /> - <option name="ANNOTATION_TYPE" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <option name="TEST_SEARCH_SCOPE"> - <value defaultName="wholeProject" /> - </option> - <option name="USE_DEFAULT_REPORTERS" value="false" /> - <option name="PROPERTIES_FILE" /> - <envs /> - <properties /> - <listeners /> - <method /> - </configuration> - <configuration default="true" type="WebLogic Instance" factoryName="Local" ALTERNATIVE_JRE_ENABLED="false"> - <option name="PORT" value="7001" /> - <deployment /> - <server-settings> - <option name="DOMAIN_PATH" value="" /> - <option name="SERVER_NAME" value="myserver" /> - <option name="DOMAIN_NAME" value="mydomain" /> - <option name="ADMIN_SERVER_HOST" value="localhost" /> - <option name="ADMIN_SERVER_PORT" value="7001" /> - <option name="TARGET_TYPE_NAME" value="TYPE_SERVER" /> - <option name="CLUSTER_NAME" value="mycluster" /> - <option name="CONNECT_TO_MANAGED_SERVER" value="false" /> - <option name="USERNAME" value="weblogic" /> - <option name="PASSWORD" value="weblogic" /> - </server-settings> - <predefined_log_file id="WEBLOGIC_DOMAIN_LOG_FILE" enabled="true" /> - <predefined_log_file id="WEBLOGIC_SERVER_LOG_FILE" enabled="true" /> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <method /> - </configuration> - <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js"> - <node-options /> - <gulpfile /> - <tasks /> - <arguments /> - <pass-parent-envs>true</pass-parent-envs> - <envs /> - <method /> - </configuration> - <configuration default="true" type="osgi.bnd.run" factoryName="Run Launcher"> - <method /> - </configuration> - <configuration default="true" type="osgi.bnd.run" factoryName="Test Launcher (JUnit)"> - <method /> - </configuration> - <configuration default="false" name="Mobibot" type="Application" factoryName="Application" show_console_on_std_out="true" show_console_on_std_err="true"> - <log_file path="$PROJECT_DIR$/mobibot.log" checked="true" skipped="true" show_all="false" alias="K:\java\mobibot\mobibot.log" /> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <option name="MAIN_CLASS_NAME" value="net.thauvin.erik.mobibot.Mobibot" /> - <option name="VM_PARAMETERS" value="" /> - <option name="PROGRAM_PARAMETERS" value="-d" /> - <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" value="" /> - <option name="ENABLE_SWING_INSPECTOR" value="false" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <module name="mobibot" /> - <envs /> - <RunnerSettings RunnerId="Debug"> - <option name="DEBUG_PORT" value="1063" /> - <option name="TRANSPORT" value="0" /> - <option name="LOCAL" value="false" /> - </RunnerSettings> - <RunnerSettings RunnerId="Run" /> - <ConfigurationWrapper RunnerId="Debug" /> - <ConfigurationWrapper RunnerId="Run" /> - <method /> - </configuration> - <configuration default="false" name="TwitterOAuth" type="Application" factoryName="Application"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <option name="MAIN_CLASS_NAME" value="net.thauvin.erik.mobibot.TwitterOAuth" /> - <option name="VM_PARAMETERS" value="" /> - <option name="PROGRAM_PARAMETERS" value="" /> - <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" value="" /> - <option name="ENABLE_SWING_INSPECTOR" value="false" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <module name="mobibot" /> - <envs /> - <RunnerSettings RunnerId="Run" /> - <ConfigurationWrapper RunnerId="Run" /> - <method /> - </configuration> - <list size="7"> - <item index="0" class="java.lang.String" itemvalue="Application.Mobibot" /> - <item index="1" class="java.lang.String" itemvalue="Application.TwitterOAuth" /> - <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> - <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> - <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> - <item index="5" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> - <item index="6" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> - </list> - <recent_temporary> - <list size="5"> - <item index="0" class="java.lang.String" itemvalue="Gradle.mobibot [release]" /> - <item index="1" class="java.lang.String" itemvalue="Gradle.mobibot [deploy]" /> - <item index="2" class="java.lang.String" itemvalue="Gradle.mobibot [copyToDeployLib]" /> - <item index="3" class="java.lang.String" itemvalue="Gradle.mobibot [clean]" /> - <item index="4" class="java.lang.String" itemvalue="Gradle.mobibot [distZip]" /> - </list> - </recent_temporary> - </component> - <component name="ScopeViewComponent"> - <subPane subId="Project"> - <PATH> - <PATH_ELEMENT USER_OBJECT="Root"> - <option name="myItemId" value="" /> - <option name="myItemType" value="" /> - </PATH_ELEMENT> - </PATH> - </subPane> - </component> - <component name="SelectInManager"> - <target name="J2EE View" /> - <target name="File Structure" /> - <target name="Project" /> - <target name="Packages" /> - <target name="Commander" /> - </component> - <component name="ShelveChangesManager" show_recycled="false" /> - <component name="SliceManager"> - <option name="analysisUIOptions"> - <AnalysisUIOptions /> - </option> - </component> - <component name="StarteamConfiguration"> - <option name="SERVER" value="" /> - <option name="PORT" value="49201" /> - <option name="USER" value="" /> - <option name="PASSWORD" value="" /> - <option name="PROJECT" value="" /> - <option name="VIEW" value="" /> - <option name="ALTERNATIVE_WORKING_PATH" value="" /> - <option name="LOCK_ON_CHECKOUT" value="false" /> - <option name="UNLOCK_ON_CHECKIN" value="false" /> - </component> - <component name="StructureViewFactory"> - <option name="AUTOSCROLL_FROM_SOURCE" value="true" /> - <option name="ACTIVE_ACTIONS" value="SHOW_INHERITED" /> - </component> - <component name="Struts Assistant"> - <option name="showInputs" value="true" /> - <option name="resources"> - <value> - <option name="strutsPath" /> - <option name="strutsHelp" /> - </value> - </option> - <option name="selectedTaglibs" /> - <option name="selectedTaglibs" /> - <option name="myStrutsValidationEnabled" value="true" /> - <option name="myTilesValidationEnabled" value="true" /> - <option name="myValidatorValidationEnabled" value="true" /> - <option name="myReportErrorsAsWarnings" value="true" /> - </component> - <component name="SvnChangesBrowserSettings"> - <option name="USE_AUTHOR_FIELD" value="true" /> - <option name="AUTHOR" value="" /> - <option name="LOCATION" value="" /> - <option name="USE_PROJECT_SETTINGS" value="true" /> - <option name="USE_ALTERNATE_LOCATION" value="false" /> - </component> - <component name="SvnConfiguration" myUseAcceleration="nothing"> - <configuration>C:\Users\erik\AppData\Roaming\Subversion</configuration> - <myIsUseDefaultProxy>true</myIsUseDefaultProxy> - <supportedVersion>125</supportedVersion> - </component> - <component name="TaskManager"> - <task active="true" id="Default" summary="Default task"> - <changelist id="944923a8-a8d5-4232-a77e-02473b958f59" name="Default" comment="" /> - <created>1267745358497</created> - <option name="number" value="Default" /> - <updated>1267745358497</updated> - <workItem from="1387264882357" duration="735000" /> - <workItem from="1387481199054" duration="134000" /> - <workItem from="1389769615762" duration="574000" /> - <workItem from="1395361548116" duration="1505000" /> - <workItem from="1395474041641" duration="571000" /> - <workItem from="1395474642318" duration="196000" /> - <workItem from="1395474982955" duration="74000" /> - <workItem from="1395475078661" duration="162000" /> - <workItem from="1395475260399" duration="425000" /> - <workItem from="1395475995661" duration="1042000" /> - <workItem from="1396073216211" duration="399000" /> - <workItem from="1396073634725" duration="1238000" /> - <workItem from="1397690762908" duration="38000" /> - <workItem from="1397809519571" duration="126000" /> - <workItem from="1397938098235" duration="4683000" /> - <workItem from="1397942922289" duration="23000" /> - <workItem from="1397942966521" duration="12216000" /> - <workItem from="1397955215138" duration="9309000" /> - <workItem from="1397967480730" duration="30000" /> - <workItem from="1397970368839" duration="10190000" /> - <workItem from="1397986767694" duration="4126000" /> - <workItem from="1397990901345" duration="117000" /> - <workItem from="1397991020096" duration="144000" /> - <workItem from="1397991303471" duration="10360000" /> - <workItem from="1398007108339" duration="1296000" /> - <workItem from="1398044136588" duration="22016000" /> - <workItem from="1398070053646" duration="28000" /> - <workItem from="1398081372292" duration="1335000" /> - <workItem from="1398121579254" duration="636000" /> - <workItem from="1398223700708" duration="491000" /> - <workItem from="1398226017077" duration="15000" /> - <workItem from="1398245977537" duration="266000" /> - <workItem from="1398344899170" duration="278000" /> - <workItem from="1398345200456" duration="821000" /> - <workItem from="1398388634845" duration="15419000" /> - <workItem from="1398409343569" duration="34452000" /> - <workItem from="1398467758159" duration="6999000" /> - <workItem from="1398488971240" duration="1675000" /> - <workItem from="1398490773855" duration="500000" /> - <workItem from="1398498222093" duration="188000" /> - <workItem from="1398499493094" duration="422000" /> - <workItem from="1398502801595" duration="19021000" /> - <workItem from="1398523374738" duration="361000" /> - <workItem from="1398523958438" duration="228000" /> - <workItem from="1398552748203" duration="41000" /> - <workItem from="1398554565444" duration="1591000" /> - <workItem from="1398629418369" duration="15129000" /> - <workItem from="1398667437285" duration="14777000" /> - <workItem from="1398725125314" duration="186000" /> - <workItem from="1398736740069" duration="354000" /> - <workItem from="1398741101527" duration="153000" /> - <workItem from="1398750996638" duration="2877000" /> - <workItem from="1398764128641" duration="5827000" /> - <workItem from="1398771310468" duration="7179000" /> - <workItem from="1398807853183" duration="5475000" /> - <workItem from="1398819448662" duration="4091000" /> - <workItem from="1398827780758" duration="5002000" /> - <workItem from="1398841856936" duration="2865000" /> - <workItem from="1398846143646" duration="974000" /> - <workItem from="1398908087195" duration="409000" /> - <workItem from="1398910788873" duration="1036000" /> - <workItem from="1398912049023" duration="768000" /> - <workItem from="1398914151479" duration="538000" /> - <workItem from="1398929482588" duration="5179000" /> - <workItem from="1399325436405" duration="6194000" /> - <workItem from="1399509126352" duration="26000" /> - <workItem from="1399592116678" duration="2183000" /> - <workItem from="1399618330807" duration="138000" /> - <workItem from="1400291768180" duration="1564000" /> - <workItem from="1400293761353" duration="2015000" /> - <workItem from="1400355710459" duration="1522000" /> - <workItem from="1400441036505" duration="68000" /> - <workItem from="1400460251513" duration="252000" /> - <workItem from="1400557767374" duration="78000" /> - <workItem from="1400738955715" duration="26000" /> - <workItem from="1400739268468" duration="73000" /> - <workItem from="1400740251061" duration="232000" /> - <workItem from="1400840140039" duration="712000" /> - <workItem from="1401258805459" duration="33000" /> - <workItem from="1401258976812" duration="149000" /> - <workItem from="1422520621496" duration="116000" /> - <workItem from="1422520824706" duration="103000" /> - <workItem from="1422522541534" duration="102000" /> - <workItem from="1422608665004" duration="181000" /> - <workItem from="1422608953671" duration="213000" /> - <workItem from="1422609958367" duration="95000" /> - <workItem from="1440871303975" duration="34000" /> - <workItem from="1440873148254" duration="1946000" /> - <workItem from="1440876283433" duration="1663000" /> - <workItem from="1441045012377" duration="2858000" /> - </task> - <servers /> - </component> - <component name="TimeTrackingManager"> - <option name="totallyTimeSpent" value="261891000" /> - </component> - <component name="TodoView" selected-index="0"> - <todo-panel id="selected-file"> - <are-packages-shown value="false" /> - <are-modules-shown value="false" /> - <flatten-packages value="false" /> - <is-autoscroll-to-source value="true" /> - </todo-panel> - <todo-panel id="all"> - <are-packages-shown value="true" /> - <are-modules-shown value="false" /> - <flatten-packages value="false" /> - <is-autoscroll-to-source value="true" /> - </todo-panel> - <todo-panel id="default-changelist"> - <are-packages-shown value="false" /> - <are-modules-shown value="false" /> - <flatten-packages value="false" /> - <is-autoscroll-to-source value="false" /> - </todo-panel> - </component> - <component name="ToolWindowManager"> - <frame x="1592" y="-8" width="1616" height="1204" extended-state="0" /> - <editor active="false" /> - <layout> - <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.31898972" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> - <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> - <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.43685687" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32692307" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.263125" sideWeight="0.4710071" order="0" side_tool="false" content_ui="combo" /> - <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.120625" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.18055555" sideWeight="0.5289929" order="1" side_tool="true" content_ui="tabs" /> - <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> - <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3988658" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> - <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> - <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> - <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> - <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="FLOATING" visible="true" weight="0.32969153" sideWeight="0.5" order="19" side_tool="true" content_ui="tabs" x="2155" y="309" width="663" height="473" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3228745" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> - <window_info id="BeanShell Box" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3293247" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="CVS File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="Properties File Structure" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.20918368" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3296845" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" /> - <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> - <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> - <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3295562" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.2750234" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - </layout> - <layout-to-restore> - <window_info id="Maven projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> - <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3996139" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="CDI" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="20" side_tool="false" content_ui="tabs" /> - <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.44135803" sideWeight="0.5" order="23" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="214" /> - <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="13" side_tool="false" content_ui="tabs" /> - <window_info id="BSFConsole" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="24" side_tool="false" content_ui="tabs" /> - <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3219659" sideWeight="0.5" order="22" side_tool="false" content_ui="tabs" /> - <window_info id="Palette " active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="Data Sources" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32698095" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> - <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" /> - <window_info id="BeanShell Box" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3293247" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="CVS File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="JetGradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.47380674" order="0" side_tool="false" content_ui="combo" /> - <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" /> - <window_info id="Properties File Structure" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.20918368" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" /> - <window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3296845" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Favorites" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32969153" sideWeight="0.5" order="10" side_tool="true" content_ui="tabs" /> - <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" /> - <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="21" side_tool="false" content_ui="tabs" /> - <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32914045" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" /> - <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="Code Outline" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" /> - <window_info id="Profile" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="19" side_tool="false" content_ui="tabs" /> - <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Duplicates" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - <window_info id="IntelliTail" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" /> - <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.21272494" sideWeight="0.52619326" order="1" side_tool="true" content_ui="tabs" /> - <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.09114249" sideWeight="0.60704356" order="2" side_tool="false" content_ui="tabs" /> - <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> - <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.39907408" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> - <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32704404" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" /> - <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32798395" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> - <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.1888574" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" x="22" y="277" width="980" height="213" /> - <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32867134" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> - <window_info id="Jalopy" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32962963" sideWeight="0.5" order="18" side_tool="false" content_ui="tabs" /> - <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" /> - <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3201133" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> - <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.12339332" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> - </layout-to-restore> - </component> - <component name="VCS.FileViewConfiguration"> - <option name="SELECTED_STATUSES" value="DEFAULT" /> - <option name="SELECTED_COLUMNS" value="DEFAULT" /> - <option name="SHOW_FILTERS" value="true" /> - <option name="CUSTOMIZE_VIEW" value="true" /> - <option name="SHOW_FILE_HISTORY_AS_TREE" value="true" /> - </component> - <component name="Vcs.Log.UiProperties"> - <option name="RECENTLY_FILTERED_USER_GROUPS"> - <collection /> - </option> - <option name="RECENTLY_FILTERED_BRANCH_GROUPS"> - <collection /> - </option> - </component> - <component name="VcsContentAnnotationSettings"> - <option name="myLimit" value="2678400000" /> - </component> - <component name="VcsManagerConfiguration"> - <option name="PERFORM_UPDATE_IN_BACKGROUND" value="false" /> - <option name="PERFORM_COMMIT_IN_BACKGROUND" value="false" /> - <option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="true" /> - <MESSAGE value="Updated to delicious 1.7" /> - <MESSAGE value="Added a baisc Twitter posting implementation." /> - <MESSAGE value="Added a basic Twitter posting implementation." /> - <MESSAGE value="Added Twitter4J attribution." /> - <option name="LAST_COMMIT_MESSAGE" value="Added Twitter4J attribution." /> - <option name="MAKE_NEW_CHANGELIST_ACTIVE" value="true" /> - </component> - <component name="VssConfiguration"> - <option name="CLIENT_PATH" value="" /> - <option name="SRCSAFEINI_PATH" value="" /> - <option name="USER_NAME" value="" /> - <option name="PWD" value="" /> - <CheckoutOptions> - <option name="COMMENT" value="" /> - <option name="DO_NOT_GET_LATEST_VERSION" value="false" /> - <option name="REPLACE_WRITABLE" value="false" /> - <option name="RECURSIVE" value="false" /> - </CheckoutOptions> - <CheckinOptions> - <option name="COMMENT" value="" /> - <option name="KEEP_CHECKED_OUT" value="false" /> - <option name="RECURSIVE" value="false" /> - </CheckinOptions> - <AddOptions> - <option name="STORE_ONLY_LATEST_VERSION" value="false" /> - <option name="CHECK_OUT_IMMEDIATELY" value="false" /> - </AddOptions> - <UndocheckoutOptions> - <option name="MAKE_WRITABLE" value="false" /> - <option name="REPLACE_LOCAL_COPY" value="0" /> - <option name="RECURSIVE" value="false" /> - </UndocheckoutOptions> - <GetOptions> - <option name="REPLACE_WRITABLE" value="0" /> - <option name="MAKE_WRITABLE" value="false" /> - <option name="ANSWER_NEGATIVELY" value="false" /> - <option name="ANSWER_POSITIVELY" value="false" /> - <option name="RECURSIVE" value="false" /> - <option name="VERSION" /> - </GetOptions> - </component> - <component name="XDebuggerManager"> - <breakpoint-manager> - <breakpoints> - <line-breakpoint enabled="true" type="java-line"> - <url>file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java</url> - <line>126</line> - <properties /> - <option name="timeStamp" value="2" /> - </line-breakpoint> - </breakpoints> - <option name="time" value="4" /> - </breakpoint-manager> - <watches-manager /> - </component> - <component name="antWorkspaceConfiguration"> - <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" /> - <option name="FILTER_TARGETS" value="true" /> - </component> - <component name="com.intellij.ide.util.scopeChooser.ScopeChooserConfigurable" proportions="" version="1"> - <option name="myLastEditedConfigurable" /> - </component> - <component name="com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectRootMasterDetailsConfigurable" proportions="0.28625" version="1"> - <option name="myPlainMode" value="false" /> - <option name="myLastEditedConfigurable" value="mobibot" /> - </component> - <component name="com.intellij.profile.ui.ErrorOptionsConfigurable" proportions="" version="1"> - <option name="myLastEditedConfigurable" /> - </component> - <component name="editorHistoryManager"> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="59" column="13" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="132" column="88" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="237" column="63" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/licenses/OstermillerUtil License.txt"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="9" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/README.txt"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-8.16"> - <caret line="12" column="4" selection-start-line="12" selection-start-column="4" selection-end-line="12" selection-end-column="4" /> - </state> - </provider> - </entry> - <entry file="file://K:/Gradle/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.5271318"> - <caret line="29" column="49" selection-start-line="29" selection-start-column="38" selection-end-line="29" selection-end-column="38" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/.gitignore"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.3242972"> - <caret line="19" column="12" selection-start-line="19" selection-start-column="12" selection-end-line="19" selection-end-column="12" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/licenses/License.txt"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.035789475"> - <caret line="2" column="23" selection-start-line="2" selection-start-column="23" selection-end-line="2" selection-end-column="23" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/website/simple.css"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.73846155"> - <caret line="43" column="83" selection-start-line="43" selection-start-column="1" selection-end-line="43" selection-end-column="1" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/website/index.html"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.3747495"> - <caret line="81" column="55" selection-start-line="0" selection-start-column="0" selection-end-line="114" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/User.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.33333334"> - <caret line="88" column="18" selection-start-line="88" selection-start-column="18" selection-end-line="88" selection-end-column="18" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/settings.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/com/sun/syndication/feed/synd/SyndEntry.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="184" column="9" selection-start-line="184" selection-start-column="9" selection-end-line="184" selection-end-column="9" /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/org/jibble/pircbot/PircBot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.3323077"> - <caret line="2424" column="18" selection-start-line="2424" selection-start-column="18" selection-end-line="2424" selection-end-column="18" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="16" column="7" selection-start-line="16" selection-start-column="7" selection-end-line="16" selection-end-column="7" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/War.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="38" column="36" selection-start-line="38" selection-start-column="36" selection-end-line="38" selection-end-column="36" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Utils.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="341" column="112" selection-start-line="341" selection-start-column="0" selection-end-line="341" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Twitter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="85" column="23" selection-start-line="85" selection-start-column="23" selection-end-line="85" selection-end-column="23" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="77" column="57" selection-start-line="77" selection-start-column="57" selection-end-line="77" selection-end-column="57" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Weather.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-10.75"> - <caret line="109" column="19" selection-start-line="109" selection-start-column="17" selection-end-line="109" selection-end-column="19" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/properties/fetcher.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/properties/log4j.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/log4j.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.38931298"> - <caret line="12" column="67" selection-start-line="12" selection-start-column="42" selection-end-line="12" selection-end-column="42" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessage.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="75" column="5" selection-start-line="75" selection-start-column="5" selection-end-line="75" selection-end-column="5" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="322" column="40" selection-start-line="322" selection-start-column="40" selection-end-line="322" selection-end-column="40" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryLink.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="33" column="7" selection-start-line="33" selection-start-column="0" selection-end-line="33" selection-end-column="7" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="55" column="74" selection-start-line="55" selection-start-column="45" selection-end-line="55" selection-end-column="74" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Dice.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="49" column="45" selection-start-line="49" selection-start-column="45" selection-end-line="49" selection-end-column="45" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/WorldTime.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="67" column="7" selection-start-line="67" selection-start-column="7" selection-end-line="67" selection-end-column="7" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="17" column="24" selection-start-line="17" selection-start-column="24" selection-end-line="17" selection-end-column="24" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="74" column="49" selection-start-line="74" selection-start-column="49" selection-end-line="74" selection-end-column="49" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Lookup.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="43" column="2" selection-start-line="43" selection-start-column="2" selection-end-line="43" selection-end-column="2" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/FeedReader.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="116" column="28" selection-start-line="116" selection-start-column="28" selection-end-line="116" selection-end-column="28" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/StockQuote.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="157" column="28" selection-start-line="157" selection-start-column="28" selection-end-line="157" selection-end-column="28" /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="117" column="55" selection-start-line="117" selection-start-column="55" selection-end-line="117" selection-end-column="55" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/EntryComment.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="0" column="2" selection-start-line="0" selection-start-column="2" selection-end-line="0" selection-end-column="2" /> - <folding> - <element signature="imports" expanded="false" /> - <element signature="e#2506#2507#0" expanded="false" /> - <element signature="e#2544#2545#0" expanded="false" /> - <element signature="e#2646#2647#0" expanded="false" /> - <element signature="e#2667#2668#0" expanded="false" /> - <element signature="e#2832#2833#0" expanded="false" /> - <element signature="e#2861#2862#0" expanded="false" /> - <element signature="e#3011#3012#0" expanded="false" /> - <element signature="e#3029#3030#0" expanded="false" /> - <element signature="e#3159#3160#0" expanded="false" /> - <element signature="e#3177#3178#0" expanded="false" /> - <element signature="e#3321#3322#0" expanded="false" /> - <element signature="e#3344#3345#0" expanded="false" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="148" column="12" selection-start-line="148" selection-start-column="12" selection-end-line="148" selection-end-column="12" /> - <folding> - <element signature="e#0#6680#0" expanded="false" /> - <element signature="e#2789#2790#0" expanded="false" /> - <element signature="e#2810#2811#0" expanded="false" /> - </folding> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/properties/mobibot.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="18" column="16" selection-start-line="18" selection-start-column="16" selection-end-line="18" selection-end-column="16" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="49" column="5" selection-start-line="49" selection-start-column="5" selection-end-line="49" selection-end-column="5" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Commands.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="172" column="56" selection-start-line="172" selection-start-column="56" selection-end-line="172" selection-end-column="56" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Mobibot.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="1255" column="40" selection-start-line="1255" selection-start-column="40" selection-end-line="1255" selection-end-column="40" /> - <folding> - <element signature="e#0#68037#0" expanded="false" /> - </folding> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/twitter4j/util/CharacterUtil.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.12295869"> - <caret line="22" column="19" selection-start-line="22" selection-start-column="19" selection-end-line="22" selection-end-column="19" /> - <folding /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/org/apache/oro/text/regex/Util.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="-4.8097982"> - <caret line="93" column="19" selection-start-line="93" selection-start-column="19" selection-end-line="93" selection-end-column="19" /> - <folding /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/del/icio/us/DeliciousUtils.class"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="7.4814816"> - <caret line="15" column="13" selection-start-line="15" selection-start-column="13" selection-end-line="15" selection-end-column="13" /> - <folding /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/org/apache/commons/codec/net/Utils.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="29" column="6" selection-start-line="29" selection-start-column="6" selection-end-line="29" selection-end-column="6" /> - <folding /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/org/apache/commons/codec/net/URLCodec.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="47" column="13" selection-start-line="47" selection-start-column="13" selection-end-line="47" selection-end-column="13" /> - <folding /> - </state> - </provider> - </entry> - <entry file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/org/jsoup/helper/StringUtil.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="8" column="19" selection-start-line="8" selection-start-column="19" selection-end-line="8" selection-end-column="19" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/build.gradle"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="28" column="53" selection-start-line="28" selection-start-column="53" selection-end-line="28" selection-end-column="53" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/src/main/java/net/thauvin/erik/mobibot/Joke.java"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.0"> - <caret line="103" column="44" selection-start-line="103" selection-start-column="44" selection-end-line="103" selection-end-column="44" /> - <folding /> - </state> - </provider> - </entry> - <entry file="file://$PROJECT_DIR$/buildnum.properties"> - <provider selected="true" editor-type-id="text-editor"> - <state vertical-scroll-proportion="0.05574913"> - <caret line="2" column="17" selection-start-line="2" selection-start-column="16" selection-end-line="2" selection-end-column="16" /> - <folding /> - </state> - </provider> - </entry> - </component> - <component name="ideajad"> - <property name="annotate" value="false" /> - <property name="annotateFully" value="false" /> - <property name="braces" value="false" /> - <property name="clear" value="false" /> - <property name="confirmNavigationTriggeredDecompile" value="true" /> - <property name="dead" value="true" /> - <property name="defaultInitializers" value="false" /> - <property name="dissassemblerOnly" value="false" /> - <property name="fieldsFirst" value="true" /> - <property name="fileExtension" value="java" /> - <property name="fullyQualifiedNames" value="false" /> - <property name="indentation" value="4" /> - <property name="intRadix" value="10" /> - <property name="lineNumbersAsComments" value="true" /> - <property name="longRadix" value="10" /> - <property name="maxStringLength" value="64" /> - <property name="nocast" value="false" /> - <property name="noclass" value="false" /> - <property name="nocode" value="false" /> - <property name="noconv" value="false" /> - <property name="noctor" value="false" /> - <property name="nodos" value="false" /> - <property name="nofd" value="false" /> - <property name="noinner" value="false" /> - <property name="nolvt" value="false" /> - <property name="nonlb" value="false" /> - <property name="outputDirectory" value="D:\projects\java\mobibot\src" /> - <property name="packFields" value="3" /> - <property name="packImports" value="7" /> - <property name="prefixNumericalClasses" value="_cls" /> - <property name="prefixNumericalFields" value="_fld" /> - <property name="prefixNumericalLocals" value="_lcl" /> - <property name="prefixNumericalMethods" value="_mth" /> - <property name="prefixNumericalParameters" value="_prm" /> - <property name="prefixUnusedExceptions" value="_ex" /> - <property name="readonly" value="false" /> - <property name="safe" value="false" /> - <property name="sort" value="false" /> - <property name="spaceAfterKeyword" value="false" /> - <property name="splitStringsAtNewline" value="false" /> - <property name="useTabs" value="false" /> - </component> - <component name="masterDetails"> - <states> - <state key="ArtifactsStructureConfigurable.UI"> - <settings> - <artifact-editor /> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - <state key="Copyright.UI"> - <settings> - <last-edited>Mobibot</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - <state key="FacetStructureConfigurable.UI"> - <settings> - <last-edited>No facets are configured</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - <state key="GlobalLibrariesConfigurable.UI"> - <settings> - <last-edited>commons-logging-api</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - <state key="JdkListConfigurable.UI"> - <settings> - <last-edited>1.8.x</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - <state key="ModuleStructureConfigurable.UI"> - <settings> - <last-edited>mobibot</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - <state key="ProjectLibrariesConfigurable.UI"> - <settings> - <last-edited>Gradle: utils-1.07.00</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - <state key="ScopeChooserConfigurable.UI"> - <settings> - <last-edited>Source</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - <order> - <scope name="Source" /> - </order> - </settings> - </state> - </states> - </component> - <component name="testng.defaultConfiguration"> - <outputDirectory /> - <properties /> - </component> -</project> \ No newline at end of file From 3e0bef10d75f2d99ea4cb5b8ecbecbdfdcaed77e Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Fri, 23 Oct 2015 17:58:36 -0700 Subject: [PATCH 020/842] Updated libraries dependencies. More code cleanup. --- build.gradle | 10 ++--- mobibot.iml | 12 +++--- mobibot.ipr | 41 ++++++++++--------- .../net/thauvin/erik/mobibot/Commands.java | 2 +- .../erik/mobibot/CurrencyConverter.java | 2 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 39 +++++++++--------- .../java/net/thauvin/erik/mobibot/Dice.java | 2 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 2 +- .../net/thauvin/erik/mobibot/EntryLink.java | 2 +- .../net/thauvin/erik/mobibot/FeedReader.java | 2 +- .../thauvin/erik/mobibot/GoogleSearch.java | 2 +- .../java/net/thauvin/erik/mobibot/Joke.java | 2 +- .../java/net/thauvin/erik/mobibot/Lookup.java | 5 +-- .../net/thauvin/erik/mobibot/Mobibot.java | 5 +-- .../net/thauvin/erik/mobibot/StockQuote.java | 2 +- .../net/thauvin/erik/mobibot/TellMessage.java | 1 - .../thauvin/erik/mobibot/TellMessagesMgr.java | 4 +- .../net/thauvin/erik/mobibot/Twitter.java | 2 +- .../java/net/thauvin/erik/mobibot/Utils.java | 2 +- .../java/net/thauvin/erik/mobibot/War.java | 2 +- .../net/thauvin/erik/mobibot/Weather.java | 2 +- .../net/thauvin/erik/mobibot/WorldTime.java | 2 +- 22 files changed, 71 insertions(+), 74 deletions(-) diff --git a/build.gradle b/build.gradle index 2d9aedc..6ec828b 100644 --- a/build.gradle +++ b/build.gradle @@ -22,8 +22,8 @@ dependencies { compile 'pircbot:pircbot:1.5.0' - compile 'commons-codec:commons-codec:1.9' - compile 'commons-logging:commons-logging:1.1.3' + compile 'commons-codec:commons-codec:1.10' + compile 'commons-logging:commons-logging:1.2' compile 'commons-net:commons-net:1.4.1' compile 'commons-cli:commons-cli:1.2' compile 'commons-httpclient:commons-httpclient:3.1' @@ -31,16 +31,16 @@ dependencies { compile 'oro:oro:2.0.8' compile 'org.jdom:jdom:1.1.3' - compile 'org.jsoup:jsoup:1.7.3' + compile 'org.jsoup:jsoup:1.8.3' compile 'rome:rome:1.0@jar' compile 'rome:rome-fetcher:1.0@jar' - compile 'org.json:json:20140107' + compile 'org.json:json:20150729' compile 'org.ostermiller:utils:1.07.00' compile 'net.sourceforge.jweather:jweather:0.3.0@jar' compile 'de.congrace:exp4j:0.3.11' - compile 'org.twitter4j:twitter4j-core:4.0.1' + compile 'org.twitter4j:twitter4j-core:4.0.4' compile 'net.sf.delicious-java:delicious:1.14' //compile fileTree(dir: 'lib', include: '*.jar') diff --git a/mobibot.iml b/mobibot.iml index a27a6a5..8fd6862 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -16,21 +16,21 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" exported="" name="Gradle: log4j:log4j:1.2.17" level="project" /> <orderEntry type="library" exported="" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-codec:commons-codec:1.9" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-logging:commons-logging:1.1.3" level="project" /> <orderEntry type="library" exported="" name="Gradle: commons-net:commons-net:1.4.1" level="project" /> <orderEntry type="library" exported="" name="Gradle: commons-cli:commons-cli:1.2" level="project" /> <orderEntry type="library" exported="" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> <orderEntry type="library" exported="" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" exported="" name="Gradle: org.jdom:jdom:1.1.3" level="project" /> - <orderEntry type="library" exported="" name="Gradle: org.jsoup:jsoup:1.7.3" level="project" /> <orderEntry type="library" exported="" name="Gradle: rome:rome:1.0" level="project" /> <orderEntry type="library" exported="" name="Gradle: rome:rome-fetcher:1.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: org.json:json:20140107" level="project" /> <orderEntry type="library" exported="" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" exported="" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> <orderEntry type="library" exported="" name="Gradle: de.congrace:exp4j:0.3.11" level="project" /> - <orderEntry type="library" exported="" name="Gradle: org.twitter4j:twitter4j-core:4.0.1" level="project" /> <orderEntry type="library" exported="" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.8.3" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20150729" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom:2.0.2" level="project" /> </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 17973d7..085dfb5 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -169,9 +169,10 @@ <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" /> <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" /> </inspection_tool> + <inspection_tool class="UnnecessarySemicolon" enabled="false" level="WARNING" enabled_by_default="false" /> </profile> - <option name="PROJECT_PROFILE" value="Project Default" /> - <option name="USE_PROJECT_PROFILE" value="true" /> + <option name="PROJECT_PROFILE" /> + <option name="USE_PROJECT_PROFILE" value="false" /> <version value="1.0" /> </component> <component name="JavadocGenerationManager"> @@ -401,13 +402,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/6c5459816530a1962ac18cd315cc775b1b384329/commons-cli-1.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-codec:commons-codec:1.9"> + <library name="Gradle: commons-codec:commons-codec:1.10"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/9ce04e34240f674bc72680f8b843b1457383161a/commons-codec-1.9.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/4b95f4897fa13f2cd904aee711aeafc0c5295cd8/commons-codec-1.10.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.9/3f15fff45d57656685abfee9e8302bf14580044c/commons-codec-1.9-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/11fb3d88ae7e3b757d70237064210ceb954a5a04/commons-codec-1.10-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> @@ -419,13 +420,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-logging:commons-logging:1.1.3"> + <library name="Gradle: commons-logging:commons-logging:1.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f/commons-logging-1.1.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.1.3/28bb0405fddaf04f15058fbfbe01fe2780d7d3b6/commons-logging-1.1.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/ecf26c7507d67782a3bbd148d170b31dfad001aa/commons-logging-1.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: commons-net:commons-net:1.4.1"> @@ -473,31 +474,31 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jdom:jdom:1.1.3"> + <library name="Gradle: org.jdom:jdom:2.0.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/d06c71e0df0ac4b94deb737718580ccce22d92e8/jdom-2.0.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/2c0614e33230394e42f5cfe55c20d5ae8949c108/jdom-2.0.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20140107"> + <library name="Gradle: org.json:json:20150729"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/d1ffca6e2482b002702c6a576166fd685e3370e3/json-20140107.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20150729/7ad48f520e9f94787fdbf2beb9916c76971bdcb0/json-20150729.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20140107/f53f91bc9c21fef71745450dea5200e423e38370/json-20140107-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20150729/d71e3dbedb977d57f76453af0bb43eed162adc30/json-20150729-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jsoup:jsoup:1.7.3"> + <library name="Gradle: org.jsoup:jsoup:1.8.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/92568d7167ce1bf9eb1fd815b022d5a2c113547a/jsoup-1.7.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.8.3/65fd012581ded67bc20945d85c32b4598c3a9cf1/jsoup-1.8.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.7.3/b783d347815471821faaa7ae7a4f0d2fec30966e/jsoup-1.7.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.8.3/17b6b6cb133d73e13e2e1f3550f90d4e23451b10/jsoup-1.8.3-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.ostermiller:utils:1.07.00"> @@ -509,13 +510,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.twitter4j:twitter4j-core:4.0.1"> + <library name="Gradle: org.twitter4j:twitter4j-core:4.0.4"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/37fd1d1eb3ed57916c9fdd1ea1f6c3afce778c7c/twitter4j-core-4.0.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/1f3c896c7b2f20c51103078ccf0bc2ea97ac012a/twitter4j-core-4.0.4.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.1/f1ba5ef4a200e94857bc49d41385a364f1b5571e/twitter4j-core-4.0.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/5fb9d7d5700c56e9a5202a95a2fd6c2159cb33b7/twitter4j-core-4.0.4-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: oro:oro:2.0.8"> diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 3c1a2bc..b784ea4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -40,7 +40,7 @@ package net.thauvin.erik.mobibot; * @created 2014-04-26 * @since 1.0 */ -public class Commands +class Commands { /** * The add (back)log command. diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 4996b63..9b61dc1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -51,7 +51,7 @@ import java.util.*; * @created Feb 11, 2004 * @since 1.0 */ -public class CurrencyConverter implements Runnable +class CurrencyConverter implements Runnable { /** * The exchange rates. diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 8dcce8b..0abbb68 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -42,8 +42,7 @@ import del.icio.us.Delicious; * @created Mar 5, 2005 * @since 1.0 */ -@SuppressWarnings("UnnecessaryBoxing") -public class DeliciousPoster +class DeliciousPoster { private final Delicious delicious; @@ -73,11 +72,11 @@ public class DeliciousPoster { public Object construct() { - return Boolean.valueOf(delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate())); + return delicious.addPost(entry.getLink(), + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate()); } }; @@ -109,7 +108,7 @@ public class DeliciousPoster { public Object construct() { - return Boolean.valueOf(delicious.deletePost(link)); + return delicious.deletePost(link); } }; @@ -132,21 +131,21 @@ public class DeliciousPoster { delicious.deletePost(oldUrl); - return Boolean.valueOf(delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate())); + return delicious.addPost(entry.getLink(), + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate()); } else { - return Boolean.valueOf(delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate(), - true, - true)); + return delicious.addPost(entry.getLink(), + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate(), + true, + true); } } }; diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/Dice.java index 7a324f4..43e9c57 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/Dice.java @@ -42,7 +42,7 @@ import java.util.Random; * @created 2014-04-28 * @since 1.0 */ -public class Dice +class Dice { /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index e28ae8c..371fce0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -50,7 +50,7 @@ import java.util.List; * @created 2014-04-28 * @since 1.0 */ -public class EntriesMgr +class EntriesMgr { /** * The name of the file containing the current entries. diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index ceee3f3..08344f6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -361,7 +361,7 @@ public class EntryLink implements Serializable * * @param tags The tags. */ - final void setTags(List<SyndCategoryImpl> tags) + private void setTags(List<SyndCategoryImpl> tags) { this.tags.addAll(tags); } diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index f12f88d..65bfda2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -50,7 +50,7 @@ import java.util.List; * @created Feb 1, 2004 * @since 1.0 */ -public class FeedReader implements Runnable +class FeedReader implements Runnable { /** * The maximum number of feed items to display. diff --git a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java index 4032836..f84e167 100644 --- a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java @@ -49,7 +49,7 @@ import java.net.URLEncoder; * @created Feb 7, 2004 * @since 1.0 */ -public class GoogleSearch implements Runnable +class GoogleSearch implements Runnable { /** * The tab indent (4 spaces). diff --git a/src/main/java/net/thauvin/erik/mobibot/Joke.java b/src/main/java/net/thauvin/erik/mobibot/Joke.java index 98579b6..1c013eb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/Joke.java @@ -48,7 +48,7 @@ import java.net.URLConnection; * @created 2014-04-20 * @since 1.0 */ -public class Joke implements Runnable +class Joke implements Runnable { /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/Lookup.java index d42c2a3..2a72f05 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/Lookup.java @@ -46,14 +46,13 @@ import java.net.UnknownHostException; * @created 2014-04-26 * @since 1.0 */ -public class Lookup +class Lookup { /** * The whois host. */ - @SuppressWarnings("WeakerAccess") - public static final String WHOIS_HOST = "whois.arin.net"; + private static final String WHOIS_HOST = "whois.arin.net"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index d9c3dc3..d830ab2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -773,8 +773,7 @@ public class Mobibot extends PircBot * * @return The file location. */ - @SuppressWarnings("WeakerAccess") - public String getSerializedObject() + private String getSerializedObject() { return serializedObject; } @@ -828,7 +827,7 @@ public class Mobibot extends PircBot * @param channel The channel. * @param action The action. */ - final void action(String channel, String action) + private void action(String channel, String action) { if (Utils.isValidString(channel) && Utils.isValidString(action)) { diff --git a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java index a2bd7fa..01d0de4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java @@ -46,7 +46,7 @@ import java.io.IOException; * @created Feb 7, 2004 * @since 1.0 */ -public class StockQuote implements Runnable +class StockQuote implements Runnable { /** * The Yahoo! stock quote URL. diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 48b3cbd..22e3346 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -46,7 +46,6 @@ import java.util.Date; */ public class TellMessage implements Serializable { - @SuppressWarnings({"UnusedDeclaration"}) private static final long serialVersionUID = 1L; private final String id; diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index d6b9b2b..a6fc52b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -48,7 +48,7 @@ import java.util.List; * @created 2014-04-26 * @since 1.0 */ -public class TellMessagesMgr +class TellMessagesMgr { /** * Disables the default constructor. @@ -132,7 +132,7 @@ public class TellMessagesMgr logger.getLogger().error("An error occurred loading the messages queue.", e); } - return (List<TellMessage>) new ArrayList(); + return new ArrayList<TellMessage>(); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/Twitter.java index 63aa811..f9b55e5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/Twitter.java @@ -44,7 +44,7 @@ import twitter4j.conf.ConfigurationBuilder; * @created Sept 10, 2008 * @since 1.0 */ -public class Twitter implements Runnable +class Twitter implements Runnable { /** * The Twitter access token. diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 8eb4862..937e669 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -50,7 +50,7 @@ import java.util.Calendar; * @created 2014-04-26 * @since 1.0 */ -public class Utils +class Utils { /** * The timestamp simple date format. diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/War.java index 7a2d6c4..0f8367c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/War.java @@ -42,7 +42,7 @@ import java.util.Random; * @created 2014-04-28 * @since 1.0 */ -public class War +class War { /** * The deck of card for the {@link net.thauvin.erik.mobibot.Commands#WAR_CMD war} command. diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/Weather.java index a232cee..0b40551 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/Weather.java @@ -47,7 +47,7 @@ import java.util.Date; * @created Feb 7, 2004 * @since 1.0 */ -public class Weather implements Runnable +class Weather implements Runnable { /** * The URL where the stations are listed. diff --git a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java index 228987c..2b2116e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java @@ -46,7 +46,7 @@ import java.util.TreeMap; * @created 2014-04-27 * @since 1.0 */ -public class WorldTime +class WorldTime { /** * The beats (Internet Time) keyword. From c5c5e94b7b1748bdbe3b60040b9e2a13459c85c0 Mon Sep 17 00:00:00 2001 From: erik <erik@thauvin.net> Date: Sat, 24 Oct 2015 01:42:20 -0700 Subject: [PATCH 021/842] Added reverse color for games. More code cleanup. --- mobibot.iml | 2 +- mobibot.ipr | 6 +- .../java/net/thauvin/erik/mobibot/Dice.java | 4 +- .../net/thauvin/erik/mobibot/Mobibot.java | 207 ++++++++++-------- .../java/net/thauvin/erik/mobibot/Utils.java | 27 +++ .../java/net/thauvin/erik/mobibot/War.java | 6 +- 6 files changed, 150 insertions(+), 102 deletions(-) diff --git a/mobibot.iml b/mobibot.iml index 8fd6862..df170be 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -31,6 +31,6 @@ <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.8.3" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20150729" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom:2.0.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom:1.1.3" level="project" /> </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 085dfb5..d8dbbc2 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -474,13 +474,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jdom:jdom:2.0.2"> + <library name="Gradle: org.jdom:jdom:1.1.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/d06c71e0df0ac4b94deb737718580ccce22d92e8/jdom-2.0.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/2c0614e33230394e42f5cfe55c20d5ae8949c108/jdom-2.0.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.json:json:20150729"> diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/Dice.java index 43e9c57..4031c04 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/Dice.java @@ -70,7 +70,7 @@ class Dice final int playerTotal = i + y; bot.send(bot.getChannel(), - sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils + sender + " rolled two dice: " + Utils.reverseColor(i) + " and " + Utils.reverseColor(y) + " for a total of " + Utils .bold(playerTotal)); i = r.nextInt(6) + 1; @@ -78,7 +78,7 @@ class Dice final int total = i + y; bot.action( - "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); + "rolled two dice: " + Utils.reverseColor(i) + " and " + Utils.reverseColor(y) + " for a total of " + Utils.bold(total)); if (playerTotal < total) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index d830ab2..799d71f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -88,11 +88,6 @@ public class Mobibot extends PircBot */ private static final int DEFAULT_TELL_MAX_SIZE = 50; - /** - * The double tab indent (8 spaces). - */ - private static final String DOUBLE_INDENT = " "; - /** * The info strings. */ @@ -131,6 +126,11 @@ public class Mobibot extends PircBot */ private static final String SER_EXT = ".ser"; + /** + * Shall we play a game? + */ + private static final String SHALL_WE_PLAY_A_GAME = "Shall we play a game?"; + /** * The start time. */ @@ -155,16 +155,16 @@ public class Mobibot extends PircBot + System.getProperty("java.vm.info") + ')' }; - /** - * Shall we play a game? - */ - private static final String shall_we_play_a_game = "Shall we play a game?"; - /** * The main channel. */ private final String channel; + /** + * The commands list. + */ + private final List<String> commandsList = new ArrayList<String>(); + /** * The currency converter. */ @@ -1050,6 +1050,32 @@ public class Mobibot extends PircBot } } + + /** + * Returns indented and bold help string. + * + * @param help The help string. + * + * @return The indented help string. + */ + private String helpIndent(String help) + { + return helpIndent(help, true); + } + + /** + * Returns indented help string. + * + * @param help The help string. + * @param isBold The bold flag. + * + * @return The indented help string. + */ + private String helpIndent(String help, boolean isBold) + { + return " " + (isBold ? Utils.bold(help) : help); + } + /** * Responds with the bot's help. * @@ -1063,111 +1089,109 @@ public class Mobibot extends PircBot if (lcTopic.endsWith(Commands.HELP_POSTING_KEYWORD)) { send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); - send(sender, DOUBLE_INDENT + Utils.bold("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); + send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + '1')); send(sender, "To add a title, use a its label and a pipe:"); - send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1:|This is the title")); + send(sender, helpIndent(Commands.LINK_CMD + "1:|This is the title")); send(sender, "To add a comment: "); - send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1:This is a comment")); + send(sender, helpIndent(Commands.LINK_CMD + "1:This is a comment")); send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + "1.1")); send(sender, "To edit a comment, use its label: "); - send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1.1:This is an edited comment")); + send(sender, helpIndent(Commands.LINK_CMD + "1.1:This is an edited comment")); send(sender, "To delete a comment, use its label and a minus sign: "); - send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1.1:-")); + send(sender, helpIndent(Commands.LINK_CMD + "1.1:-")); send(sender, "You can also view a posting by saying its label."); } else if (lcTopic.endsWith(Commands.HELP_TAGS_KEYWORD)) { send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:")); - send(sender, DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); + send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); } else if (lcTopic.endsWith(Commands.VIEW_CMD)) { send(sender, "To list or search the current URL posts:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); + send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); } else if (lcTopic.endsWith(channel.substring(1).toLowerCase())) { send(sender, "To list the last 5 posts from the channel's weblog:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + channel.substring(1))); + send(sender, helpIndent(getNick() + ": " + channel.substring(1))); } else if (lcTopic.endsWith(Commands.GOOGLE_CMD)) { send(sender, "To search Google:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.GOOGLE_CMD + " <query>")); + send(sender, helpIndent(getNick() + ": " + Commands.GOOGLE_CMD + " <query>")); } else if (lcTopic.endsWith(Commands.TWITTER_CMD) && isTwitterEnabled()) { send(sender, "To post to Twitter:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TWITTER_CMD + " <message>")); + send(sender, helpIndent(getNick() + ": " + Commands.TWITTER_CMD + " <message>")); } else if (lcTopic.endsWith(Commands.RECAP_CMD)) { send(sender, "To list the last 10 public channel messages:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.RECAP_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); } else if (lcTopic.endsWith(Commands.CALC_CMD)) { send(sender, "To solve a mathematical calculation:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CALC_CMD + " <calculation>")); + send(sender, helpIndent(getNick() + ": " + Commands.CALC_CMD + " <calculation>")); } else if (lcTopic.endsWith(Commands.LOOKUP_CMD)) { send(sender, "To perform a DNS lookup query:"); - send(sender, - DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.LOOKUP_CMD + " <ip address or hostname>")); + send(sender, helpIndent(getNick() + ": " + Commands.LOOKUP_CMD + " <ip address or hostname>")); } else if (lcTopic.endsWith(Commands.TIME_CMD)) { send(sender, "To display a country's current date/time:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TIME_CMD) + " [<country code>]"); + send(sender, helpIndent(getNick() + ": " + Commands.TIME_CMD) + " [<country code>]"); send(sender, "For a listing of the supported countries:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TIME_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.TIME_CMD)); } else if (lcTopic.endsWith(Commands.JOKE_CMD)) { send(sender, "To retrieve a random joke:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.JOKE_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.JOKE_CMD)); } else if (lcTopic.endsWith(Commands.STOCK_CMD)) { send(sender, "To retrieve a stock quote:"); - send(sender, - DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.STOCK_CMD + " <symbol[.country code]>")); + send(sender, helpIndent(getNick() + ": " + Commands.STOCK_CMD + " <symbol[.country code]>")); } else if (lcTopic.endsWith(Commands.DICE_CMD)) { send(sender, "To roll the dice:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.DICE_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.DICE_CMD)); } else if (lcTopic.endsWith(Commands.WAR_CMD)) { send(sender, "To play war:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.WAR_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.WAR_CMD)); } else if (lcTopic.endsWith(Commands.WEATHER_CMD)) { send(sender, "To display weather information:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.WEATHER_CMD + " <station id>")); + send(sender, helpIndent(getNick() + ": " + Commands.WEATHER_CMD + " <station id>")); send(sender, "For a listing of the ICAO station IDs, please visit: " + Weather.STATIONS_URL); } else if (lcTopic.endsWith(Commands.USERS_CMD)) { send(sender, "To list the users present on the channel:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.USERS_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); } else if (lcTopic.endsWith(Commands.INFO_CMD)) { send(sender, "To view information about the bot:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.INFO_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); } else if (lcTopic.endsWith(Commands.CYCLE_CMD)) { if (isOp(sender)) { send(sender, "To have the bot leave the channel and come back:"); - send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); } } else if (lcTopic.endsWith(Commands.ME_CMD)) @@ -1175,7 +1199,7 @@ public class Mobibot extends PircBot if (isOp(sender)) { send(sender, "To have the bot perform an action:"); - send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); } } else if (lcTopic.endsWith(Commands.SAY_CMD)) @@ -1183,7 +1207,7 @@ public class Mobibot extends PircBot if (isOp(sender)) { send(sender, "To have the bot say something on the channel:"); - send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); } } else if (lcTopic.endsWith(Commands.VERSION_CMD)) @@ -1191,7 +1215,7 @@ public class Mobibot extends PircBot if (isOp(sender)) { send(sender, "To view the version data (bot, java, etc.):"); - send(sender, DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); } } else if (lcTopic.endsWith(Commands.MSG_CMD)) @@ -1199,42 +1223,39 @@ public class Mobibot extends PircBot if (isOp(sender)) { send(sender, "To have the bot send a private message to someone:"); - send(sender, - DOUBLE_INDENT + Utils.bold("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); } } else if (lcTopic.startsWith(Commands.CURRENCY_CMD)) { send(sender, "To convert from one currency to another:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD + " [100 USD to EUR]")); + send(sender, helpIndent(getNick() + ": " + Commands.CURRENCY_CMD + " [100 USD to EUR]")); if (lcTopic.endsWith(Commands.CURRENCY_CMD)) { send(sender, "For a listing of currency rates:"); send(sender, - DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD) + ' ' - + Commands.CURRENCY_RATES_KEYWORD); + helpIndent(getNick() + ": " + Commands.CURRENCY_CMD) + ' ' + Commands.CURRENCY_RATES_KEYWORD); send(sender, "For a listing of supported currencies:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.CURRENCY_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.CURRENCY_CMD)); } } else if (lcTopic.startsWith(Commands.IGNORE_CMD)) { send(sender, "To check your ignore status:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.IGNORE_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); send(sender, "To toggle your ignore status:"); - send(sender, - DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); + send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); } else if (lcTopic.startsWith(Commands.TELL_CMD)) { send(sender, "To send a message to someone when they join the channel:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TELL_CMD + " <nick> <message>")); + send(sender, helpIndent(getNick() + ": " + Commands.TELL_CMD + " <nick> <message>")); send(sender, "To view queued and sent messages:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.VIEW_CMD)); + send(sender, helpIndent(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.VIEW_CMD)); send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days."); } @@ -1242,56 +1263,58 @@ public class Mobibot extends PircBot { send(sender, Utils.bold("Type a URL on " + channel + " to post it.")); send(sender, "For more information on specific command, type:"); - send(sender, DOUBLE_INDENT + Utils.bold(getNick() + ": " + Commands.HELP_CMD + " <command>")); + send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); send(sender, "The commands are:"); - final List<String> cmds = new ArrayList<String>(); - cmds.add(Commands.CALC_CMD); - cmds.add(Commands.CURRENCY_CMD); - cmds.add(Commands.DICE_CMD); - cmds.add(Commands.GOOGLE_CMD); - cmds.add(Commands.IGNORE_CMD); - cmds.add(Commands.INFO_CMD); - cmds.add(Commands.JOKE_CMD); - cmds.add(Commands.LOOKUP_CMD); - cmds.add(channel.substring(1)); - cmds.add(Commands.HELP_POSTING_KEYWORD); - cmds.add(Commands.RECAP_CMD); - cmds.add(Commands.STOCK_CMD); - cmds.add(Commands.HELP_TAGS_KEYWORD); - cmds.add(Commands.TIME_CMD); - cmds.add(Commands.USERS_CMD); - cmds.add(Commands.VIEW_CMD); - cmds.add(Commands.WAR_CMD); - cmds.add(Commands.WEATHER_CMD); - - if (isTellEnabled()) + if (commandsList.isEmpty()) { - cmds.add(Commands.TELL_CMD); - } + commandsList.add(Commands.CALC_CMD); + commandsList.add(Commands.CURRENCY_CMD); + commandsList.add(Commands.DICE_CMD); + commandsList.add(Commands.GOOGLE_CMD); + commandsList.add(Commands.IGNORE_CMD); + commandsList.add(Commands.INFO_CMD); + commandsList.add(Commands.JOKE_CMD); + commandsList.add(Commands.LOOKUP_CMD); + commandsList.add(channel.substring(1)); + commandsList.add(Commands.HELP_POSTING_KEYWORD); + commandsList.add(Commands.RECAP_CMD); + commandsList.add(Commands.STOCK_CMD); + commandsList.add(Commands.HELP_TAGS_KEYWORD); + commandsList.add(Commands.TIME_CMD); + commandsList.add(Commands.USERS_CMD); + commandsList.add(Commands.VIEW_CMD); + commandsList.add(Commands.WAR_CMD); + commandsList.add(Commands.WEATHER_CMD); - if (isTwitterEnabled()) - { - cmds.add(Commands.TWITTER_CMD); - } + if (isTellEnabled()) + { + commandsList.add(Commands.TELL_CMD); + } - Collections.sort(cmds); + if (isTwitterEnabled()) + { + commandsList.add(Commands.TWITTER_CMD); + } + + Collections.sort(commandsList); + } final StringBuilder sb = new StringBuilder(0); - for (int i = 0, cmdCount = 1; i < cmds.size(); i++, cmdCount++) + for (int i = 0, cmdCount = 1; i < commandsList.size(); i++, cmdCount++) { if (sb.length() > 0) { sb.append(" "); } - sb.append(cmds.get(i)); + sb.append(commandsList.get(i)); // 5 commands per line or last command - if (sb.length() > 0 && (cmdCount == 5 || i == (cmds.size() - 1))) + if (sb.length() > 0 && (cmdCount == 5 || i == (commandsList.size() - 1))) { - send(sender, DOUBLE_INDENT + Utils.bold(sb.toString())); + send(sender, helpIndent(sb.toString())); sb.setLength(0); cmdCount = 0; @@ -1302,9 +1325,8 @@ public class Mobibot extends PircBot { send(sender, "The op commands are:"); send(sender, - DOUBLE_INDENT + Utils - .bold(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); + helpIndent(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); } } } @@ -1667,9 +1689,7 @@ public class Mobibot extends PircBot if (NO_TITLE.equals(entry.getTitle())) { send(sender, "Please specify a title, by typing:", true); - send(sender, - DOUBLE_INDENT + Utils.bold(Commands.LINK_CMD + (index + 1) + ":|This is the title"), - true); + send(sender, helpIndent(Commands.LINK_CMD + (index + 1) + ":|This is the title"), true); } } else @@ -1749,14 +1769,14 @@ public class Mobibot extends PircBot // mobibot: dice else if (cmd.equals(Commands.DICE_CMD)) { - send(channel, shall_we_play_a_game); + send(channel, SHALL_WE_PLAY_A_GAME); Dice.roll(this, sender); } // mobibot: war else if (cmd.equals(Commands.WAR_CMD)) { - send(channel, shall_we_play_a_game); + send(channel, SHALL_WE_PLAY_A_GAME); War.play(this, sender); } @@ -2507,7 +2527,7 @@ public class Mobibot extends PircBot true); } - send(sender, DOUBLE_INDENT + message.getMessage(), true); + send(sender, helpIndent(message.getMessage(), false), true); } } @@ -2519,9 +2539,8 @@ public class Mobibot extends PircBot { send(sender, "To delete one or all delivered messages:"); send(sender, - DOUBLE_INDENT + Utils - .bold(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id|" - + Commands.TELL_ALL_CMD + '>')); + helpIndent(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id|" + + Commands.TELL_ALL_CMD + '>')); send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days."); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 937e669..c43b6cd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -90,6 +90,20 @@ class Utils return Colors.BOLD + i + Colors.BOLD; } + /** + * Makes the given int reverse color. + * + * @param i The int. + * + * @return The reverse color string. + */ + public static String reverseColor(int i) + { + return Colors.REVERSE + i + Colors.REVERSE; + } + + + /** * Builds an entry's comment for display on the channel. * @@ -168,6 +182,19 @@ class Utils return Colors.BOLD + s + Colors.BOLD; } + /** + * Makes the given string reverse color. + * + * @param s The string. + * + * @return The reverse color string. + */ + public static String reverseColor(String s) + { + return Colors.REVERSE + s + Colors.REVERSE; + } + + /** * Build an entry's tags/categories for display on the channel. * diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/War.java index 0f8367c..7313720 100644 --- a/src/main/java/net/thauvin/erik/mobibot/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/War.java @@ -85,13 +85,15 @@ class War y = r.nextInt(WAR_DECK.length); bot.send(bot.getChannel(), - sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + sender + " drew the " + Utils.reverseColor(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + bot.action("drew the " + Utils.reverseColor(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); if (i != y) { break; } + + bot.send(bot.getChannel(), "This means " + Utils.bold("WAR") + '!'); } if (i < y) From edf97b3e1c10bae7717aa6fbf994548d18e13662 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 26 Oct 2015 14:40:41 -0700 Subject: [PATCH 022/842] Updated to exp4j v0.4.5. --- build.gradle | 2 +- buildnum.properties | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 52266 -> 53638 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 6 +----- mobibot.iml | 2 +- mobibot.ipr | 18 +++++++++--------- .../net/thauvin/erik/mobibot/Mobibot.java | 11 +++++------ .../net/thauvin/erik/mobibot/ReleaseInfo.java | 8 ++++---- 9 files changed, 24 insertions(+), 29 deletions(-) diff --git a/build.gradle b/build.gradle index 6ec828b..c08f2ba 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ dependencies { compile 'org.ostermiller:utils:1.07.00' compile 'net.sourceforge.jweather:jweather:0.3.0@jar' - compile 'de.congrace:exp4j:0.3.11' + compile 'net.objecthunter:exp4j:0.4.5' compile 'org.twitter4j:twitter4j-core:4.0.4' compile 'net.sf.delicious-java:delicious:1.14' diff --git a/buildnum.properties b/buildnum.properties index ec85d8a..268e39c 100644 --- a/buildnum.properties +++ b/buildnum.properties @@ -1,3 +1,3 @@ #ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Mon Aug 31 12:19:55 PDT 2015 +#Mon Oct 26 14:39:18 PDT 2015 build.num.last=0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b5166dad4d90021f6a0b45268c0755719f1d5cd4..e8c6bf7bb47dff6b81c2cf7a349eb7e912c9fbe2 100644 GIT binary patch delta 12509 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 CY2(!Z delta 11130 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 W$vnO&{vix(0l%K+gokbYgZ>|ytsjp7 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0196fc2..580ff2f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Aug 29 12:01:59 PDT 2015 +#Sat Oct 24 14:24:39 PDT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip diff --git a/gradlew b/gradlew index 91a7e26..97fac78 100644 --- a/gradlew +++ b/gradlew @@ -42,11 +42,6 @@ case "`uname`" in ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -114,6 +109,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/mobibot.iml b/mobibot.iml index df170be..de5056b 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -24,7 +24,6 @@ <orderEntry type="library" exported="" name="Gradle: rome:rome-fetcher:1.0" level="project" /> <orderEntry type="library" exported="" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" exported="" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: de.congrace:exp4j:0.3.11" level="project" /> <orderEntry type="library" exported="" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> @@ -32,5 +31,6 @@ <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.8.3" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20150729" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom:1.1.3" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.5" level="project" /> </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index d8dbbc2..f85427e 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -438,15 +438,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/4c85b39e7f03471338bf7d36558eefe1e463e3de/commons-net-1.4.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: de.congrace:exp4j:0.3.11"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/150e7b4a77af47b03a1b65be7a01bd663ea64420/exp4j-0.3.11.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/de.congrace/exp4j/0.3.11/61abb5ef010830519e47ac56faade402807b245b/exp4j-0.3.11-sources.jar!/" /> - </SOURCES> - </library> <library name="Gradle: log4j:log4j:1.2.17"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar!/" /> @@ -456,6 +447,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: net.objecthunter:exp4j:0.4.5"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.5/c6876761bb3816c603350f39b22023ee236430be/exp4j-0.4.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.5/977535d7083d551378ff8ba8e5d2c9ddf5f0c455/exp4j-0.4.5-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: net.sf.delicious-java:delicious:1.14"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 799d71f..5137b08 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -36,8 +36,8 @@ package net.thauvin.erik.mobibot; import com.sun.syndication.fetcher.impl.FeedFetcherCache; import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache; import com.sun.syndication.io.FeedException; -import de.congrace.exp4j.Calculable; -import de.congrace.exp4j.ExpressionBuilder; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; import org.apache.commons.cli.*; import org.apache.commons.logging.impl.Log4JLogger; import org.apache.log4j.Level; @@ -850,8 +850,8 @@ public class Mobibot extends PircBot try { - final Calculable calc = new ExpressionBuilder(args).build(); - send(channel, args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.calculate())); + final Expression calc = new ExpressionBuilder(args).build(); + send(channel, args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate())); } catch (Exception e) { @@ -1050,7 +1050,6 @@ public class Mobibot extends PircBot } } - /** * Returns indented and bold help string. * @@ -1060,7 +1059,7 @@ public class Mobibot extends PircBot */ private String helpIndent(String help) { - return helpIndent(help, true); + return helpIndent(help, true); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 1edd473..6179c2b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Mon Aug 31 12:19:55 PDT 2015 */ +/* Creation date Mon Oct 26 14:39:18 PDT 2015 */ package net.thauvin.erik.mobibot; import java.util.Date; @@ -20,11 +20,11 @@ public class ReleaseInfo { } - /** buildDate (set during build process to 1441048795735L). */ - private static final Date buildDate = new Date(1441048795735L); + /** buildDate (set during build process to 1445895558361L). */ + private static final Date buildDate = new Date(1445895558361L); /** - * Get buildDate (set during build process to Mon Aug 31 12:19:55 PDT 2015). + * Get buildDate (set during build process to Mon Oct 26 14:39:18 PDT 2015). * @return Date buildDate */ public static Date getBuildDate() { return buildDate; } From 30f432390e5861a597f6a80dec0457bd118cc545 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 26 Oct 2015 23:17:20 -0700 Subject: [PATCH 023/842] Updated website for 0.6.0 release. --- website/index.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/index.html b/website/index.html index fff8736..58ca775 100644 --- a/website/index.html +++ b/website/index.html @@ -63,11 +63,8 @@ <p>Other features include:</p> <ul> <li>Displaying the latest entries on Mobitopia</li> - <li>Sending messages on join - <div><code>mobibot: tell Nickname Welcome back!</code></div> - </li> <li>Performing calculations - <div><code>mobibot: calc (floor(sqrt(3))+3.14)*3^2</code></div> + <div><code>mobibot: calc (floor(sqrt(3)) + 3.14) * 3^2</code></div> </li> <li>Converting between currencies <div><code>mobibot: currency 17.54 USD to EUR</code></div> @@ -87,9 +84,12 @@ <li>Displaying the time in various time zones <div><code>mobibot: time UK</code></div> </li> + <li>Sending messages to people on join/activity: + <div><code>mobibot: tell nickname Give me a call when you see this.</code></div> + </li> <li>Recapping public channel messages</li> <li>Listing the users on the channel</li> - <li>Random quotes from <a href="http://iheartquotes.com">I Heart Quotes</a></li> + <li>Random jokes from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a></li> <li>Rolling dice and playing war</li> <li>Posting to <a href="http://twitter.com/mobitopia">Twitter</a></li> </ul> @@ -114,4 +114,4 @@ </p> </div> </body> -</html> +</html> \ No newline at end of file From cdb50c576d3def0969de875bca6580319fc65d4e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 9 Nov 2015 00:39:11 -0800 Subject: [PATCH 024/842] Fixed typos. --- mobibot.ipr | 9 ++++++++- .../java/net/thauvin/erik/mobibot/DeliciousPoster.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntryLink.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 6 +++--- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/mobibot.ipr b/mobibot.ipr index f85427e..30be848 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -103,7 +103,9 @@ <option name="ADDITIONAL_OPTIONS_STRING" value="" /> <option name="MAXIMUM_HEAP_SIZE" value="128" /> </component> - <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" /> + <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"> + <file url="PROJECT" charset="UTF-8" /> + </component> <component name="EntryPointsManager"> <entry_points version="2.0" /> </component> @@ -127,6 +129,11 @@ <option value="$PROJECT_DIR$" /> </set> </option> + <option name="myModules"> + <set> + <option value="$PROJECT_DIR$" /> + </set> + </option> </GradleProjectSettings> </option> </component> diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 0abbb68..b5bd726 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -51,7 +51,7 @@ class DeliciousPoster /** * Creates a new {@link DeliciousPoster} instance. * - * @param username The del.icio.us username. + * @param username The del.icio.us user name. * @param password The del.icio.us password. * @param ircServer The IRC server. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 08344f6..bcfe220 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -306,7 +306,7 @@ public class EntryLink implements Serializable } /** - * Return's the comment's author login. + * Returns the comment's author login. * * @return The login; */ @@ -316,7 +316,7 @@ public class EntryLink implements Serializable } /** - * Set the comment's author login. + * Sets the comment's author login. * * @param login The new login. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 5137b08..8fe9503 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -677,7 +677,7 @@ public class Mobibot extends PircBot /** * Sets the del.icio.us authentication. * - * @param username The del.icio.us username. + * @param username The del.icio.us user name. * @param password The del.icio.us password. */ private void setDeliciousAuth(String username, String password) @@ -733,7 +733,7 @@ public class Mobibot extends PircBot * Set the {@link Commands#TELL_CMD} parameters * * @param tellMaxDays The max number of days to hold messages for. - * @param tellMaxSize The maximmm number of messages to hold + * @param tellMaxSize The maximum number of messages to hold */ private void setTell(int tellMaxDays, int tellMaxSize) { @@ -1261,7 +1261,7 @@ public class Mobibot extends PircBot else { send(sender, Utils.bold("Type a URL on " + channel + " to post it.")); - send(sender, "For more information on specific command, type:"); + send(sender, "For more information on a specific command, type:"); send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); send(sender, "The commands are:"); From ef7dd954a55023e2fd3dbddbd63da2a480c0b2e9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 7 Dec 2015 01:24:56 -0800 Subject: [PATCH 025/842] PMD/FindBugs optimization. --- README.txt | 2 +- .../erik/mobibot/CurrencyConverter.java | 13 +-- .../java/net/thauvin/erik/mobibot/Utils.java | 93 ++++++++++--------- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/README.txt b/README.txt index 333fbf7..1d111a1 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ Some very basic instructions: - { clone with git or download the ZIP} + { clone with git or download the ZIP } git clone git://github.com/ethauvin/mobibot.git cd mobibot diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 9b61dc1..b138c95 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -88,7 +88,7 @@ class CurrencyConverter implements Runnable * * @param bot The bot's instance. */ - public CurrencyConverter(Mobibot bot) + public CurrencyConverter(final Mobibot bot) { this.bot = bot; } @@ -186,23 +186,18 @@ class CurrencyConverter implements Runnable { bot.send(sender, "Last Update: " + pubDate); - final Iterator<String> it = EXCHANGE_RATES.keySet().iterator(); - String rate; - final StringBuilder buff = new StringBuilder(0); - while (it.hasNext()) + for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { - rate = it.next(); if (buff.length() > 0) { buff.append(", "); } - buff.append(rate).append(": ").append(EXCHANGE_RATES.get(rate)); + buff.append(rate.getKey()).append(": ").append(rate.getValue()); } bot.send(sender, buff.toString()); - } } } @@ -221,7 +216,7 @@ class CurrencyConverter implements Runnable * @param query The currency query. */ - public synchronized void setQuery(String sender, String query) + public synchronized void setQuery(final String sender, final String query) { this.query = query; this.sender = sender; diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index c43b6cd..75734d0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -50,7 +50,7 @@ import java.util.Calendar; * @created 2014-04-26 * @since 1.0 */ -class Utils +final class Utils { /** * The timestamp simple date format. @@ -85,25 +85,11 @@ class Utils * * @return The bold string. */ - public static String bold(int i) + public static String bold(final int i) { return Colors.BOLD + i + Colors.BOLD; } - /** - * Makes the given int reverse color. - * - * @param i The int. - * - * @return The reverse color string. - */ - public static String reverseColor(int i) - { - return Colors.REVERSE + i + Colors.REVERSE; - } - - - /** * Builds an entry's comment for display on the channel. * @@ -113,7 +99,7 @@ class Utils * * @return The entry's comment. */ - static String buildComment(int entryIndex, int commentIndex, EntryComment comment) + static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) { return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " + comment.getComment()); @@ -129,7 +115,7 @@ class Utils * * @see #buildLink(int, net.thauvin.erik.mobibot.EntryLink, boolean) */ - static String buildLink(int index, EntryLink entry) + static String buildLink(final int index, final EntryLink entry) { return buildLink(index, entry, false); } @@ -143,7 +129,7 @@ class Utils * * @return The entry's link. */ - static String buildLink(int index, EntryLink entry, boolean isView) + static String buildLink(final int index, final EntryLink entry, final boolean isView) { final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); @@ -177,24 +163,11 @@ class Utils * * @return The bold string. */ - public static String bold(String s) + public static String bold(final String s) { return Colors.BOLD + s + Colors.BOLD; } - /** - * Makes the given string reverse color. - * - * @param s The string. - * - * @return The reverse color string. - */ - public static String reverseColor(String s) - { - return Colors.REVERSE + s + Colors.REVERSE; - } - - /** * Build an entry's tags/categories for display on the channel. * @@ -203,7 +176,7 @@ class Utils * * @return The entry's tags. */ - static String buildTags(int entryIndex, EntryLink entry) + static String buildTags(final int entryIndex, final EntryLink entry) { return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); } @@ -217,7 +190,7 @@ class Utils * @throws java.io.IOException If the file could not be copied. */ @SuppressWarnings("UnusedDeclaration") - public static void copyFile(File in, File out) + public static void copyFile(final File in, final File out) throws IOException { FileChannel inChannel = null; @@ -281,7 +254,7 @@ class Utils * * @return The location ending with a slash. */ - public static String ensureDir(String location, boolean isUrl) + public static String ensureDir(final String location, final boolean isUrl) { if (isUrl) { @@ -315,7 +288,7 @@ class Utils * * @return The port or default value if invalid. */ - public static int getIntProperty(String property, int def) + public static int getIntProperty(final String property, final int def) { int prop; @@ -361,11 +334,11 @@ class Utils if (beats < 10) { - return ("@00" + String.valueOf(beats)); + return ("@00" + beats); } else if (beats < 100) { - return ("@0" + String.valueOf(beats)); + return ("@0" + beats); } return ('@' + String.valueOf(beats)); @@ -378,9 +351,45 @@ class Utils * * @return true if the string is non-empty and not null, false otherwise. */ - public static boolean isValidString(String s) + public static boolean isValidString(final CharSequence s) { - return (s != null) && (s.trim().length() > 0); + final int len; + if (s == null || (len = s.length()) == 0) + { + return false; + } + for (int i = 0; i < len; i++) + { + if (!Character.isWhitespace(s.charAt(i))) + { + return true; + } + } + return false; + } + + /** + * Makes the given int reverse color. + * + * @param i The int. + * + * @return The reverse color string. + */ + public static String reverseColor(final int i) + { + return Colors.REVERSE + i + Colors.REVERSE; + } + + /** + * Makes the given string reverse color. + * + * @param s The string. + * + * @return The reverse color string. + */ + public static String reverseColor(final String s) + { + return Colors.REVERSE + s + Colors.REVERSE; } /** @@ -400,7 +409,7 @@ class Utils * * @return The unescaped string. */ - public static String unescapeXml(String str) + public static String unescapeXml(final String str) { String s = str.replaceAll("&", "&"); s = s.replaceAll("<", "<"); From b71c488977ccd4ad296f07bcdaf4a45c9dd4c63f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 7 Dec 2015 20:05:08 -0800 Subject: [PATCH 026/842] Improved source code documenation. --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 75734d0..9a0977b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -345,11 +345,11 @@ final class Utils } /** - * Returns true if the given string is valid. + * Returns <code>true</code> if the given string is <em>not</em> blank or null. * - * @param s The string to validate. + * @param s The string to check. * - * @return true if the string is non-empty and not null, false otherwise. + * @return <code>true</code> if the string is valid, <code>false</code> otherwise. */ public static boolean isValidString(final CharSequence s) { @@ -420,4 +420,4 @@ final class Utils return s; } -} \ No newline at end of file +} From 9cf5ee8fbc6e21ed9475a9ed9a725a82b720ee44 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Jan 2016 16:59:36 -0800 Subject: [PATCH 027/842] Added semantic version annotations. Added and updated license. --- LICENSE.TXT | 27 ++ ant/jreleaseinfo-1.3.0.jar | Bin 23925 -> 0 bytes build.gradle | 149 +++++----- buildnum.properties | 3 - gradle/wrapper/gradle-wrapper.jar | Bin 53638 -> 53636 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 4 +- mobibot.iml | 213 +++++++++++++- mobibot.ipr | 261 +++++++++++++++++- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 128 +++++++++ .../net/thauvin/erik/mobibot/Commands.java | 44 ++- .../erik/mobibot/CurrencyConverter.java | 42 ++- .../thauvin/erik/mobibot/DeliciousPoster.java | 48 ++-- .../java/net/thauvin/erik/mobibot/Dice.java | 46 ++- .../net/thauvin/erik/mobibot/EntriesMgr.java | 50 ++-- .../thauvin/erik/mobibot/EntryComment.java | 48 ++-- .../net/thauvin/erik/mobibot/EntryLink.java | 68 +++-- .../net/thauvin/erik/mobibot/FeedReader.java | 44 ++- .../thauvin/erik/mobibot/GoogleSearch.java | 44 ++- .../java/net/thauvin/erik/mobibot/Joke.java | 44 ++- .../java/net/thauvin/erik/mobibot/Lookup.java | 50 ++-- .../net/thauvin/erik/mobibot/Mobibot.java | 150 +++++----- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 59 ---- .../net/thauvin/erik/mobibot/StockQuote.java | 44 ++- .../net/thauvin/erik/mobibot/TellMessage.java | 48 ++-- .../thauvin/erik/mobibot/TellMessagesMgr.java | 50 ++-- .../net/thauvin/erik/mobibot/Twitter.java | 46 ++- .../java/net/thauvin/erik/mobibot/Utils.java | 42 ++- .../java/net/thauvin/erik/mobibot/War.java | 46 ++- .../net/thauvin/erik/mobibot/Weather.java | 44 ++- .../net/thauvin/erik/mobibot/WorldTime.java | 44 ++- version.properties | 8 + 32 files changed, 1227 insertions(+), 671 deletions(-) create mode 100644 LICENSE.TXT delete mode 100644 ant/jreleaseinfo-1.3.0.jar delete mode 100644 buildnum.properties create mode 100644 src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java delete mode 100644 src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java create mode 100644 version.properties diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 0000000..4d3d187 --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,27 @@ +Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of this project nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/ant/jreleaseinfo-1.3.0.jar b/ant/jreleaseinfo-1.3.0.jar deleted file mode 100644 index 0c00c5f78cf6bdde3832614f6ea4584017fe5fe6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23925 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 diff --git a/build.gradle b/build.gradle index c08f2ba..484eff9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,117 +1,136 @@ +plugins { + id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" +} apply plugin: 'java' apply plugin: 'idea' apply plugin: 'application' defaultTasks 'deploy' -version = '0.6' - def packageName = 'net.thauvin.erik.mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames +def getVersion(isIncrement = false) +{ + def propsFile = 'version.properties' + def majorKey = 'version.major' + def minorKey = 'version.minor' + def patchKey = 'version.patch' + def metaKey = 'version.buildmeta' + def preKey = 'version.prerelease' + if (isIncrement) + { + ant.propertyfile(file: propsFile) { + entry(key: patchKey, + type: 'int', + default: '-1', + operation: '+') + } + } + def p = new Properties() + file(propsFile).withInputStream { stream -> p.load(stream) } + def metadata = p.getProperty(metaKey, '') + def prerelease = p.getProperty(preKey, '') + return (p.getProperty(majorKey, '1') + '.' + p.getProperty(minorKey, '0') + '.' + p.getProperty(patchKey, '0') + + (prerelease.length() > 0 ? '-' + prerelease : '') + (metadata.length() > 0 ? '+' + metadata : '')) +} + +version = getVersion() + mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' repositories { - mavenCentral() + mavenLocal() + mavenCentral() } dependencies { - compile 'log4j:log4j:1.2.17@jar' + compile 'log4j:log4j:1.2.17@jar' - compile 'pircbot:pircbot:1.5.0' + compile 'pircbot:pircbot:1.5.0' - compile 'commons-codec:commons-codec:1.10' - compile 'commons-logging:commons-logging:1.2' - compile 'commons-net:commons-net:1.4.1' - compile 'commons-cli:commons-cli:1.2' - compile 'commons-httpclient:commons-httpclient:3.1' + compile 'commons-codec:commons-codec:1.10' + compile 'commons-logging:commons-logging:1.2' + compile 'commons-net:commons-net:1.4.1' + compile 'commons-cli:commons-cli:1.2' + compile 'commons-httpclient:commons-httpclient:3.1' - compile 'oro:oro:2.0.8' + compile 'oro:oro:2.0.8' - compile 'org.jdom:jdom:1.1.3' - compile 'org.jsoup:jsoup:1.8.3' - compile 'rome:rome:1.0@jar' - compile 'rome:rome-fetcher:1.0@jar' - compile 'org.json:json:20150729' - compile 'org.ostermiller:utils:1.07.00' + compile 'org.jdom:jdom:1.1.3' + compile 'org.jsoup:jsoup:1.8.3' + compile 'rome:rome:1.0@jar' + compile 'rome:rome-fetcher:1.0@jar' + compile 'org.json:json:20150729' + compile 'org.ostermiller:utils:1.07.00' - compile 'net.sourceforge.jweather:jweather:0.3.0@jar' - compile 'net.objecthunter:exp4j:0.4.5' + compile 'net.sourceforge.jweather:jweather:0.3.0@jar' + compile 'net.objecthunter:exp4j:0.4.5' - compile 'org.twitter4j:twitter4j-core:4.0.4' - compile 'net.sf.delicious-java:delicious:1.14' + compile 'org.twitter4j:twitter4j-core:4.0.4' + compile 'net.sf.delicious-java:delicious:1.14' - //compile fileTree(dir: 'lib', include: '*.jar') - //compile files('../path/to/example.jar') + compile 'net.thauvin.erik:semver:0.9.1-beta' +} + +annotationProcessor { + project.version = getVersion(isRelease) + library 'net.thauvin.erik:semver:0.9.1-beta' + processor 'net.thauvin.erik.semver.VersionProcessor' } compileJava { - doFirst { - if (isRelease) - { - ant.taskdef(name: 'jreleaseinfo', - classname: 'ch.oscg.jreleaseinfo.anttask.JReleaseInfoAntTask', - classpath: 'ant/jreleaseinfo-1.3.0.jar') - ant.jreleaseinfo(targetDir: file('src/main/java'), - className: 'ReleaseInfo', - packageName: packageName, - project: rootProject.name, - version: version, - buildnumfile: file('buildnum.properties')) - - } - - } - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + options.compilerArgs << '-proc:none' << '-Xlint:unchecked' << '-Xlint:deprecation' } javadoc { - options.tags = ['created'] + options.tags = ['created'] } jar { - manifest.attributes('Main-Class': mainClassName, - 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) + manifest.attributes('Main-Class': mainClassName, + 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) - version = null + version = null } clean { - delete deployDir + delete deployDir } task wrapper(type: Wrapper) { - gradleVersion = gradle.gradleVersion + gradleVersion = gradle.gradleVersion } task copyToDeploy(type: Copy) { - from('properties') { - include '*.properties' - } - from jar - into deployDir + from('properties') { + include '*.properties' + } + from jar + into deployDir } task copyToDeployLib(type: Copy) { - from configurations.runtime - into deployDir + '/lib' + from configurations.runtime + into deployDir + '/lib' } task deploy(dependsOn: ['build']) { - description = "Copies all needed files to the ${deployDir} directory." - group = "Publishing" - outputs.dir deployDir - inputs.files copyToDeploy - inputs.files copyToDeployLib - doLast { - file(deployDir + '/logs').mkdir() - } - mustRunAfter clean + description = 'Copies all needed files to the ${deployDir} directory.' + group = 'Publishing' + outputs.dir deployDir + inputs.files copyToDeploy + inputs.files copyToDeployLib + doLast { + file(deployDir + '/logs').mkdir() + } + mustRunAfter clean } -task release(dependsOn: ['deploy', 'wrapper']) { - group = "Publishing" - description = "Releases new version." +task release(dependsOn: ['deploy', 'wrapper']) << { + group = 'Publishing' + description = 'Releases new version.' + isRelease = true } diff --git a/buildnum.properties b/buildnum.properties deleted file mode 100644 index 268e39c..0000000 --- a/buildnum.properties +++ /dev/null @@ -1,3 +0,0 @@ -#ANT Task: ch.oscg.jreleaseinfo.BuildNumberHandler -#Mon Oct 26 14:39:18 PDT 2015 -build.num.last=0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e8c6bf7bb47dff6b81c2cf7a349eb7e912c9fbe2..13372aef5e24af05341d49695ee84e5f9b594659 100644 GIT binary patch delta 1855 zcmZWp3rtg27(TtXJk)mbS_LnHJmeMSQPk-W70@w65#*skhoFF{Ow4q`1{PuXn8P?Z zU?h@Ep`pg1)V8wWkQ56Pq&zA!YRon?_*&2?(cu`*o%^2wyROO2_n)ud|2+S_hicf8 z8de$`&5EZYWNnT7G!-YLUQz;)w<=DpTZRxyJh439!HIb_{Fw+%3S~s7a$|Yu@&WiG zkO%B5y2qnd%Isl)_)7c;h)*q)KxiGT?Ja@vd+!cqENufVfLSd2mNF*CBr9O%%uccg zLlG>Ynq!M((}1as7?}#jnV4T_zypa^)cxrCL~ALjB2w?Pv^|zI5rc;|huiOtT9Xtg zPY9H2<ny)ipt#-pq&Pe^KsuEuJ@1^%=pjmHtVne$yt>N}C@INpUS%6rUKpI!nRKS( zM)l6HXkCrIOcJ$l^!4S_C3p7^zUWB%TzmDRVn7)okr+hHRgX4x1SCgg$NDB5Iac@E zU`lHB$i=3O7q0A6w`-pIEnfJ3T#n7*D<R+gbuvp?yDeeC<=x*jiihjw<@nUD*q3SB zFZM6AOdg(>EDOC+<@8Kv{y;UfrO)R>w?bv{srtiz8V<h7ol&otqqZCV`FwwC5e`-> z^R~sm^bnPOp|ITf@MYohAl>vCB~LF6hF4y;^VvM`WKo~%<gJlLabDV7mY`OZc7(Cz zJC|?HI9^LSe@q@9e5uqEr>Fk_5|@m(3>dSso=`)w6N|uU*^I2WMk|C4+anYTPO|IH z+UMD3D{yGt>&k-hFgJu6p85S2z>FRk6@f8Qe3OcMkD4h{Q~Etk6L<Wa9caeRzM#x= z<p4GMRSjbJtF(?~p_B%_de12aq4Sm#rSv*$2kNV-!$Hj_H2MQ=81?l1v5zuijR{l} z--KB^eiqw*-H$#Zb67zhSA^G^-LRA1@twty$MTD_Qt!?Bk$L9m=L_E#tky5!_YyzW z2LR4&SqK>4vJ|kqB?j<8OAg@rYij^i*ERq$h7`c926}EbY=pCO>ngzQt#N=?TcZJ| zwNYJWTRfbb+7baB+G*VM_N{Qf-oE;sm%_ui!j*4(V);b~Ig-}Lf4ZVfe8}j{&l{_a zoM;C&8C~G?)W|WFX*jUQ3HPSW#GxG$pjUTrjAuFyOZS@_8!ciSSo&GvbF2%WGV2_< zrV(;YOU4rr)D_ue5C;z&o5A5X|HhGYa*SOTK3ngLH)P51Gm~UCALFrybU2dK7){~? z31a-cgT!#g>P`<33f5B~EYHT5vX|lJE?RKdb&hG+3yH?|+Ds%G+;Y?V9!au`tT_J> z9RKNxZs$U=j(IM83`pkwWoL4k1wxflguKR;1a(ESe5h(|z6Af&PQ_K(n|f8Gg<2M& zMFR2A0Vv}_*DM%icNfPzDaNPk+_A(2Zv%ESaiSI+YKj8E*CvjsD1~Y|O+i4tAymY1 zLb>B5-P9+pn`54x1(kob-T!VS(wsR|MjEXm^#=>exvzwBUUxgfisX6SPe>IbG;<n4 zAp&6=4(M40=?i)|Mo~vf_Md21@Nx>=O6p1c|6D{b$K+~YTx!j9exYEt5oX!L-^r*7 zNLu06+|oi*w|l*zK8!*t3&>o@NHdECi8$=i_c7GBy^qYIk<Q|Z?Zggr5_)A5&{ND_ W5KZ_Um<*>Yx{G7Xa?2Dw&;0|3WSSoU delta 1773 zcmZ8h3s96*6u$fK0xPiO@^W#p1&q)}aM{Qg68MNU9xJP$j#h$cG6R|-LP4bqXo)xo zCmnRGB#~vv$Hs0LO5?%;%P#C9m8CN}L8AE}c{GC&Cf)!1g026|&Ufy2&v(u}=bpPo zr}+C%@kL4Te1R(=LLoUAA66!MSwunkwK}Idnh=s)7PH#>*k4&gSv=zCs14+^{vERr zM&vBOY+f&mdJ(gWL84Ib2NK(ys*uoo3%#6?dBx*8$2g1SPRK0uJ<Ktkk~J>K1P83~ zLgSNH-8r8_3w+&>sb2QT6^)asn_R%XDK4D6cx{Rhi}1_d`9MvJSPA_bmwF3Tp-J)x zwOnUV)aew8RXcJ-9<U^Jt|*7rYxwo&C3fmM9X?JC++*8r3Yc0n&=}_<sBh{u249Fv z2|JK?)^}axa_2e5HL}Z9Z;p)E`uj_-KmOgmL0+*tL44pt;aZt4S@_LE$+Uza&7m*) zh6}$7nzyg#f%d1I^y;4ByD_yE`Rr>Esa<E6wj~x?YKLm_vf@w23TtLOJYn~{Q4n$a z*xy+PbVoPTuFoqt9nfI%_^smLO7*Tqt42(V%B`c~C&N3(whfNtTl1|ROa0#~srVr{ zLzQbtiJpHrbK_TKXH%a<pIo1AI|BbM>=QWcR+{HuW)esF&#sLY3b|=>_!RZkX`eBy z0pXkPAu(giI1+kU`aRBY%K#p3Rc6YBXqd8Pd^B$7M)Ez_z6Ir3)|WdGa!drgLLb;s z>ZVua*Epfm=-z)g{Yb%87i8?k-8?ZXQ)I5Kk7EcCP-!f5XTJpI&S3p$Nivr?skFuu z1!I*%95YbW!x=wn>jk)V@C=_jOWFBBZmX#7Z7yG*ehKF@>-#GlGuDvIVB*+%;iD)% zA=W8`L}KDnn-jQq`{=)HRPsI9v)!m|nxdqm@CN<K2qbm~g2X8C{@?g0E-1p)I6lja zA?y&sHe-b2vw?HQ*Kp^KaW0~yX&$1w>2<{NCKckXruPw3niCOEH?L!{-1H7E(@eO8 zM$-mdPj87s+}ff>ywnnpIHi^I+uXVu*B4t;5Ix(tu$$W6#dUjI!gEg}(7as6mOc3b zc!Wr3=@XwpvlWWWA#Bg#oLS1t0IT^$Ts<&Lnd52DtMvnW+H{C)cSky|UCIP+hHpz| zLUFqnQYR@T-hxz5I~U5;B4xbNVaPrkk}N@Zg1a+C!%CVxo(@L%M0$z_%{Y`<V$dgg zTKc%zg^*9Z2#Ip=IRb;01k4s~l`<EyG4xpWROs(?M`_=tt0BEK^mZy||Jo)#gXTII zttym?JEY9Ce0orSQ02QrLmMYOk@;4amY%GdPR@b}9YurYPtbHIQQASJ_Y2_onILfO z#OVcDr<7?fhMUEU+2#bWU3M6Jhe$_DXxbI*X))Xw)h;ikR1hq(ap6|lq|87iq&3ch z&uxC_rK8lXs;6CahR07m#dayvrlmI%HF+y}l-hHK9xrUNs(PbtR|RcIt@HD}+y{g9 zr@icwG9T1oL(bQ|Fj>a3Zr?liX)``@BV@5d8Q1jGc_#eQh1XnuMauIB|0^r7_)wqp z0vDR_l7J9}gKD4wRiPKfAh&Xza(Jl@&asWO2T>q2bC=`zUY~gyG&dljTj88)WV!zU Djev9M diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 580ff2f..3b69f79 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Oct 24 14:24:39 PDT 2015 +#Mon Jan 25 16:37:02 PST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip diff --git a/gradlew b/gradlew index 97fac78..9d82f78 100644 --- a/gradlew +++ b/gradlew @@ -56,9 +56,9 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- +cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" -cd "$SAVED" >&- +cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar diff --git a/mobibot.iml b/mobibot.iml index de5056b..78a2fd9 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -1,12 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6" relativePaths="false" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1" relativePaths="false" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="false"> <output url="file://$MODULE_DIR$/build/classes/main" /> <output-test url="file://$MODULE_DIR$/build/classes/test" /> <exclude-output /> <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/generated/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/src/annotationProcessor/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" /> <excludeFolder url="file://$MODULE_DIR$/.gradle" /> @@ -32,5 +34,214 @@ <orderEntry type="library" name="Gradle: org.json:json:20150729" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom:1.1.3" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.5" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.1-beta" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> + <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> + <orderEntry type="library" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + </component> + <component name="org.twodividedbyzero.idea.findbugs"> + <option name="_basePreferences"> + <map> + <entry key="property.analysisEffortLevel" value="default" /> + <entry key="property.analyzeAfterAutoMake" value="false" /> + <entry key="property.analyzeAfterCompile" value="false" /> + <entry key="property.annotationGutterIconEnabled" value="true" /> + <entry key="property.annotationSuppressWarningsClass" value="edu.umd.cs.findbugs.annotations.SuppressFBWarnings" /> + <entry key="property.annotationTextRangeMarkupEnabled" value="true" /> + <entry key="property.exportAsHtml" value="true" /> + <entry key="property.exportAsXml" value="true" /> + <entry key="property.exportBaseDir" value="" /> + <entry key="property.exportCreateArchiveDir" value="false" /> + <entry key="property.exportOpenBrowser" value="true" /> + <entry key="property.minPriorityToReport" value="Medium" /> + <entry key="property.runAnalysisInBackground" value="false" /> + <entry key="property.showHiddenDetectors" value="false" /> + <entry key="property.toolWindowToFront" value="true" /> + </map> + </option> + <option name="_detectors"> + <map> + <entry key="AppendingToAnObjectOutputStream" value="true" /> + <entry key="AtomicityProblem" value="true" /> + <entry key="BadAppletConstructor" value="false" /> + <entry key="BadResultSetAccess" value="true" /> + <entry key="BadSyntaxForRegularExpression" value="true" /> + <entry key="BadUseOfReturnValue" value="true" /> + <entry key="BadlyOverriddenAdapter" value="true" /> + <entry key="BooleanReturnNull" value="true" /> + <entry key="BuildInterproceduralCallGraph" value="false" /> + <entry key="BuildObligationPolicyDatabase" value="true" /> + <entry key="BuildStringPassthruGraph" value="true" /> + <entry key="CallToUnsupportedMethod" value="false" /> + <entry key="CalledMethods" value="true" /> + <entry key="CheckCalls" value="false" /> + <entry key="CheckExpectedWarnings" value="false" /> + <entry key="CheckImmutableAnnotation" value="true" /> + <entry key="CheckRelaxingNullnessAnnotation" value="true" /> + <entry key="CheckTypeQualifiers" value="true" /> + <entry key="CloneIdiom" value="true" /> + <entry key="ComparatorIdiom" value="true" /> + <entry key="ConfusedInheritance" value="true" /> + <entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" /> + <entry key="CovariantArrayAssignment" value="false" /> + <entry key="CrossSiteScripting" value="true" /> + <entry key="DefaultEncodingDetector" value="true" /> + <entry key="DoInsideDoPrivileged" value="true" /> + <entry key="DontCatchIllegalMonitorStateException" value="true" /> + <entry key="DontIgnoreResultOfPutIfAbsent" value="true" /> + <entry key="DontUseEnum" value="true" /> + <entry key="DroppedException" value="true" /> + <entry key="DumbMethodInvocations" value="true" /> + <entry key="DumbMethods" value="true" /> + <entry key="DuplicateBranches" value="true" /> + <entry key="EmptyZipFileEntry" value="false" /> + <entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" /> + <entry key="ExplicitSerialization" value="true" /> + <entry key="FieldItemSummary" value="true" /> + <entry key="FinalizerNullsFields" value="true" /> + <entry key="FindBadCast2" value="true" /> + <entry key="FindBadForLoop" value="true" /> + <entry key="FindBugsSummaryStats" value="true" /> + <entry key="FindCircularDependencies" value="false" /> + <entry key="FindComparatorProblems" value="true" /> + <entry key="FindDeadLocalStores" value="true" /> + <entry key="FindDoubleCheck" value="true" /> + <entry key="FindEmptySynchronizedBlock" value="true" /> + <entry key="FindFieldSelfAssignment" value="true" /> + <entry key="FindFinalizeInvocations" value="true" /> + <entry key="FindFloatEquality" value="true" /> + <entry key="FindFloatMath" value="false" /> + <entry key="FindHEmismatch" value="true" /> + <entry key="FindInconsistentSync2" value="true" /> + <entry key="FindJSR166LockMonitorenter" value="true" /> + <entry key="FindLocalSelfAssignment2" value="true" /> + <entry key="FindMaskedFields" value="true" /> + <entry key="FindMismatchedWaitOrNotify" value="true" /> + <entry key="FindNakedNotify" value="true" /> + <entry key="FindNoSideEffectMethods" value="true" /> + <entry key="FindNonSerializableStoreIntoSession" value="false" /> + <entry key="FindNonSerializableValuePassedToWriteObject" value="false" /> + <entry key="FindNonShortCircuit" value="true" /> + <entry key="FindNullDeref" value="true" /> + <entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" /> + <entry key="FindOpenStream" value="true" /> + <entry key="FindPuzzlers" value="true" /> + <entry key="FindRefComparison" value="true" /> + <entry key="FindReturnRef" value="true" /> + <entry key="FindRoughConstants" value="true" /> + <entry key="FindRunInvocations" value="true" /> + <entry key="FindSelfComparison" value="true" /> + <entry key="FindSelfComparison2" value="true" /> + <entry key="FindSleepWithLockHeld" value="true" /> + <entry key="FindSpinLoop" value="true" /> + <entry key="FindSqlInjection" value="true" /> + <entry key="FindTwoLockWait" value="true" /> + <entry key="FindUncalledPrivateMethods" value="true" /> + <entry key="FindUnconditionalWait" value="true" /> + <entry key="FindUninitializedGet" value="true" /> + <entry key="FindUnrelatedTypesInGenericContainer" value="true" /> + <entry key="FindUnreleasedLock" value="true" /> + <entry key="FindUnsatisfiedObligation" value="true" /> + <entry key="FindUnsyncGet" value="true" /> + <entry key="FindUseOfNonSerializableValue" value="true" /> + <entry key="FindUselessControlFlow" value="true" /> + <entry key="FindUselessObjects" value="true" /> + <entry key="FormatStringChecker" value="true" /> + <entry key="FunctionsThatMightBeMistakenForProcedures" value="true" /> + <entry key="HugeSharedStringConstants" value="true" /> + <entry key="IDivResultCastToDouble" value="true" /> + <entry key="IncompatMask" value="true" /> + <entry key="InconsistentAnnotations" value="true" /> + <entry key="InefficientIndexOf" value="false" /> + <entry key="InefficientInitializationInsideLoop" value="false" /> + <entry key="InefficientMemberAccess" value="false" /> + <entry key="InefficientToArray" value="false" /> + <entry key="InfiniteLoop" value="true" /> + <entry key="InfiniteRecursiveLoop" value="true" /> + <entry key="InheritanceUnsafeGetResource" value="true" /> + <entry key="InitializationChain" value="true" /> + <entry key="InitializeNonnullFieldsInConstructor" value="true" /> + <entry key="InstantiateStaticClass" value="true" /> + <entry key="IntCast2LongAsInstant" value="true" /> + <entry key="InvalidJUnitTest" value="true" /> + <entry key="IteratorIdioms" value="true" /> + <entry key="LazyInit" value="true" /> + <entry key="LoadOfKnownNullValue" value="true" /> + <entry key="LostLoggerDueToWeakReference" value="true" /> + <entry key="MethodReturnCheck" value="true" /> + <entry key="Methods" value="true" /> + <entry key="MultithreadedInstanceAccess" value="true" /> + <entry key="MutableEnum" value="true" /> + <entry key="MutableLock" value="true" /> + <entry key="MutableStaticFields" value="true" /> + <entry key="Naming" value="true" /> + <entry key="Noise" value="false" /> + <entry key="NoiseNullDeref" value="false" /> + <entry key="NoteAnnotationRetention" value="true" /> + <entry key="NoteCheckReturnValueAnnotations" value="true" /> + <entry key="NoteDirectlyRelevantTypeQualifiers" value="true" /> + <entry key="NoteJCIPAnnotation" value="true" /> + <entry key="NoteNonNullAnnotations" value="false" /> + <entry key="NoteNonnullReturnValues" value="false" /> + <entry key="NoteSuppressedWarnings" value="true" /> + <entry key="NoteUnconditionalParamDerefs" value="true" /> + <entry key="NumberConstructor" value="true" /> + <entry key="OptionalReturnNull" value="true" /> + <entry key="OverridingEqualsNotSymmetrical" value="true" /> + <entry key="PreferZeroLengthArrays" value="true" /> + <entry key="PublicSemaphores" value="false" /> + <entry key="QuestionableBooleanAssignment" value="true" /> + <entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" /> + <entry key="ReadReturnShouldBeChecked" value="true" /> + <entry key="RedundantConditions" value="true" /> + <entry key="RedundantInterfaces" value="true" /> + <entry key="ReflectiveClasses" value="true" /> + <entry key="RepeatedConditionals" value="true" /> + <entry key="ResolveAllReferences" value="false" /> + <entry key="RuntimeExceptionCapture" value="true" /> + <entry key="SerializableIdiom" value="true" /> + <entry key="StartInConstructor" value="true" /> + <entry key="StaticCalendarDetector" value="true" /> + <entry key="StringConcatenation" value="true" /> + <entry key="SuperfluousInstanceOf" value="true" /> + <entry key="SuspiciousThreadInterrupted" value="true" /> + <entry key="SwitchFallthrough" value="true" /> + <entry key="SynchronizationOnSharedBuiltinConstant" value="true" /> + <entry key="SynchronizeAndNullCheckField" value="true" /> + <entry key="SynchronizeOnClassLiteralNotGetClass" value="true" /> + <entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" /> + <entry key="TestASM" value="false" /> + <entry key="TestDataflowAnalysis" value="false" /> + <entry key="TestingGround" value="false" /> + <entry key="TestingGround2" value="false" /> + <entry key="TrainFieldStoreTypes" value="true" /> + <entry key="TrainLongInstantfParams" value="true" /> + <entry key="TrainNonNullAnnotations" value="true" /> + <entry key="TrainUnconditionalDerefParams" value="true" /> + <entry key="URLProblems" value="true" /> + <entry key="UncallableMethodOfAnonymousClass" value="true" /> + <entry key="UnnecessaryMath" value="true" /> + <entry key="UnreadFields" value="true" /> + <entry key="UselessSubclassMethod" value="false" /> + <entry key="VarArgsProblems" value="true" /> + <entry key="VolatileUsage" value="true" /> + <entry key="WaitInLoop" value="true" /> + <entry key="WrongMapIterator" value="true" /> + <entry key="XMLFactoryBypass" value="true" /> + </map> + </option> + <option name="_reportCategories"> + <map> + <entry key="BAD_PRACTICE" value="true" /> + <entry key="CORRECTNESS" value="true" /> + <entry key="EXPERIMENTAL" value="true" /> + <entry key="I18N" value="true" /> + <entry key="MALICIOUS_CODE" value="true" /> + <entry key="MT_CORRECTNESS" value="true" /> + <entry key="PERFORMANCE" value="true" /> + <entry key="SECURITY" value="true" /> + <entry key="STYLE" value="true" /> + </map> + </option> </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 30be848..67925dd 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -72,7 +72,7 @@ </component> <component name="CopyrightManager" default="Mobibot"> <copyright> - <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> + <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> <option name="keyword" value="Copyright" /> <option name="allowReplaceKeyword" value="Erik C. Thauvin" /> <option name="myName" value="Mobibot" /> @@ -418,6 +418,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/11fb3d88ae7e3b757d70237064210ceb954a5a04/commons-codec-1.10-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: commons-collections:commons-collections:3.2.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/761ea405b9b37ced573d2df0d1e3a4e0f9edc668/commons-collections-3.2.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/fa095ef874374e5b2a11f8b06c26a5d68c7cb3a4/commons-collections-3.2.1-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar!/" /> @@ -427,6 +436,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: commons-lang:commons-lang:2.4"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/16313e02a793435009f1e458fa4af5d879f6fb11/commons-lang-2.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/2b8c4b3035e45520ef42033e823c7d33e4b4402c/commons-lang-2.4-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: commons-logging:commons-logging:1.2"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar!/" /> @@ -481,6 +499,24 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: net.thauvin.erik:semver:0.9.1-beta"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/0.9.1-beta/644e4543823ae014b38e5f089f9d1c5cf9b5b1db/semver-0.9.1-beta.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/0.9.1-beta/f100911addb16b8924d91897972ae074925908fc/semver-0.9.1-beta-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.apache.velocity:velocity:1.7"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/2ceb567b8f3f21118ecdec129fe1271dbc09aa7a/velocity-1.7.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/eb11eb70171ed64842b2e5216d5904e21ed162ac/velocity-1.7-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.jdom:jdom:1.1.3"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> @@ -563,4 +599,227 @@ </SOURCES> </library> </component> + <component name="org.twodividedbyzero.idea.findbugs"> + <option name="annotationTypeSettings"> + <map> + <entry key="ExpPriority" value="-4473925;-12828863;-8355712;WAVE_UNDERSCORE;0;" /> + <entry key="HighPriority" value="-39836;-12828863;-39836;WAVE_UNDERSCORE;1;" /> + <entry key="IgnorePriority" value="-4473925;-12828863;-11978414;WAVE_UNDERSCORE;0;" /> + <entry key="LowPriority" value="-4473925;-12828863;-10316203;BOXED;0;" /> + <entry key="NormalPriority" value="-4473925;-12828863;-10461184;WAVE_UNDERSCORE;2;" /> + </map> + </option> + <option name="_basePreferences"> + <map> + <entry key="property.analysisEffortLevel" value="default" /> + <entry key="property.analyzeAfterAutoMake" value="false" /> + <entry key="property.analyzeAfterCompile" value="false" /> + <entry key="property.annotationGutterIconEnabled" value="true" /> + <entry key="property.annotationSuppressWarningsClass" value="edu.umd.cs.findbugs.annotations.SuppressFBWarnings" /> + <entry key="property.annotationTextRangeMarkupEnabled" value="true" /> + <entry key="property.exportAsHtml" value="true" /> + <entry key="property.exportAsXml" value="true" /> + <entry key="property.exportBaseDir" value="" /> + <entry key="property.exportCreateArchiveDir" value="false" /> + <entry key="property.exportOpenBrowser" value="true" /> + <entry key="property.minPriorityToReport" value="Medium" /> + <entry key="property.runAnalysisInBackground" value="false" /> + <entry key="property.showHiddenDetectors" value="false" /> + <entry key="property.toolWindowToFront" value="true" /> + </map> + </option> + <option name="_detectors"> + <map> + <entry key="AppendingToAnObjectOutputStream" value="true" /> + <entry key="AtomicityProblem" value="true" /> + <entry key="BadAppletConstructor" value="false" /> + <entry key="BadResultSetAccess" value="true" /> + <entry key="BadSyntaxForRegularExpression" value="true" /> + <entry key="BadUseOfReturnValue" value="true" /> + <entry key="BadlyOverriddenAdapter" value="true" /> + <entry key="BooleanReturnNull" value="true" /> + <entry key="BuildInterproceduralCallGraph" value="false" /> + <entry key="BuildObligationPolicyDatabase" value="true" /> + <entry key="BuildStringPassthruGraph" value="true" /> + <entry key="CallToUnsupportedMethod" value="false" /> + <entry key="CalledMethods" value="true" /> + <entry key="CheckCalls" value="false" /> + <entry key="CheckExpectedWarnings" value="false" /> + <entry key="CheckImmutableAnnotation" value="true" /> + <entry key="CheckRelaxingNullnessAnnotation" value="true" /> + <entry key="CheckTypeQualifiers" value="true" /> + <entry key="CloneIdiom" value="true" /> + <entry key="ComparatorIdiom" value="true" /> + <entry key="ConfusedInheritance" value="true" /> + <entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" /> + <entry key="CovariantArrayAssignment" value="false" /> + <entry key="CrossSiteScripting" value="true" /> + <entry key="DefaultEncodingDetector" value="true" /> + <entry key="DoInsideDoPrivileged" value="true" /> + <entry key="DontCatchIllegalMonitorStateException" value="true" /> + <entry key="DontIgnoreResultOfPutIfAbsent" value="true" /> + <entry key="DontUseEnum" value="true" /> + <entry key="DroppedException" value="true" /> + <entry key="DumbMethodInvocations" value="true" /> + <entry key="DumbMethods" value="true" /> + <entry key="DuplicateBranches" value="true" /> + <entry key="EmptyZipFileEntry" value="false" /> + <entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" /> + <entry key="ExplicitSerialization" value="true" /> + <entry key="FieldItemSummary" value="true" /> + <entry key="FinalizerNullsFields" value="true" /> + <entry key="FindBadCast2" value="true" /> + <entry key="FindBadForLoop" value="true" /> + <entry key="FindBugsSummaryStats" value="true" /> + <entry key="FindCircularDependencies" value="false" /> + <entry key="FindComparatorProblems" value="true" /> + <entry key="FindDeadLocalStores" value="true" /> + <entry key="FindDoubleCheck" value="true" /> + <entry key="FindEmptySynchronizedBlock" value="true" /> + <entry key="FindFieldSelfAssignment" value="true" /> + <entry key="FindFinalizeInvocations" value="true" /> + <entry key="FindFloatEquality" value="true" /> + <entry key="FindFloatMath" value="false" /> + <entry key="FindHEmismatch" value="true" /> + <entry key="FindInconsistentSync2" value="true" /> + <entry key="FindJSR166LockMonitorenter" value="true" /> + <entry key="FindLocalSelfAssignment2" value="true" /> + <entry key="FindMaskedFields" value="true" /> + <entry key="FindMismatchedWaitOrNotify" value="true" /> + <entry key="FindNakedNotify" value="true" /> + <entry key="FindNoSideEffectMethods" value="true" /> + <entry key="FindNonSerializableStoreIntoSession" value="false" /> + <entry key="FindNonSerializableValuePassedToWriteObject" value="false" /> + <entry key="FindNonShortCircuit" value="true" /> + <entry key="FindNullDeref" value="true" /> + <entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" /> + <entry key="FindOpenStream" value="true" /> + <entry key="FindPuzzlers" value="true" /> + <entry key="FindRefComparison" value="true" /> + <entry key="FindReturnRef" value="true" /> + <entry key="FindRoughConstants" value="true" /> + <entry key="FindRunInvocations" value="true" /> + <entry key="FindSelfComparison" value="true" /> + <entry key="FindSelfComparison2" value="true" /> + <entry key="FindSleepWithLockHeld" value="true" /> + <entry key="FindSpinLoop" value="true" /> + <entry key="FindSqlInjection" value="true" /> + <entry key="FindTwoLockWait" value="true" /> + <entry key="FindUncalledPrivateMethods" value="true" /> + <entry key="FindUnconditionalWait" value="true" /> + <entry key="FindUninitializedGet" value="true" /> + <entry key="FindUnrelatedTypesInGenericContainer" value="true" /> + <entry key="FindUnreleasedLock" value="true" /> + <entry key="FindUnsatisfiedObligation" value="true" /> + <entry key="FindUnsyncGet" value="true" /> + <entry key="FindUseOfNonSerializableValue" value="true" /> + <entry key="FindUselessControlFlow" value="true" /> + <entry key="FindUselessObjects" value="true" /> + <entry key="FormatStringChecker" value="true" /> + <entry key="FunctionsThatMightBeMistakenForProcedures" value="true" /> + <entry key="HugeSharedStringConstants" value="true" /> + <entry key="IDivResultCastToDouble" value="true" /> + <entry key="IncompatMask" value="true" /> + <entry key="InconsistentAnnotations" value="true" /> + <entry key="InefficientIndexOf" value="false" /> + <entry key="InefficientInitializationInsideLoop" value="false" /> + <entry key="InefficientMemberAccess" value="false" /> + <entry key="InefficientToArray" value="false" /> + <entry key="InfiniteLoop" value="true" /> + <entry key="InfiniteRecursiveLoop" value="true" /> + <entry key="InheritanceUnsafeGetResource" value="true" /> + <entry key="InitializationChain" value="true" /> + <entry key="InitializeNonnullFieldsInConstructor" value="true" /> + <entry key="InstantiateStaticClass" value="true" /> + <entry key="IntCast2LongAsInstant" value="true" /> + <entry key="InvalidJUnitTest" value="true" /> + <entry key="IteratorIdioms" value="true" /> + <entry key="LazyInit" value="true" /> + <entry key="LoadOfKnownNullValue" value="true" /> + <entry key="LostLoggerDueToWeakReference" value="true" /> + <entry key="MethodReturnCheck" value="true" /> + <entry key="Methods" value="true" /> + <entry key="MultithreadedInstanceAccess" value="true" /> + <entry key="MutableEnum" value="true" /> + <entry key="MutableLock" value="true" /> + <entry key="MutableStaticFields" value="true" /> + <entry key="Naming" value="true" /> + <entry key="Noise" value="false" /> + <entry key="NoiseNullDeref" value="false" /> + <entry key="NoteAnnotationRetention" value="true" /> + <entry key="NoteCheckReturnValueAnnotations" value="true" /> + <entry key="NoteDirectlyRelevantTypeQualifiers" value="true" /> + <entry key="NoteJCIPAnnotation" value="true" /> + <entry key="NoteNonNullAnnotations" value="false" /> + <entry key="NoteNonnullReturnValues" value="false" /> + <entry key="NoteSuppressedWarnings" value="true" /> + <entry key="NoteUnconditionalParamDerefs" value="true" /> + <entry key="NumberConstructor" value="true" /> + <entry key="OptionalReturnNull" value="true" /> + <entry key="OverridingEqualsNotSymmetrical" value="true" /> + <entry key="PreferZeroLengthArrays" value="true" /> + <entry key="PublicSemaphores" value="false" /> + <entry key="QuestionableBooleanAssignment" value="true" /> + <entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" /> + <entry key="ReadReturnShouldBeChecked" value="true" /> + <entry key="RedundantConditions" value="true" /> + <entry key="RedundantInterfaces" value="true" /> + <entry key="ReflectiveClasses" value="true" /> + <entry key="RepeatedConditionals" value="true" /> + <entry key="ResolveAllReferences" value="false" /> + <entry key="RuntimeExceptionCapture" value="true" /> + <entry key="SerializableIdiom" value="true" /> + <entry key="StartInConstructor" value="true" /> + <entry key="StaticCalendarDetector" value="true" /> + <entry key="StringConcatenation" value="true" /> + <entry key="SuperfluousInstanceOf" value="true" /> + <entry key="SuspiciousThreadInterrupted" value="true" /> + <entry key="SwitchFallthrough" value="true" /> + <entry key="SynchronizationOnSharedBuiltinConstant" value="true" /> + <entry key="SynchronizeAndNullCheckField" value="true" /> + <entry key="SynchronizeOnClassLiteralNotGetClass" value="true" /> + <entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" /> + <entry key="TestASM" value="false" /> + <entry key="TestDataflowAnalysis" value="false" /> + <entry key="TestingGround" value="false" /> + <entry key="TestingGround2" value="false" /> + <entry key="TrainFieldStoreTypes" value="true" /> + <entry key="TrainLongInstantfParams" value="true" /> + <entry key="TrainNonNullAnnotations" value="true" /> + <entry key="TrainUnconditionalDerefParams" value="true" /> + <entry key="URLProblems" value="true" /> + <entry key="UncallableMethodOfAnonymousClass" value="true" /> + <entry key="UnnecessaryMath" value="true" /> + <entry key="UnreadFields" value="true" /> + <entry key="UselessSubclassMethod" value="false" /> + <entry key="VarArgsProblems" value="true" /> + <entry key="VolatileUsage" value="true" /> + <entry key="WaitInLoop" value="true" /> + <entry key="WrongMapIterator" value="true" /> + <entry key="XMLFactoryBypass" value="true" /> + </map> + </option> + <option name="_reportCategories"> + <map> + <entry key="BAD_PRACTICE" value="true" /> + <entry key="CORRECTNESS" value="true" /> + <entry key="EXPERIMENTAL" value="true" /> + <entry key="I18N" value="true" /> + <entry key="MALICIOUS_CODE" value="true" /> + <entry key="MT_CORRECTNESS" value="true" /> + <entry key="PERFORMANCE" value="true" /> + <entry key="SECURITY" value="true" /> + <entry key="STYLE" value="true" /> + </map> + </option> + <option name="_annotationTypeSettings"> + <map> + <entry key="ExpPriority" value="-4473925;-12828863;-8355712;WAVE_UNDERSCORE;0;" /> + <entry key="HighPriority" value="-39836;-12828863;-39836;WAVE_UNDERSCORE;1;" /> + <entry key="IgnorePriority" value="-4473925;-12828863;-11978414;WAVE_UNDERSCORE;0;" /> + <entry key="LowPriority" value="-4473925;-12828863;-10316203;BOXED;0;" /> + <entry key="NormalPriority" value="-4473925;-12828863;-10461184;WAVE_UNDERSCORE;2;" /> + </map> + </option> + </component> </project> \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java new file mode 100644 index 0000000..c2a290f --- /dev/null +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -0,0 +1,128 @@ +/* + * This file is automatically generated by the Semantic Version Annotation Processor. + * Do not modify this file -- YOUR CHANGES WILL BE ERASED! + */ +package net.thauvin.erik.mobibot; + +import java.util.Date; + +/** + * This class provides semantic version information. + * + * @author Semantic Version Annotation Processor + */ +public final class ReleaseInfo { + private final static String buildmeta = "002"; + private final static Date date = new Date(1453769004642L); + private final static int major = 0; + private final static int minor = 6; + private final static int patch = 1; + private final static String prerelease = "beta"; + private final static String project = "mobibot"; + + /** + * Returns the build date. + * + * @return The build date. + */ + public static Date getBuildDate() { + return date; + } + + /** + * Returns the full version string. + * <p> + * Formatted as: + * <blockquote> + * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> + * </blockquote> + * <p> + * For example: + * <ul> + * <li><code>1.0.0</code></li> + * <li><code>1.0.0-beta</code></li> + * <li><code>1.0.0+20160124144700</code></li> + * <li><code>1.0.0-alpha+001</code></li> + * </ul> + * + * @return The version string. + */ + public static String getVersion() { + return Integer.toString(getMajor()) + '.' + + Integer.toString(getMinor()) + '.' + + Integer.toString(getPatch()) + + getPreRelease() + getBuildMetadata(); + } + + /** + * Returns the major version. + * + * @return The major version. + */ + public static int getMajor() { + return major; + } + + /** + * Returns the minor version. + * + * @return The minor version. + */ + public static int getMinor() { + return minor; + } + + /** + * Returns the patch version. + * + * @return The patch version. + */ + public static int getPatch() { + return patch; + } + + /** + * Returns the pre-release version. + * + * @return The pre-release version, if any. + */ + public static String getPreRelease() { + if (prerelease.length() > 0) { + return '-' + prerelease; + } + + return ""; + } + + /** + * Returns the project name. + * + * @return The project name, if any. + */ + public static String getProject() { + return project; + } + + /** + * Returns the build metadata. + * + * @return The build metadata, if any. + */ + public static String getBuildMetadata() { + if (buildmeta.length() > 0) { + return '+' + buildmeta; + } + + return ""; + } + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if the constructor is called. + */ + private ReleaseInfo() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index b784ea4..81509c5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -1,35 +1,33 @@ /* * Commands.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -40,7 +38,7 @@ package net.thauvin.erik.mobibot; * @created 2014-04-26 * @since 1.0 */ -class Commands +final class Commands { /** * The add (back)log command. diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index b138c95..0cf5fbf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -1,35 +1,33 @@ /* * CurrencyConverter.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index b5bd726..2e536ee 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -1,35 +1,33 @@ /* * DeliciousPoster.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -55,7 +53,7 @@ class DeliciousPoster * @param password The del.icio.us password. * @param ircServer The IRC server. */ - public DeliciousPoster(String username, String password, String ircServer) + public DeliciousPoster(final String username, final String password, final String ircServer) { delicious = new Delicious(username, password); this.ircServer = ircServer; @@ -90,7 +88,7 @@ class DeliciousPoster * * @return The extended attribution line. */ - private String postedBy(EntryLink entry) + private String postedBy(final EntryLink entry) { return "Posted by " + entry.getNick() + " on " + entry.getChannel() + " (" + ircServer + ')'; } @@ -100,7 +98,7 @@ class DeliciousPoster * * @param entry The entry to delete. */ - public final void deletePost(EntryLink entry) + public final void deletePost(final EntryLink entry) { final String link = entry.getLink(); diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/Dice.java index 4031c04..127e9c1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/Dice.java @@ -1,35 +1,33 @@ /* * Dice.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -42,7 +40,7 @@ import java.util.Random; * @created 2014-04-28 * @since 1.0 */ -class Dice +final class Dice { /** * Disables the default constructor. @@ -61,7 +59,7 @@ class Dice * @param bot The bot's instance. * @param sender The sender's nickname. */ - public static void roll(Mobibot bot, String sender) + public static void roll(final Mobibot bot, final String sender) { final Random r = new Random(); diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 371fce0..141f761 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -1,35 +1,33 @@ /* * EntriesMgr.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -50,7 +48,7 @@ import java.util.List; * @created 2014-04-28 * @since 1.0 */ -class EntriesMgr +final class EntriesMgr { /** * The name of the file containing the current entries. @@ -92,7 +90,7 @@ class EntriesMgr * @throws FileNotFoundException If the file was not found. * @throws FeedException If an error occurred while reading the feed. */ - public static void loadBacklogs(String file, List<String> history) + public static void loadBacklogs(final String file, final List<String> history) throws FileNotFoundException, FeedException { history.clear(); @@ -145,7 +143,7 @@ class EntriesMgr * @throws com.sun.syndication.io.FeedException If an error occurred while reading the feed. */ @SuppressWarnings("unchecked") - public static String loadEntries(String file, String channel, List<EntryLink> entries) + public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) throws FileNotFoundException, FeedException { entries.clear(); @@ -224,7 +222,7 @@ class EntriesMgr * @param history The history array. * @param isDayBackup Set the true if the daily backup file should also be created. */ - public static void saveEntries(Mobibot bot, List<EntryLink> entries, List<String> history, boolean isDayBackup) + public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, final boolean isDayBackup) { if (bot.getLogger().isDebugEnabled()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index 1fe3eae..e8e09b2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -1,35 +1,33 @@ /* * EntryComment.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -66,7 +64,7 @@ public class EntryComment implements Serializable * @param comment The new comment. * @param nick The nickname of the comment's author. */ - public EntryComment(String comment, String nick) + public EntryComment(final String comment, final String nick) { this.comment = comment; this.nick = nick; @@ -97,7 +95,7 @@ public class EntryComment implements Serializable * @param comment The actual comment. */ @SuppressWarnings("UnusedDeclaration") - public final void setComment(String comment) + public final void setComment(final String comment) { this.comment = comment; } @@ -128,7 +126,7 @@ public class EntryComment implements Serializable * * @param nick The new nickname. */ - public final void setNick(String nick) + public final void setNick(final String nick) { this.nick = nick; } diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index bcfe220..881f960 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -1,35 +1,33 @@ /* * EntryLink.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -89,7 +87,7 @@ public class EntryLink implements Serializable * @param channel The channel. * @param tags The entry's tags/categories. */ - public EntryLink(String link, String title, String nick, String login, String channel, String tags) + public EntryLink(final String link, final String title, final String nick, final String login, final String channel, final String tags) { this.link = link; this.title = title; @@ -105,7 +103,7 @@ public class EntryLink implements Serializable * * @param tags The space-delimited tags. */ - public final void setTags(String tags) + public final void setTags(final String tags) { if (tags != null) { @@ -165,7 +163,7 @@ public class EntryLink implements Serializable * @param date The entry date. * @param tags The entry's tags/categories. */ - public EntryLink(String link, String title, String nick, String channel, Date date, List<SyndCategoryImpl> tags) + public EntryLink(final String link, final String title, final String nick, final String channel, final Date date, final List<SyndCategoryImpl> tags) { this.link = link; this.title = title; @@ -184,7 +182,7 @@ public class EntryLink implements Serializable * * @return The total number of comments for this entry. */ - public final int addComment(String comment, String nick) + public final int addComment(final String comment, final String nick) { comments.add(new EntryComment(comment, nick)); @@ -196,7 +194,7 @@ public class EntryLink implements Serializable * * @param index The index of the comment to delete. */ - public final void deleteComment(int index) + public final void deleteComment(final int index) { if (index < comments.size()) { @@ -220,7 +218,7 @@ public class EntryLink implements Serializable * @param channel The channel. */ @SuppressWarnings("UnusedDeclaration") - public final void setChannel(String channel) + public final void setChannel(final String channel) { this.channel = channel; } @@ -232,7 +230,7 @@ public class EntryLink implements Serializable * * @return The specific comment. */ - public final EntryComment getComment(int index) + public final EntryComment getComment(final int index) { return (comments.get(index)); } @@ -300,7 +298,7 @@ public class EntryLink implements Serializable * * @param link The new link. */ - public final void setLink(String link) + public final void setLink(final String link) { this.link = link; } @@ -321,7 +319,7 @@ public class EntryLink implements Serializable * @param login The new login. */ @SuppressWarnings("UnusedDeclaration") - public final void setLogin(String login) + public final void setLogin(final String login) { this.login = login; } @@ -341,7 +339,7 @@ public class EntryLink implements Serializable * * @param nick The new nickname. */ - public final void setNick(String nick) + public final void setNick(final String nick) { this.nick = nick; } @@ -361,7 +359,7 @@ public class EntryLink implements Serializable * * @param tags The tags. */ - private void setTags(List<SyndCategoryImpl> tags) + private void setTags(final List<SyndCategoryImpl> tags) { this.tags.addAll(tags); } @@ -381,7 +379,7 @@ public class EntryLink implements Serializable * * @param title The new title. */ - public final void setTitle(String title) + public final void setTitle(final String title) { this.title = title; } @@ -413,7 +411,7 @@ public class EntryLink implements Serializable * @param comment The actual comment. * @param nick The nickname of the author of the comment. */ - public final void setComment(int index, String comment, String nick) + public final void setComment(final int index, final String comment, final String nick) { if (index < comments.size()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 65bfda2..8640f37 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -1,35 +1,33 @@ /* * FeedReader.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -84,7 +82,7 @@ class FeedReader implements Runnable * @param sender The nick of the person who sent the message. * @param url The URL to fetch. */ - public FeedReader(Mobibot bot, String sender, String url) + public FeedReader(final Mobibot bot, final String sender, final String url) { this.bot = bot; this.sender = sender; diff --git a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java index f84e167..25298a1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java @@ -1,35 +1,33 @@ /* * GoogleSearch.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -78,7 +76,7 @@ class GoogleSearch implements Runnable * @param sender The nick of the person who sent the message. * @param query The Google query */ - public GoogleSearch(Mobibot bot, String sender, String query) + public GoogleSearch(final Mobibot bot, final String sender, final String query) { this.bot = bot; this.sender = sender; diff --git a/src/main/java/net/thauvin/erik/mobibot/Joke.java b/src/main/java/net/thauvin/erik/mobibot/Joke.java index 1c013eb..facffa7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/Joke.java @@ -1,35 +1,33 @@ /* * Joke.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -72,7 +70,7 @@ class Joke implements Runnable * @param bot The bot's instance. * @param sender The nick of the person who sent the message. */ - public Joke(Mobibot bot, String sender) + public Joke(final Mobibot bot, final String sender) { this.bot = bot; this.sender = sender; diff --git a/src/main/java/net/thauvin/erik/mobibot/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/Lookup.java index 2a72f05..560f90a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/Lookup.java @@ -1,35 +1,33 @@ /* * Lookup.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -46,7 +44,7 @@ import java.net.UnknownHostException; * @created 2014-04-26 * @since 1.0 */ -class Lookup +final class Lookup { /** @@ -74,7 +72,7 @@ class Lookup * * @throws java.net.UnknownHostException If the host is unknown. */ - public static String lookup(String query) + public static String lookup(final String query) throws UnknownHostException { final StringBuilder buffer = new StringBuilder(""); @@ -118,7 +116,7 @@ class Lookup * * @throws java.io.IOException If a connection error occurs. */ - public static String[] whois(String query) + public static String[] whois(final String query) throws IOException { return whois(query, WHOIS_HOST); @@ -135,7 +133,7 @@ class Lookup * @throws java.io.IOException If a connection error occurs. */ @SuppressWarnings("WeakerAccess, SameParameterValue") - public static String[] whois(String query, String host) + public static String[] whois(final String query, final String host) throws IOException { final WhoisClient whois = new WhoisClient(); diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 8fe9503..2605827 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1,35 +1,33 @@ /* * Mobibot.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -38,6 +36,7 @@ import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache; import com.sun.syndication.io.FeedException; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; +import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; import org.apache.commons.logging.impl.Log4JLogger; import org.apache.log4j.Level; @@ -60,6 +59,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * @created Jan 31, 2004 * @since 1.0 */ +@Version(properties = "version.properties", className = "ReleaseInfo") public class Mobibot extends PircBot { /** @@ -92,8 +92,8 @@ public class Mobibot extends PircBot * The info strings. */ private static final String[] INFO_STRS = { - ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() - + " by Erik C. Thauvin (erik@thauvin.net)", "http://www.mobitopia.org/mobibot/" + ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + " by Erik C. Thauvin (erik@thauvin.net)", + "http://www.mobitopia.org/mobibot/" }; /** @@ -145,8 +145,7 @@ public class Mobibot extends PircBot * The version strings. */ private static final String[] VERSION_STRS = { - "Version: " + ReleaseInfo.getVersion() + '.' + ReleaseInfo.getBuildNumber() + " (" + Utils.ISO_SDF - .format(ReleaseInfo.getBuildDate()) + ')', + "Version: " + ReleaseInfo.getVersion() + " (" + Utils.ISO_SDF.format(ReleaseInfo.getBuildDate()) + ')', "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System @@ -320,7 +319,8 @@ public class Mobibot extends PircBot * @param logsDir The logs directory. */ @SuppressWarnings("WeakerAccess") - public Mobibot(String server, int port, String nickname, String channel, String logsDir) + public Mobibot(final String server, final int port, final String nickname, final String channel, + final String logsDir) { System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); @@ -385,7 +385,7 @@ public class Mobibot extends PircBot * * @param args The command line arguments. */ - public static void main(String[] args) + public static void main(final String[] args) { // Setup the command line options final Options options = new Options(); @@ -639,7 +639,7 @@ public class Mobibot extends PircBot * * @param pwd The password. */ - private void setIdent(String pwd) + private void setIdent(final String pwd) { ident = pwd; } @@ -649,7 +649,7 @@ public class Mobibot extends PircBot * * @param nick The nickname. */ - private void setIdentNick(String nick) + private void setIdentNick(final String nick) { identNick = nick; } @@ -659,7 +659,7 @@ public class Mobibot extends PircBot * * @param msg The message. */ - private void setIdentMsg(String msg) + private void setIdentMsg(final String msg) { identMsg = msg; } @@ -669,7 +669,7 @@ public class Mobibot extends PircBot * * @param feedURL The feed URL. */ - private void setFeedURL(String feedURL) + private void setFeedURL(final String feedURL) { this.feedURL = feedURL; } @@ -680,7 +680,7 @@ public class Mobibot extends PircBot * @param username The del.icio.us user name. * @param password The del.icio.us password. */ - private void setDeliciousAuth(String username, String password) + private void setDeliciousAuth(final String username, final String password) { delicious = new DeliciousPoster(username, password, ircServer); } @@ -693,7 +693,8 @@ public class Mobibot extends PircBot * @param token The Twitter token. * @param tokenSecret The Twitter token secret. */ - private void setTwitterAuth(String consumerKey, String consumerSecret, String token, String tokenSecret) + private void setTwitterAuth(final String consumerKey, final String consumerSecret, final String token, + final String tokenSecret) { twitterConsumerKey = consumerKey; twitterConsumerSecret = consumerSecret; @@ -706,7 +707,7 @@ public class Mobibot extends PircBot * * @param tags The tags. */ - private void setTags(String tags) + private void setTags(final String tags) { defaultTags = tags; } @@ -716,7 +717,7 @@ public class Mobibot extends PircBot * * @param nicks The nicks to ignore */ - private void setIgnoredNicks(String nicks) + private void setIgnoredNicks(final String nicks) { if (Utils.isValidString(nicks)) { @@ -735,7 +736,7 @@ public class Mobibot extends PircBot * @param tellMaxDays The max number of days to hold messages for. * @param tellMaxSize The maximum number of messages to hold */ - private void setTell(int tellMaxDays, int tellMaxSize) + private void setTell(final int tellMaxDays, final int tellMaxSize) { this.tellMaxDays = tellMaxDays; this.tellMaxSize = tellMaxSize; @@ -746,7 +747,7 @@ public class Mobibot extends PircBot * * @param isDayBackup Set the true if the daily backup file should also be created. */ - private void saveEntries(boolean isDayBackup) + private void saveEntries(final boolean isDayBackup) { EntriesMgr.saveEntries(this, entries, history, isDayBackup); } @@ -756,7 +757,7 @@ public class Mobibot extends PircBot * * @param secs The number of seconds to sleep for. */ - private static void sleep(int secs) + private static void sleep(final int secs) { try { @@ -816,7 +817,7 @@ public class Mobibot extends PircBot * * @param action The action. */ - final void action(String action) + final void action(final String action) { action(channel, action); } @@ -827,7 +828,7 @@ public class Mobibot extends PircBot * @param channel The channel. * @param action The action. */ - private void action(String channel, String action) + private void action(final String channel, final String action) { if (Utils.isValidString(channel) && Utils.isValidString(action)) { @@ -842,7 +843,7 @@ public class Mobibot extends PircBot * @param args The command arguments. * @param message The actual message. */ - private void calcResponse(String sender, String args, String message) + private void calcResponse(final String sender, final String args, final String message) { if (Utils.isValidString(args)) { @@ -874,7 +875,7 @@ public class Mobibot extends PircBot * * @param sender The nick of the person who sent the private message. */ - private void feedResponse(String sender) + private void feedResponse(final String sender) { if (Utils.isValidString(feedURL)) { @@ -893,7 +894,7 @@ public class Mobibot extends PircBot * * @return The index or -1 if none. */ - private int findDupEntry(String link) + private int findDupEntry(final String link) { EntryLink entry; @@ -928,7 +929,7 @@ public class Mobibot extends PircBot * * @param backLogsUrl The backlogs URL. */ - private void setBacklogsUrl(String backLogsUrl) + private void setBacklogsUrl(final String backLogsUrl) { this.backLogsUrl = backLogsUrl; } @@ -1027,7 +1028,7 @@ public class Mobibot extends PircBot * * @param weblogUrl The weblog URL. */ - private void setWeblogUrl(String weblogUrl) + private void setWeblogUrl(final String weblogUrl) { this.weblogUrl = weblogUrl; } @@ -1038,7 +1039,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the private message. * @param query The Google query to execute. */ - private void googleResponse(String sender, String query) + private void googleResponse(final String sender, final String query) { if (query.length() > 0) { @@ -1057,7 +1058,7 @@ public class Mobibot extends PircBot * * @return The indented help string. */ - private String helpIndent(String help) + private String helpIndent(final String help) { return helpIndent(help, true); } @@ -1070,7 +1071,7 @@ public class Mobibot extends PircBot * * @return The indented help string. */ - private String helpIndent(String help, boolean isBold) + private String helpIndent(final String help, final boolean isBold) { return " " + (isBold ? Utils.bold(help) : help); } @@ -1081,7 +1082,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the private message. * @param topic The help topic, if any. */ - public final void helpResponse(String sender, String topic) + public final void helpResponse(final String sender, final String topic) { final String lcTopic = topic.toLowerCase(); @@ -1336,7 +1337,7 @@ public class Mobibot extends PircBot * @param sender The sender. * @param args The command arguments. */ - private void ignoreResponse(String sender, String args) + private void ignoreResponse(final String sender, final String args) { if (!isOp(sender)) { @@ -1404,7 +1405,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the message. * @param isPrivate Set to true is the response should be send as a private message. */ - private void infoResponse(String sender, boolean isPrivate) + private void infoResponse(final String sender, final boolean isPrivate) { for (final String info : INFO_STRS) { @@ -1434,7 +1435,7 @@ public class Mobibot extends PircBot * * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. */ - private boolean isIgnoredNick(String nick) + private boolean isIgnoredNick(final String nick) { return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); @@ -1447,7 +1448,7 @@ public class Mobibot extends PircBot * * @return true, if the sender is an Op. */ - private boolean isOp(String sender) + private boolean isOp(final String sender) { final User[] users = getUsers(channel); @@ -1479,7 +1480,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the message * @param query The hostname or IP address. */ - private void lookupResponse(String sender, String query) + private void lookupResponse(final String sender, final String query) { if (query.matches("(\\S.)+(\\S)+")) { @@ -1598,7 +1599,8 @@ public class Mobibot extends PircBot } @Override - protected final void onMessage(String channel, String sender, String login, String hostname, String message) + protected final void onMessage(final String channel, final String sender, final String login, final String hostname, + final String message) { if (logger.isDebugEnabled()) { @@ -2093,7 +2095,8 @@ public class Mobibot extends PircBot } @Override - protected final void onPrivateMessage(String sender, String login, String hostname, String message) + protected final void onPrivateMessage(final String sender, final String login, final String hostname, + final String message) { if (logger.isDebugEnabled()) { @@ -2270,7 +2273,8 @@ public class Mobibot extends PircBot } @Override - protected final void onAction(String sender, String login, String hostname, String target, String action) + protected final void onAction(final String sender, final String login, final String hostname, final String target, + final String action) { if (target.equals(channel)) { @@ -2285,7 +2289,7 @@ public class Mobibot extends PircBot * @param message The actual message sent. * @param isAction Set to true if the message is an action. */ - private void recap(String sender, String message, boolean isAction) + private void recap(final String sender, final String message, final boolean isAction) { recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") + message); @@ -2297,13 +2301,13 @@ public class Mobibot extends PircBot } @Override - protected void onJoin(String channel, String sender, String login, String hostname) + protected void onJoin(final String channel, final String sender, final String login, final String hostname) { tellSendMessages(sender); } @Override - protected void onNickChange(String oldNick, String login, String hostname, String newNick) + protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { tellSendMessages(newNick); } @@ -2313,7 +2317,7 @@ public class Mobibot extends PircBot * * @param nickname The user's nickname. */ - private void tellSendMessages(String nickname) + private void tellSendMessages(final String nickname) { tellSendMessages(nickname, false); } @@ -2324,7 +2328,7 @@ public class Mobibot extends PircBot * @param nickname The user's nickname. * @param isMessage The message flag. */ - private void tellSendMessages(String nickname, boolean isMessage) + private void tellSendMessages(final String nickname, final boolean isMessage) { if (!nickname.equals(getNick()) && isTellEnabled()) { @@ -2396,7 +2400,7 @@ public class Mobibot extends PircBot * @param message The actual message. * @param isPrivate Set to true if the response should be a private message, otherwise a notice is sent. */ - public final void send(String sender, String message, boolean isPrivate) + public final void send(final String sender, final String message, final boolean isPrivate) { if (Utils.isValidString(message) && Utils.isValidString(sender)) { @@ -2427,7 +2431,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the private message. * @param isPrivate Set to true is the response should be send as a private message. */ - private void recapResponse(String sender, boolean isPrivate) + private void recapResponse(final String sender, final boolean isPrivate) { for (final String recap : this.recap) { @@ -2441,7 +2445,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the message. * @param message The actual message. */ - public final void send(String sender, String message) + public final void send(final String sender, final String message) { send(sender, message, false); } @@ -2452,7 +2456,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the message. * @param symbol The stock symbol to lookup. */ - private void stockResponse(String sender, String symbol) + private void stockResponse(final String sender, final String symbol) { if (symbol.length() > 0) { @@ -2470,7 +2474,7 @@ public class Mobibot extends PircBot * @param sender The sender's nick. * @param cmds The commands string. */ - private void tellResponse(String sender, String cmds) + private void tellResponse(final String sender, final String cmds) { if (!Utils.isValidString(cmds)) { @@ -2653,7 +2657,7 @@ public class Mobibot extends PircBot * @param sender The sender's nick. * @param message The message. */ - private void twitterResponse(String sender, String message) + private void twitterResponse(final String sender, final String message) { if (isTwitterEnabled()) { @@ -2684,7 +2688,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the message. * @param isPrivate Set to true is the response should be send as a private message. */ - private void usersResponse(String sender, boolean isPrivate) + private void usersResponse(final String sender, final boolean isPrivate) { final User[] users = getUsers(channel); final String[] nicks = new String[users.length]; @@ -2717,7 +2721,7 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the message. * @param isPrivate Set to true is the response should be send as a private message. */ - private void versionResponse(String sender, boolean isPrivate) + private void versionResponse(final String sender, final boolean isPrivate) { if (isOp(sender)) { @@ -2735,7 +2739,7 @@ public class Mobibot extends PircBot * @param args The view command arguments. * @param isPrivate Set to true is the response should be send as a private message. */ - private void viewResponse(String sender, String args, boolean isPrivate) + private void viewResponse(final String sender, final String args, final boolean isPrivate) { String lcArgs = args.toLowerCase(); @@ -2838,7 +2842,7 @@ public class Mobibot extends PircBot * @param id The station's ID. * @param isPrivate Set to true is the response should be send as a private message. */ - private void weatherResponse(String sender, String id, boolean isPrivate) + private void weatherResponse(final String sender, final String id, final boolean isPrivate) { new Thread(new Weather(this, sender, id, isPrivate)).start(); } diff --git a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java deleted file mode 100644 index 6179c2b..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ /dev/null @@ -1,59 +0,0 @@ -/* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Mon Oct 26 14:39:18 PDT 2015 */ -package net.thauvin.erik.mobibot; - -import java.util.Date; - -/** - * This class provides information gathered from the build environment. - * - * @author JReleaseInfo AntTask - */ -public class ReleaseInfo { - - /** - * Disables the default constructor. - * @throws UnsupportedOperationException if the constructor is called. - */ - private ReleaseInfo() throws UnsupportedOperationException { - throw new UnsupportedOperationException("Illegal constructor call."); - } - - - /** buildDate (set during build process to 1445895558361L). */ - private static final Date buildDate = new Date(1445895558361L); - - /** - * Get buildDate (set during build process to Mon Oct 26 14:39:18 PDT 2015). - * @return Date buildDate - */ - public static Date getBuildDate() { return buildDate; } - - - /** project (set during build process to "mobibot"). */ - private static final String project = "mobibot"; - - /** - * Get project (set during build process to "mobibot"). - * @return String project - */ - public static String getProject() { return project; } - - - /** version (set during build process to "0.6"). */ - private static final String version = "0.6"; - - /** - * Get version (set during build process to "0.6"). - * @return String version - */ - public static String getVersion() { return version; } - - - /** - * Get buildNumber (set during build process to 0). - * @return int buildNumber - */ - public static int getBuildNumber() { return 0; } - -} diff --git a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java index 01d0de4..e026841 100644 --- a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/StockQuote.java @@ -1,35 +1,33 @@ /* * StockQuote.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -75,7 +73,7 @@ class StockQuote implements Runnable * @param sender The nick of the person who sent the message. * @param symbol The stock symbol. */ - public StockQuote(Mobibot bot, String sender, String symbol) + public StockQuote(final Mobibot bot, final String sender, final String symbol) { this.bot = bot; this.sender = sender; diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 22e3346..1d20b77 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -1,35 +1,33 @@ /* * TellMessage.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -71,7 +69,7 @@ public class TellMessage implements Serializable * @param recipient The recipient's nick. * @param message The message. */ - public TellMessage(String sender, String recipient, String message) + public TellMessage(final String sender, final String recipient, final String message) { this.sender = sender; this.recipient = recipient; @@ -149,7 +147,7 @@ public class TellMessage implements Serializable * * @return <code>true</code> if the nickname matches. */ - public boolean isMatch(String nick) + public boolean isMatch(final String nick) { return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); } @@ -161,7 +159,7 @@ public class TellMessage implements Serializable * * @return <code>true</code> if the id matches. */ - public boolean isMatchId(String id) + public boolean isMatchId(final String id) { return this.id.equals(id); } diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index a6fc52b..cc659bd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -1,35 +1,33 @@ /* * TellMessagesMgr.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -48,7 +46,7 @@ import java.util.List; * @created 2014-04-26 * @since 1.0 */ -class TellMessagesMgr +final class TellMessagesMgr { /** * Disables the default constructor. @@ -69,7 +67,7 @@ class TellMessagesMgr * * @return <code>True</code> if the queue was cleaned. */ - public static boolean cleanTellMessages(List<TellMessage> tellMessages, int tellMaxDays) + public static boolean cleanTellMessages(final List<TellMessage> tellMessages, final int tellMaxDays) { final Calendar maxDate = Calendar.getInstance(); final Date today = new Date(); @@ -99,7 +97,7 @@ class TellMessagesMgr * @return The {@link net.thauvin.erik.mobibot.TellMessage} array. */ @SuppressWarnings("unchecked") - public static List<TellMessage> load(String file, Log4JLogger logger) + public static List<TellMessage> load(final String file, final Log4JLogger logger) { try { @@ -142,7 +140,7 @@ class TellMessagesMgr * @param messages The {@link net.thauvin.erik.mobibot.TellMessage} array. * @param logger The logger. */ - public static void save(String file, List<TellMessage> messages, Log4JLogger logger) + public static void save(final String file, final List<TellMessage> messages, final Log4JLogger logger) { try { diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/Twitter.java index f9b55e5..3bf3030 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/Twitter.java @@ -1,35 +1,33 @@ /* * Twitter.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -92,8 +90,8 @@ class Twitter implements Runnable * @param accessTokenSecret The Twitter access token secret. * @param message The Twitter message. */ - public Twitter(Mobibot bot, String sender, String consumerKey, String consumerSecret, String accessToken, - String accessTokenSecret, String message) + public Twitter(final Mobibot bot, final String sender, final String consumerKey, final String consumerSecret, final String accessToken, + final String accessTokenSecret, final String message) { this.bot = bot; this.consumerKey = consumerKey; diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 9a0977b..d56de0d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -1,35 +1,33 @@ /* * Utils.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/War.java index 7313720..f4c5b0e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/War.java @@ -1,35 +1,33 @@ /* * War.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -42,7 +40,7 @@ import java.util.Random; * @created 2014-04-28 * @since 1.0 */ -class War +final class War { /** * The deck of card for the {@link net.thauvin.erik.mobibot.Commands#WAR_CMD war} command. @@ -72,7 +70,7 @@ class War * @param bot The bot's instance. * @param sender The sender's nickname. */ - public static void play(Mobibot bot, String sender) + public static void play(final Mobibot bot, final String sender) { final Random r = new Random(); diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/Weather.java index 0b40551..e0b457d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/Weather.java @@ -1,35 +1,33 @@ /* * Weather.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -87,7 +85,7 @@ class Weather implements Runnable * @param station The station ID. * @param isPrivate Set to true is the response should be send as a private message. */ - public Weather(Mobibot bot, String sender, String station, boolean isPrivate) + public Weather(final Mobibot bot, final String sender, final String station, final boolean isPrivate) { this.bot = bot; this.sender = sender; diff --git a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java index 2b2116e..da6364c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java @@ -1,35 +1,33 @@ /* * WorldTime.java * - * Copyright (c) 2004-2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot; @@ -147,7 +145,7 @@ class WorldTime * @param args The time command arguments. * @param isPrivate Set to true is the response should be send as a private message. */ - public final void timeResponse(Mobibot bot, String sender, String args, boolean isPrivate) + public final void timeResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { boolean isInvalidTz = false; final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); diff --git a/version.properties b/version.properties new file mode 100644 index 0000000..3349c49 --- /dev/null +++ b/version.properties @@ -0,0 +1,8 @@ +#Mon, 25 Jan 2016 16:36:57 -0800 +#Mon Dec 07 01:31:00 PST 2015 +version.project=mobibot +version.major=0 +version.minor=6 +version.patch=1 +version.prerelease=beta +version.buildmeta=002 From 2c1602f331dff0ebd794e9060b11538677c37a90 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 2 Feb 2016 21:39:29 -0800 Subject: [PATCH 028/842] Code formatting. --- build.gradle | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 484eff9..6c1b622 100644 --- a/build.gradle +++ b/build.gradle @@ -11,16 +11,14 @@ def packageName = 'net.thauvin.erik.mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -def getVersion(isIncrement = false) -{ +def getVersion(isIncrement = false) { def propsFile = 'version.properties' def majorKey = 'version.major' def minorKey = 'version.minor' def patchKey = 'version.patch' def metaKey = 'version.buildmeta' def preKey = 'version.prerelease' - if (isIncrement) - { + if (isIncrement) { ant.propertyfile(file: propsFile) { entry(key: patchKey, type: 'int', From e1685ba706f727d012fde707262694783c115f38 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 5 Feb 2016 12:53:03 -0800 Subject: [PATCH 029/842] Updated dependencies. --- build.gradle | 21 +- mobibot.iml | 12 +- mobibot.ipr | 30 +-- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 205 +++++++++--------- .../erik/mobibot/CurrencyConverter.java | 10 +- .../java/net/thauvin/erik/mobibot/Lookup.java | 4 +- .../net/thauvin/erik/mobibot/Mobibot.java | 7 +- version.properties | 2 +- 8 files changed, 151 insertions(+), 140 deletions(-) diff --git a/build.gradle b/build.gradle index 6c1b622..168d333 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,19 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.coders-kitchen:compileonlyplugin:1.0.0' + } +} plugins { id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" + id "com.github.ben-manes.versions" version "0.12.0" } apply plugin: 'java' apply plugin: 'idea' apply plugin: 'application' +apply plugin: 'compileOnly' defaultTasks 'deploy' @@ -51,17 +61,18 @@ dependencies { compile 'commons-codec:commons-codec:1.10' compile 'commons-logging:commons-logging:1.2' - compile 'commons-net:commons-net:1.4.1' - compile 'commons-cli:commons-cli:1.2' + compile 'commons-net:commons-net:3.4' + compile 'commons-cli:commons-cli:1.3.1' compile 'commons-httpclient:commons-httpclient:3.1' compile 'oro:oro:2.0.8' compile 'org.jdom:jdom:1.1.3' + compile 'org.jdom:jdom2:2.0.6' compile 'org.jsoup:jsoup:1.8.3' compile 'rome:rome:1.0@jar' compile 'rome:rome-fetcher:1.0@jar' - compile 'org.json:json:20150729' + compile 'org.json:json:20151123' compile 'org.ostermiller:utils:1.07.00' compile 'net.sourceforge.jweather:jweather:0.3.0@jar' @@ -70,12 +81,12 @@ dependencies { compile 'org.twitter4j:twitter4j-core:4.0.4' compile 'net.sf.delicious-java:delicious:1.14' - compile 'net.thauvin.erik:semver:0.9.1-beta' + compileOnly 'net.thauvin.erik:semver:0.9.5-beta' } annotationProcessor { project.version = getVersion(isRelease) - library 'net.thauvin.erik:semver:0.9.1-beta' + library 'net.thauvin.erik:semver:0.9.5-beta' processor 'net.thauvin.erik.semver.VersionProcessor' } diff --git a/mobibot.iml b/mobibot.iml index 78a2fd9..58ff58e 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1" relativePaths="false" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1-beta+002" relativePaths="false" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="false"> <output url="file://$MODULE_DIR$/build/classes/main" /> <output-test url="file://$MODULE_DIR$/build/classes/test" /> @@ -18,8 +18,6 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" exported="" name="Gradle: log4j:log4j:1.2.17" level="project" /> <orderEntry type="library" exported="" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-net:commons-net:1.4.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-cli:commons-cli:1.2" level="project" /> <orderEntry type="library" exported="" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> <orderEntry type="library" exported="" name="Gradle: oro:oro:2.0.8" level="project" /> <orderEntry type="library" exported="" name="Gradle: rome:rome:1.0" level="project" /> @@ -31,13 +29,15 @@ <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.8.3" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20150729" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom:1.1.3" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.5" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.1-beta" level="project" /> <orderEntry type="library" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> <orderEntry type="library" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom:2.0.2" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20151123" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> </component> <component name="org.twodividedbyzero.idea.findbugs"> <option name="_basePreferences"> diff --git a/mobibot.ipr b/mobibot.ipr index 67925dd..5e6f7be 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -400,13 +400,13 @@ </option> </component> <component name="libraryTable"> - <library name="Gradle: commons-cli:commons-cli:1.2"> + <library name="Gradle: commons-cli:commons-cli:1.3.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/1303efbc4b181e5a58bf2e967dc156a3132b97c0/commons-cli-1.3.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/6c5459816530a1962ac18cd315cc775b1b384329/commons-cli-1.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/12ec02d8cb9fbb33bd05506109a4fc8bcc3578/commons-cli-1.3.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: commons-codec:commons-codec:1.10"> @@ -454,13 +454,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/ecf26c7507d67782a3bbd148d170b31dfad001aa/commons-logging-1.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-net:commons-net:1.4.1"> + <library name="Gradle: commons-net:commons-net:3.4"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/abb932adb2c10790c1eaa4365d3ac2a1ac7cb700/commons-net-1.4.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.4/5e984db9554728564d58e90da5d90eff8ae8cf2d/commons-net-3.4.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/1.4.1/4c85b39e7f03471338bf7d36558eefe1e463e3de/commons-net-1.4.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.4/8439b8f20d7bdec502423732798a639501732c8/commons-net-3.4-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: log4j:log4j:1.2.17"> @@ -499,13 +499,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:semver:0.9.1-beta"> + <library name="Gradle: net.thauvin.erik:semver:0.9.5-beta"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/0.9.1-beta/644e4543823ae014b38e5f089f9d1c5cf9b5b1db/semver-0.9.1-beta.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/0.9.1-beta/f100911addb16b8924d91897972ae074925908fc/semver-0.9.1-beta-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.apache.velocity:velocity:1.7"> @@ -517,22 +517,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/eb11eb70171ed64842b2e5216d5904e21ed162ac/velocity-1.7-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jdom:jdom:1.1.3"> + <library name="Gradle: org.jdom:jdom:2.0.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/8bdfeb39fa929c35f5e4f0b02d34350db39a1efc/jdom-1.1.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/d06c71e0df0ac4b94deb737718580ccce22d92e8/jdom-2.0.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/1.1.3/b8b961a9f20d1faf160681c49f773a66b87efd63/jdom-1.1.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/2c0614e33230394e42f5cfe55c20d5ae8949c108/jdom-2.0.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20150729"> + <library name="Gradle: org.json:json:20151123"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20150729/7ad48f520e9f94787fdbf2beb9916c76971bdcb0/json-20150729.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20151123/1675d5c1142000dedd6627a73af26a8b885ef0e1/json-20151123.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20150729/d71e3dbedb977d57f76453af0bb43eed162adc30/json-20150729-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20151123/9c179a1becb79c6af729c89f7b212b1cb5e68da3/json-20151123-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jsoup:jsoup:1.8.3"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index c2a290f..e3e8542 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,128 +1,129 @@ /* - * This file is automatically generated by the Semantic Version Annotation Processor. - * Do not modify this file -- YOUR CHANGES WILL BE ERASED! + * This file is automatically generated. + * Do not modify! -- ALL CHANGES WILL BE ERASED! */ package net.thauvin.erik.mobibot; import java.util.Date; /** - * This class provides semantic version information. + * Provides semantic version information. * - * @author Semantic Version Annotation Processor + * @author <a href="https://github.com/ethauvin/semver">Semantic Version + * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "002"; - private final static Date date = new Date(1453769004642L); + private final static String buildmeta = "003"; + private final static Date date = new Date(1454706693276L); private final static int major = 0; private final static int minor = 6; private final static int patch = 1; private final static String prerelease = "beta"; private final static String project = "mobibot"; - /** - * Returns the build date. - * - * @return The build date. - */ - public static Date getBuildDate() { - return date; - } + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private ReleaseInfo() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } - /** - * Returns the full version string. - * <p> - * Formatted as: - * <blockquote> - * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> - * </blockquote> - * <p> - * For example: - * <ul> - * <li><code>1.0.0</code></li> - * <li><code>1.0.0-beta</code></li> - * <li><code>1.0.0+20160124144700</code></li> - * <li><code>1.0.0-alpha+001</code></li> - * </ul> - * - * @return The version string. - */ - public static String getVersion() { - return Integer.toString(getMajor()) + '.' - + Integer.toString(getMinor()) + '.' - + Integer.toString(getPatch()) - + getPreRelease() + getBuildMetadata(); - } + /** + * Returns the build date. + * + * @return The build date. + */ + public static Date getBuildDate() { + return date; + } - /** - * Returns the major version. - * - * @return The major version. - */ - public static int getMajor() { - return major; - } + /** + * Returns the project name. + * + * @return The project name, if any. + */ + public static String getProject() { + return project; + } - /** - * Returns the minor version. - * - * @return The minor version. - */ - public static int getMinor() { - return minor; - } + /** + * Returns the full version string. + * <p> + * Formatted as: + * <blockquote> + * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> + * </blockquote> + * <p> + * For example: + * <ul> + * <li><code>1.0.0</code></li> + * <li><code>1.0.0-beta</code></li> + * <li><code>1.0.0+20160124144700</code></li> + * <li><code>1.0.0-alpha+001</code></li> + * </ul> + * + * @return The version string. + */ + public static String getVersion() { + return Integer.toString(getMajor()) + '.' + + Integer.toString(getMinor()) + '.' + + Integer.toString(getPatch()) + + getPreRelease() + getBuildMetadata(); + } - /** - * Returns the patch version. - * - * @return The patch version. - */ - public static int getPatch() { - return patch; - } + /** + * Returns the major version. + * + * @return The major version. + */ + public static int getMajor() { + return major; + } - /** - * Returns the pre-release version. - * - * @return The pre-release version, if any. - */ - public static String getPreRelease() { - if (prerelease.length() > 0) { - return '-' + prerelease; - } + /** + * Returns the minor version. + * + * @return The minor version. + */ + public static int getMinor() { + return minor; + } - return ""; - } + /** + * Returns the patch version. + * + * @return The patch version. + */ + public static int getPatch() { + return patch; + } - /** - * Returns the project name. - * - * @return The project name, if any. - */ - public static String getProject() { - return project; - } + /** + * Returns the pre-release version. + * + * @return The pre-release version, if any. + */ + public static String getPreRelease() { + if (prerelease.length() > 0) { + return '-' + prerelease; + } - /** - * Returns the build metadata. - * - * @return The build metadata, if any. - */ - public static String getBuildMetadata() { - if (buildmeta.length() > 0) { - return '+' + buildmeta; - } + return ""; + } - return ""; - } - - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException if the constructor is called. - */ - private ReleaseInfo() - throws UnsupportedOperationException { - throw new UnsupportedOperationException("Illegal constructor call."); - } + /** + * Returns the build metadata. + * + * @return The build metadata, if any. + */ + public static String getBuildMetadata() { + if (buildmeta.length() > 0) { + return '+' + buildmeta; + } + + return ""; + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 0cf5fbf..781edc4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -31,11 +31,11 @@ */ package net.thauvin.erik.mobibot; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.Namespace; -import org.jdom.input.SAXBuilder; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.input.SAXBuilder; import java.io.IOException; import java.net.URL; diff --git a/src/main/java/net/thauvin/erik/mobibot/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/Lookup.java index 560f90a..632184e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/Lookup.java @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot; -import org.apache.commons.net.WhoisClient; +import org.apache.commons.net.whois.WhoisClient; import java.io.IOException; import java.net.InetAddress; @@ -155,4 +155,4 @@ final class Lookup return lines; } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 2605827..9310a7f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -394,13 +394,12 @@ public class Mobibot extends PircBot Commands.DEBUG_ARG, false, "print debug & logging data directly to the console"); - //noinspection AccessStaticViaInstance - options.addOption(OptionBuilder.withArgName("file").hasArg().withDescription("use alternate properties file") - .withLongOpt(Commands.PROPS_ARG).create(Commands.PROPS_ARG.substring(0, 1))); + options.addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file") + .desc("use alternate properties file").longOpt(Commands.PROPS_ARG).build()); options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); // Parse the command line - final CommandLineParser parser = new PosixParser(); + final CommandLineParser parser = new DefaultParser(); CommandLine line = null; try diff --git a/version.properties b/version.properties index 3349c49..d986a57 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=6 version.patch=1 version.prerelease=beta -version.buildmeta=002 +version.buildmeta=003 From a048fec8a0a4a5045182f097855ba3d32c14120a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 5 Feb 2016 16:02:51 -0800 Subject: [PATCH 030/842] Updated/moved license. --- LICENSE.TXT => licenses/LICENSE.TXT | 0 licenses/License.txt | 44 ++++++++++++++--------------- 2 files changed, 21 insertions(+), 23 deletions(-) rename LICENSE.TXT => licenses/LICENSE.TXT (100%) diff --git a/LICENSE.TXT b/licenses/LICENSE.TXT similarity index 100% rename from LICENSE.TXT rename to licenses/LICENSE.TXT diff --git a/licenses/License.txt b/licenses/License.txt index 68001fc..4d3d187 100644 --- a/licenses/License.txt +++ b/licenses/License.txt @@ -1,29 +1,27 @@ -Mobibot License - -Copyright (c) 2004-2014, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -Neither the name of the author nor the names of its contributors may be used to -endorse or promote products derived from this software without specific prior -written permission. +* Neither the name of this project nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From 77f2f5c218b7ebc03d526ef3ad3c1c07e5ddff1a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 25 Jun 2016 12:37:51 -0700 Subject: [PATCH 031/842] Updated libraries. --- build.gradle | 8 +- gradle/wrapper/gradle-wrapper.properties | 4 +- licenses/{LICENSE.TXT => LICENSE.txt} | 0 mobibot.iml | 205 -------- mobibot.ipr | 438 +----------------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- version.properties | 2 +- 7 files changed, 13 insertions(+), 648 deletions(-) rename licenses/{LICENSE.TXT => LICENSE.txt} (100%) diff --git a/build.gradle b/build.gradle index 168d333..7e06eaf 100644 --- a/build.gradle +++ b/build.gradle @@ -61,7 +61,7 @@ dependencies { compile 'commons-codec:commons-codec:1.10' compile 'commons-logging:commons-logging:1.2' - compile 'commons-net:commons-net:3.4' + compile 'commons-net:commons-net:3.5' compile 'commons-cli:commons-cli:1.3.1' compile 'commons-httpclient:commons-httpclient:3.1' @@ -69,14 +69,14 @@ dependencies { compile 'org.jdom:jdom:1.1.3' compile 'org.jdom:jdom2:2.0.6' - compile 'org.jsoup:jsoup:1.8.3' + compile 'org.jsoup:jsoup:1.9.2' compile 'rome:rome:1.0@jar' compile 'rome:rome-fetcher:1.0@jar' - compile 'org.json:json:20151123' + compile 'org.json:json:20160212' compile 'org.ostermiller:utils:1.07.00' compile 'net.sourceforge.jweather:jweather:0.3.0@jar' - compile 'net.objecthunter:exp4j:0.4.5' + compile 'net.objecthunter:exp4j:0.4.7' compile 'org.twitter4j:twitter4j-core:4.0.4' compile 'net.sf.delicious-java:delicious:1.14' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3b69f79..aad1c32 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jan 25 16:37:02 PST 2016 +#Sat Jun 25 11:57:02 PDT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/licenses/LICENSE.TXT b/licenses/LICENSE.txt similarity index 100% rename from licenses/LICENSE.TXT rename to licenses/LICENSE.txt diff --git a/mobibot.iml b/mobibot.iml index 58ff58e..f60ef91 100644 --- a/mobibot.iml +++ b/mobibot.iml @@ -39,209 +39,4 @@ <orderEntry type="library" name="Gradle: org.json:json:20151123" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> </component> - <component name="org.twodividedbyzero.idea.findbugs"> - <option name="_basePreferences"> - <map> - <entry key="property.analysisEffortLevel" value="default" /> - <entry key="property.analyzeAfterAutoMake" value="false" /> - <entry key="property.analyzeAfterCompile" value="false" /> - <entry key="property.annotationGutterIconEnabled" value="true" /> - <entry key="property.annotationSuppressWarningsClass" value="edu.umd.cs.findbugs.annotations.SuppressFBWarnings" /> - <entry key="property.annotationTextRangeMarkupEnabled" value="true" /> - <entry key="property.exportAsHtml" value="true" /> - <entry key="property.exportAsXml" value="true" /> - <entry key="property.exportBaseDir" value="" /> - <entry key="property.exportCreateArchiveDir" value="false" /> - <entry key="property.exportOpenBrowser" value="true" /> - <entry key="property.minPriorityToReport" value="Medium" /> - <entry key="property.runAnalysisInBackground" value="false" /> - <entry key="property.showHiddenDetectors" value="false" /> - <entry key="property.toolWindowToFront" value="true" /> - </map> - </option> - <option name="_detectors"> - <map> - <entry key="AppendingToAnObjectOutputStream" value="true" /> - <entry key="AtomicityProblem" value="true" /> - <entry key="BadAppletConstructor" value="false" /> - <entry key="BadResultSetAccess" value="true" /> - <entry key="BadSyntaxForRegularExpression" value="true" /> - <entry key="BadUseOfReturnValue" value="true" /> - <entry key="BadlyOverriddenAdapter" value="true" /> - <entry key="BooleanReturnNull" value="true" /> - <entry key="BuildInterproceduralCallGraph" value="false" /> - <entry key="BuildObligationPolicyDatabase" value="true" /> - <entry key="BuildStringPassthruGraph" value="true" /> - <entry key="CallToUnsupportedMethod" value="false" /> - <entry key="CalledMethods" value="true" /> - <entry key="CheckCalls" value="false" /> - <entry key="CheckExpectedWarnings" value="false" /> - <entry key="CheckImmutableAnnotation" value="true" /> - <entry key="CheckRelaxingNullnessAnnotation" value="true" /> - <entry key="CheckTypeQualifiers" value="true" /> - <entry key="CloneIdiom" value="true" /> - <entry key="ComparatorIdiom" value="true" /> - <entry key="ConfusedInheritance" value="true" /> - <entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" /> - <entry key="CovariantArrayAssignment" value="false" /> - <entry key="CrossSiteScripting" value="true" /> - <entry key="DefaultEncodingDetector" value="true" /> - <entry key="DoInsideDoPrivileged" value="true" /> - <entry key="DontCatchIllegalMonitorStateException" value="true" /> - <entry key="DontIgnoreResultOfPutIfAbsent" value="true" /> - <entry key="DontUseEnum" value="true" /> - <entry key="DroppedException" value="true" /> - <entry key="DumbMethodInvocations" value="true" /> - <entry key="DumbMethods" value="true" /> - <entry key="DuplicateBranches" value="true" /> - <entry key="EmptyZipFileEntry" value="false" /> - <entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" /> - <entry key="ExplicitSerialization" value="true" /> - <entry key="FieldItemSummary" value="true" /> - <entry key="FinalizerNullsFields" value="true" /> - <entry key="FindBadCast2" value="true" /> - <entry key="FindBadForLoop" value="true" /> - <entry key="FindBugsSummaryStats" value="true" /> - <entry key="FindCircularDependencies" value="false" /> - <entry key="FindComparatorProblems" value="true" /> - <entry key="FindDeadLocalStores" value="true" /> - <entry key="FindDoubleCheck" value="true" /> - <entry key="FindEmptySynchronizedBlock" value="true" /> - <entry key="FindFieldSelfAssignment" value="true" /> - <entry key="FindFinalizeInvocations" value="true" /> - <entry key="FindFloatEquality" value="true" /> - <entry key="FindFloatMath" value="false" /> - <entry key="FindHEmismatch" value="true" /> - <entry key="FindInconsistentSync2" value="true" /> - <entry key="FindJSR166LockMonitorenter" value="true" /> - <entry key="FindLocalSelfAssignment2" value="true" /> - <entry key="FindMaskedFields" value="true" /> - <entry key="FindMismatchedWaitOrNotify" value="true" /> - <entry key="FindNakedNotify" value="true" /> - <entry key="FindNoSideEffectMethods" value="true" /> - <entry key="FindNonSerializableStoreIntoSession" value="false" /> - <entry key="FindNonSerializableValuePassedToWriteObject" value="false" /> - <entry key="FindNonShortCircuit" value="true" /> - <entry key="FindNullDeref" value="true" /> - <entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" /> - <entry key="FindOpenStream" value="true" /> - <entry key="FindPuzzlers" value="true" /> - <entry key="FindRefComparison" value="true" /> - <entry key="FindReturnRef" value="true" /> - <entry key="FindRoughConstants" value="true" /> - <entry key="FindRunInvocations" value="true" /> - <entry key="FindSelfComparison" value="true" /> - <entry key="FindSelfComparison2" value="true" /> - <entry key="FindSleepWithLockHeld" value="true" /> - <entry key="FindSpinLoop" value="true" /> - <entry key="FindSqlInjection" value="true" /> - <entry key="FindTwoLockWait" value="true" /> - <entry key="FindUncalledPrivateMethods" value="true" /> - <entry key="FindUnconditionalWait" value="true" /> - <entry key="FindUninitializedGet" value="true" /> - <entry key="FindUnrelatedTypesInGenericContainer" value="true" /> - <entry key="FindUnreleasedLock" value="true" /> - <entry key="FindUnsatisfiedObligation" value="true" /> - <entry key="FindUnsyncGet" value="true" /> - <entry key="FindUseOfNonSerializableValue" value="true" /> - <entry key="FindUselessControlFlow" value="true" /> - <entry key="FindUselessObjects" value="true" /> - <entry key="FormatStringChecker" value="true" /> - <entry key="FunctionsThatMightBeMistakenForProcedures" value="true" /> - <entry key="HugeSharedStringConstants" value="true" /> - <entry key="IDivResultCastToDouble" value="true" /> - <entry key="IncompatMask" value="true" /> - <entry key="InconsistentAnnotations" value="true" /> - <entry key="InefficientIndexOf" value="false" /> - <entry key="InefficientInitializationInsideLoop" value="false" /> - <entry key="InefficientMemberAccess" value="false" /> - <entry key="InefficientToArray" value="false" /> - <entry key="InfiniteLoop" value="true" /> - <entry key="InfiniteRecursiveLoop" value="true" /> - <entry key="InheritanceUnsafeGetResource" value="true" /> - <entry key="InitializationChain" value="true" /> - <entry key="InitializeNonnullFieldsInConstructor" value="true" /> - <entry key="InstantiateStaticClass" value="true" /> - <entry key="IntCast2LongAsInstant" value="true" /> - <entry key="InvalidJUnitTest" value="true" /> - <entry key="IteratorIdioms" value="true" /> - <entry key="LazyInit" value="true" /> - <entry key="LoadOfKnownNullValue" value="true" /> - <entry key="LostLoggerDueToWeakReference" value="true" /> - <entry key="MethodReturnCheck" value="true" /> - <entry key="Methods" value="true" /> - <entry key="MultithreadedInstanceAccess" value="true" /> - <entry key="MutableEnum" value="true" /> - <entry key="MutableLock" value="true" /> - <entry key="MutableStaticFields" value="true" /> - <entry key="Naming" value="true" /> - <entry key="Noise" value="false" /> - <entry key="NoiseNullDeref" value="false" /> - <entry key="NoteAnnotationRetention" value="true" /> - <entry key="NoteCheckReturnValueAnnotations" value="true" /> - <entry key="NoteDirectlyRelevantTypeQualifiers" value="true" /> - <entry key="NoteJCIPAnnotation" value="true" /> - <entry key="NoteNonNullAnnotations" value="false" /> - <entry key="NoteNonnullReturnValues" value="false" /> - <entry key="NoteSuppressedWarnings" value="true" /> - <entry key="NoteUnconditionalParamDerefs" value="true" /> - <entry key="NumberConstructor" value="true" /> - <entry key="OptionalReturnNull" value="true" /> - <entry key="OverridingEqualsNotSymmetrical" value="true" /> - <entry key="PreferZeroLengthArrays" value="true" /> - <entry key="PublicSemaphores" value="false" /> - <entry key="QuestionableBooleanAssignment" value="true" /> - <entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" /> - <entry key="ReadReturnShouldBeChecked" value="true" /> - <entry key="RedundantConditions" value="true" /> - <entry key="RedundantInterfaces" value="true" /> - <entry key="ReflectiveClasses" value="true" /> - <entry key="RepeatedConditionals" value="true" /> - <entry key="ResolveAllReferences" value="false" /> - <entry key="RuntimeExceptionCapture" value="true" /> - <entry key="SerializableIdiom" value="true" /> - <entry key="StartInConstructor" value="true" /> - <entry key="StaticCalendarDetector" value="true" /> - <entry key="StringConcatenation" value="true" /> - <entry key="SuperfluousInstanceOf" value="true" /> - <entry key="SuspiciousThreadInterrupted" value="true" /> - <entry key="SwitchFallthrough" value="true" /> - <entry key="SynchronizationOnSharedBuiltinConstant" value="true" /> - <entry key="SynchronizeAndNullCheckField" value="true" /> - <entry key="SynchronizeOnClassLiteralNotGetClass" value="true" /> - <entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" /> - <entry key="TestASM" value="false" /> - <entry key="TestDataflowAnalysis" value="false" /> - <entry key="TestingGround" value="false" /> - <entry key="TestingGround2" value="false" /> - <entry key="TrainFieldStoreTypes" value="true" /> - <entry key="TrainLongInstantfParams" value="true" /> - <entry key="TrainNonNullAnnotations" value="true" /> - <entry key="TrainUnconditionalDerefParams" value="true" /> - <entry key="URLProblems" value="true" /> - <entry key="UncallableMethodOfAnonymousClass" value="true" /> - <entry key="UnnecessaryMath" value="true" /> - <entry key="UnreadFields" value="true" /> - <entry key="UselessSubclassMethod" value="false" /> - <entry key="VarArgsProblems" value="true" /> - <entry key="VolatileUsage" value="true" /> - <entry key="WaitInLoop" value="true" /> - <entry key="WrongMapIterator" value="true" /> - <entry key="XMLFactoryBypass" value="true" /> - </map> - </option> - <option name="_reportCategories"> - <map> - <entry key="BAD_PRACTICE" value="true" /> - <entry key="CORRECTNESS" value="true" /> - <entry key="EXPERIMENTAL" value="true" /> - <entry key="I18N" value="true" /> - <entry key="MALICIOUS_CODE" value="true" /> - <entry key="MT_CORRECTNESS" value="true" /> - <entry key="PERFORMANCE" value="true" /> - <entry key="SECURITY" value="true" /> - <entry key="STYLE" value="true" /> - </map> - </option> - </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 5e6f7be..e9e1bc1 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -121,19 +121,9 @@ <option name="linkedExternalProjectsSettings"> <GradleProjectSettings> <option name="createEmptyContentRootDirectories" value="true" /> - <option name="distributionType" value="LOCAL" /> + <option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="gradleHome" value="C:/gradle" /> - <option name="modules"> - <set> - <option value="$PROJECT_DIR$" /> - </set> - </option> - <option name="myModules"> - <set> - <option value="$PROJECT_DIR$" /> - </set> - </option> </GradleProjectSettings> </option> </component> @@ -205,6 +195,9 @@ <option name="FILTER_INFO" value="true" /> <option name="CUSTOM_FILTER" /> </component> + <component name="PDMPlugin"> + <option name="skipTestSources" value="false" /> + </component> <component name="PEExternalization"> <option name="IGNORE_METHOD_NAMES" value="" /> <option name="IGNORE_METHOD_NAMES_ENABLED" value="false" /> @@ -399,427 +392,4 @@ </value> </option> </component> - <component name="libraryTable"> - <library name="Gradle: commons-cli:commons-cli:1.3.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/1303efbc4b181e5a58bf2e967dc156a3132b97c0/commons-cli-1.3.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/12ec02d8cb9fbb33bd05506109a4fc8bcc3578/commons-cli-1.3.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-codec:commons-codec:1.10"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/4b95f4897fa13f2cd904aee711aeafc0c5295cd8/commons-codec-1.10.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/11fb3d88ae7e3b757d70237064210ceb954a5a04/commons-codec-1.10-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-collections:commons-collections:3.2.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/761ea405b9b37ced573d2df0d1e3a4e0f9edc668/commons-collections-3.2.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/fa095ef874374e5b2a11f8b06c26a5d68c7cb3a4/commons-collections-3.2.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-lang:commons-lang:2.4"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/16313e02a793435009f1e458fa4af5d879f6fb11/commons-lang-2.4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/2b8c4b3035e45520ef42033e823c7d33e4b4402c/commons-lang-2.4-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-logging:commons-logging:1.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/ecf26c7507d67782a3bbd148d170b31dfad001aa/commons-logging-1.2-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-net:commons-net:3.4"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.4/5e984db9554728564d58e90da5d90eff8ae8cf2d/commons-net-3.4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.4/8439b8f20d7bdec502423732798a639501732c8/commons-net-3.4-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: log4j:log4j:1.2.17"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.objecthunter:exp4j:0.4.5"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.5/c6876761bb3816c603350f39b22023ee236430be/exp4j-0.4.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.5/977535d7083d551378ff8ba8e5d2c9ddf5f0c455/exp4j-0.4.5-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.sf.delicious-java:delicious:1.14"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.sourceforge.jweather:jweather:0.3.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.thauvin.erik:semver:0.9.5-beta"> - <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.apache.velocity:velocity:1.7"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/2ceb567b8f3f21118ecdec129fe1271dbc09aa7a/velocity-1.7.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/eb11eb70171ed64842b2e5216d5904e21ed162ac/velocity-1.7-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jdom:jdom:2.0.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/d06c71e0df0ac4b94deb737718580ccce22d92e8/jdom-2.0.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom/2.0.2/2c0614e33230394e42f5cfe55c20d5ae8949c108/jdom-2.0.2-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.json:json:20151123"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20151123/1675d5c1142000dedd6627a73af26a8b885ef0e1/json-20151123.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20151123/9c179a1becb79c6af729c89f7b212b1cb5e68da3/json-20151123-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jsoup:jsoup:1.8.3"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.8.3/65fd012581ded67bc20945d85c32b4598c3a9cf1/jsoup-1.8.3.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.8.3/17b6b6cb133d73e13e2e1f3550f90d4e23451b10/jsoup-1.8.3-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.ostermiller:utils:1.07.00"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.twitter4j:twitter4j-core:4.0.4"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/1f3c896c7b2f20c51103078ccf0bc2ea97ac012a/twitter4j-core-4.0.4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/5fb9d7d5700c56e9a5202a95a2fd6c2159cb33b7/twitter4j-core-4.0.4-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: oro:oro:2.0.8"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: pircbot:pircbot:1.5.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: rome:rome-fetcher:1.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/6044bcd5d6f793fa3a38843e774e58c0737a7125/rome-fetcher-1.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome-fetcher/1.0/a36d7419a32690c5f47a625691c5490d67b72d4e/rome-fetcher-1.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: rome:rome:1.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/22b33347f315833e9348cec2751af1a5d5656e4/rome-1.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/rome/rome/1.0/195e9a962672c32943ec8883e010b6a5ea568745/rome-1.0-sources.jar!/" /> - </SOURCES> - </library> - </component> - <component name="org.twodividedbyzero.idea.findbugs"> - <option name="annotationTypeSettings"> - <map> - <entry key="ExpPriority" value="-4473925;-12828863;-8355712;WAVE_UNDERSCORE;0;" /> - <entry key="HighPriority" value="-39836;-12828863;-39836;WAVE_UNDERSCORE;1;" /> - <entry key="IgnorePriority" value="-4473925;-12828863;-11978414;WAVE_UNDERSCORE;0;" /> - <entry key="LowPriority" value="-4473925;-12828863;-10316203;BOXED;0;" /> - <entry key="NormalPriority" value="-4473925;-12828863;-10461184;WAVE_UNDERSCORE;2;" /> - </map> - </option> - <option name="_basePreferences"> - <map> - <entry key="property.analysisEffortLevel" value="default" /> - <entry key="property.analyzeAfterAutoMake" value="false" /> - <entry key="property.analyzeAfterCompile" value="false" /> - <entry key="property.annotationGutterIconEnabled" value="true" /> - <entry key="property.annotationSuppressWarningsClass" value="edu.umd.cs.findbugs.annotations.SuppressFBWarnings" /> - <entry key="property.annotationTextRangeMarkupEnabled" value="true" /> - <entry key="property.exportAsHtml" value="true" /> - <entry key="property.exportAsXml" value="true" /> - <entry key="property.exportBaseDir" value="" /> - <entry key="property.exportCreateArchiveDir" value="false" /> - <entry key="property.exportOpenBrowser" value="true" /> - <entry key="property.minPriorityToReport" value="Medium" /> - <entry key="property.runAnalysisInBackground" value="false" /> - <entry key="property.showHiddenDetectors" value="false" /> - <entry key="property.toolWindowToFront" value="true" /> - </map> - </option> - <option name="_detectors"> - <map> - <entry key="AppendingToAnObjectOutputStream" value="true" /> - <entry key="AtomicityProblem" value="true" /> - <entry key="BadAppletConstructor" value="false" /> - <entry key="BadResultSetAccess" value="true" /> - <entry key="BadSyntaxForRegularExpression" value="true" /> - <entry key="BadUseOfReturnValue" value="true" /> - <entry key="BadlyOverriddenAdapter" value="true" /> - <entry key="BooleanReturnNull" value="true" /> - <entry key="BuildInterproceduralCallGraph" value="false" /> - <entry key="BuildObligationPolicyDatabase" value="true" /> - <entry key="BuildStringPassthruGraph" value="true" /> - <entry key="CallToUnsupportedMethod" value="false" /> - <entry key="CalledMethods" value="true" /> - <entry key="CheckCalls" value="false" /> - <entry key="CheckExpectedWarnings" value="false" /> - <entry key="CheckImmutableAnnotation" value="true" /> - <entry key="CheckRelaxingNullnessAnnotation" value="true" /> - <entry key="CheckTypeQualifiers" value="true" /> - <entry key="CloneIdiom" value="true" /> - <entry key="ComparatorIdiom" value="true" /> - <entry key="ConfusedInheritance" value="true" /> - <entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" /> - <entry key="CovariantArrayAssignment" value="false" /> - <entry key="CrossSiteScripting" value="true" /> - <entry key="DefaultEncodingDetector" value="true" /> - <entry key="DoInsideDoPrivileged" value="true" /> - <entry key="DontCatchIllegalMonitorStateException" value="true" /> - <entry key="DontIgnoreResultOfPutIfAbsent" value="true" /> - <entry key="DontUseEnum" value="true" /> - <entry key="DroppedException" value="true" /> - <entry key="DumbMethodInvocations" value="true" /> - <entry key="DumbMethods" value="true" /> - <entry key="DuplicateBranches" value="true" /> - <entry key="EmptyZipFileEntry" value="false" /> - <entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" /> - <entry key="ExplicitSerialization" value="true" /> - <entry key="FieldItemSummary" value="true" /> - <entry key="FinalizerNullsFields" value="true" /> - <entry key="FindBadCast2" value="true" /> - <entry key="FindBadForLoop" value="true" /> - <entry key="FindBugsSummaryStats" value="true" /> - <entry key="FindCircularDependencies" value="false" /> - <entry key="FindComparatorProblems" value="true" /> - <entry key="FindDeadLocalStores" value="true" /> - <entry key="FindDoubleCheck" value="true" /> - <entry key="FindEmptySynchronizedBlock" value="true" /> - <entry key="FindFieldSelfAssignment" value="true" /> - <entry key="FindFinalizeInvocations" value="true" /> - <entry key="FindFloatEquality" value="true" /> - <entry key="FindFloatMath" value="false" /> - <entry key="FindHEmismatch" value="true" /> - <entry key="FindInconsistentSync2" value="true" /> - <entry key="FindJSR166LockMonitorenter" value="true" /> - <entry key="FindLocalSelfAssignment2" value="true" /> - <entry key="FindMaskedFields" value="true" /> - <entry key="FindMismatchedWaitOrNotify" value="true" /> - <entry key="FindNakedNotify" value="true" /> - <entry key="FindNoSideEffectMethods" value="true" /> - <entry key="FindNonSerializableStoreIntoSession" value="false" /> - <entry key="FindNonSerializableValuePassedToWriteObject" value="false" /> - <entry key="FindNonShortCircuit" value="true" /> - <entry key="FindNullDeref" value="true" /> - <entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" /> - <entry key="FindOpenStream" value="true" /> - <entry key="FindPuzzlers" value="true" /> - <entry key="FindRefComparison" value="true" /> - <entry key="FindReturnRef" value="true" /> - <entry key="FindRoughConstants" value="true" /> - <entry key="FindRunInvocations" value="true" /> - <entry key="FindSelfComparison" value="true" /> - <entry key="FindSelfComparison2" value="true" /> - <entry key="FindSleepWithLockHeld" value="true" /> - <entry key="FindSpinLoop" value="true" /> - <entry key="FindSqlInjection" value="true" /> - <entry key="FindTwoLockWait" value="true" /> - <entry key="FindUncalledPrivateMethods" value="true" /> - <entry key="FindUnconditionalWait" value="true" /> - <entry key="FindUninitializedGet" value="true" /> - <entry key="FindUnrelatedTypesInGenericContainer" value="true" /> - <entry key="FindUnreleasedLock" value="true" /> - <entry key="FindUnsatisfiedObligation" value="true" /> - <entry key="FindUnsyncGet" value="true" /> - <entry key="FindUseOfNonSerializableValue" value="true" /> - <entry key="FindUselessControlFlow" value="true" /> - <entry key="FindUselessObjects" value="true" /> - <entry key="FormatStringChecker" value="true" /> - <entry key="FunctionsThatMightBeMistakenForProcedures" value="true" /> - <entry key="HugeSharedStringConstants" value="true" /> - <entry key="IDivResultCastToDouble" value="true" /> - <entry key="IncompatMask" value="true" /> - <entry key="InconsistentAnnotations" value="true" /> - <entry key="InefficientIndexOf" value="false" /> - <entry key="InefficientInitializationInsideLoop" value="false" /> - <entry key="InefficientMemberAccess" value="false" /> - <entry key="InefficientToArray" value="false" /> - <entry key="InfiniteLoop" value="true" /> - <entry key="InfiniteRecursiveLoop" value="true" /> - <entry key="InheritanceUnsafeGetResource" value="true" /> - <entry key="InitializationChain" value="true" /> - <entry key="InitializeNonnullFieldsInConstructor" value="true" /> - <entry key="InstantiateStaticClass" value="true" /> - <entry key="IntCast2LongAsInstant" value="true" /> - <entry key="InvalidJUnitTest" value="true" /> - <entry key="IteratorIdioms" value="true" /> - <entry key="LazyInit" value="true" /> - <entry key="LoadOfKnownNullValue" value="true" /> - <entry key="LostLoggerDueToWeakReference" value="true" /> - <entry key="MethodReturnCheck" value="true" /> - <entry key="Methods" value="true" /> - <entry key="MultithreadedInstanceAccess" value="true" /> - <entry key="MutableEnum" value="true" /> - <entry key="MutableLock" value="true" /> - <entry key="MutableStaticFields" value="true" /> - <entry key="Naming" value="true" /> - <entry key="Noise" value="false" /> - <entry key="NoiseNullDeref" value="false" /> - <entry key="NoteAnnotationRetention" value="true" /> - <entry key="NoteCheckReturnValueAnnotations" value="true" /> - <entry key="NoteDirectlyRelevantTypeQualifiers" value="true" /> - <entry key="NoteJCIPAnnotation" value="true" /> - <entry key="NoteNonNullAnnotations" value="false" /> - <entry key="NoteNonnullReturnValues" value="false" /> - <entry key="NoteSuppressedWarnings" value="true" /> - <entry key="NoteUnconditionalParamDerefs" value="true" /> - <entry key="NumberConstructor" value="true" /> - <entry key="OptionalReturnNull" value="true" /> - <entry key="OverridingEqualsNotSymmetrical" value="true" /> - <entry key="PreferZeroLengthArrays" value="true" /> - <entry key="PublicSemaphores" value="false" /> - <entry key="QuestionableBooleanAssignment" value="true" /> - <entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" /> - <entry key="ReadReturnShouldBeChecked" value="true" /> - <entry key="RedundantConditions" value="true" /> - <entry key="RedundantInterfaces" value="true" /> - <entry key="ReflectiveClasses" value="true" /> - <entry key="RepeatedConditionals" value="true" /> - <entry key="ResolveAllReferences" value="false" /> - <entry key="RuntimeExceptionCapture" value="true" /> - <entry key="SerializableIdiom" value="true" /> - <entry key="StartInConstructor" value="true" /> - <entry key="StaticCalendarDetector" value="true" /> - <entry key="StringConcatenation" value="true" /> - <entry key="SuperfluousInstanceOf" value="true" /> - <entry key="SuspiciousThreadInterrupted" value="true" /> - <entry key="SwitchFallthrough" value="true" /> - <entry key="SynchronizationOnSharedBuiltinConstant" value="true" /> - <entry key="SynchronizeAndNullCheckField" value="true" /> - <entry key="SynchronizeOnClassLiteralNotGetClass" value="true" /> - <entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" /> - <entry key="TestASM" value="false" /> - <entry key="TestDataflowAnalysis" value="false" /> - <entry key="TestingGround" value="false" /> - <entry key="TestingGround2" value="false" /> - <entry key="TrainFieldStoreTypes" value="true" /> - <entry key="TrainLongInstantfParams" value="true" /> - <entry key="TrainNonNullAnnotations" value="true" /> - <entry key="TrainUnconditionalDerefParams" value="true" /> - <entry key="URLProblems" value="true" /> - <entry key="UncallableMethodOfAnonymousClass" value="true" /> - <entry key="UnnecessaryMath" value="true" /> - <entry key="UnreadFields" value="true" /> - <entry key="UselessSubclassMethod" value="false" /> - <entry key="VarArgsProblems" value="true" /> - <entry key="VolatileUsage" value="true" /> - <entry key="WaitInLoop" value="true" /> - <entry key="WrongMapIterator" value="true" /> - <entry key="XMLFactoryBypass" value="true" /> - </map> - </option> - <option name="_reportCategories"> - <map> - <entry key="BAD_PRACTICE" value="true" /> - <entry key="CORRECTNESS" value="true" /> - <entry key="EXPERIMENTAL" value="true" /> - <entry key="I18N" value="true" /> - <entry key="MALICIOUS_CODE" value="true" /> - <entry key="MT_CORRECTNESS" value="true" /> - <entry key="PERFORMANCE" value="true" /> - <entry key="SECURITY" value="true" /> - <entry key="STYLE" value="true" /> - </map> - </option> - <option name="_annotationTypeSettings"> - <map> - <entry key="ExpPriority" value="-4473925;-12828863;-8355712;WAVE_UNDERSCORE;0;" /> - <entry key="HighPriority" value="-39836;-12828863;-39836;WAVE_UNDERSCORE;1;" /> - <entry key="IgnorePriority" value="-4473925;-12828863;-11978414;WAVE_UNDERSCORE;0;" /> - <entry key="LowPriority" value="-4473925;-12828863;-10316203;BOXED;0;" /> - <entry key="NormalPriority" value="-4473925;-12828863;-10461184;WAVE_UNDERSCORE;2;" /> - </map> - </option> - </component> </project> \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index e3e8542..5bc23df 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "003"; - private final static Date date = new Date(1454706693276L); + private final static String buildmeta = "004"; + private final static Date date = new Date(1466883211201L); private final static int major = 0; private final static int minor = 6; private final static int patch = 1; diff --git a/version.properties b/version.properties index d986a57..27d832c 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=6 version.patch=1 version.prerelease=beta -version.buildmeta=003 +version.buildmeta=004 From 62c011234e727149165c4f7657a60c6a150beff0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jun 2016 18:09:42 -0700 Subject: [PATCH 032/842] Upgraded to ROME 1.6.1 Added run arguments to build.gradle --- .idea/modules/mobibot.iml | 12 + .idea/modules/mobibot_annotationProcessor.iml | 15 ++ .idea/modules/mobibot_main.iml | 35 +++ .idea/modules/mobibot_test.iml | 37 +++ build.gradle | 30 ++- mobibot.iml | 42 ---- mobibot.ipr | 219 +++++++++++++++++- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 15 +- .../net/thauvin/erik/mobibot/EntryLink.java | 15 +- .../net/thauvin/erik/mobibot/FeedReader.java | 16 +- .../net/thauvin/erik/mobibot/Mobibot.java | 17 +- version.properties | 2 +- 13 files changed, 362 insertions(+), 97 deletions(-) create mode 100644 .idea/modules/mobibot.iml create mode 100644 .idea/modules/mobibot_annotationProcessor.iml create mode 100644 .idea/modules/mobibot_main.iml create mode 100644 .idea/modules/mobibot_test.iml delete mode 100644 mobibot.iml diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml new file mode 100644 index 0000000..ca3a54c --- /dev/null +++ b/.idea/modules/mobibot.iml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$/../.."> + <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> + <excludeFolder url="file://$MODULE_DIR$/../../build" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/modules/mobibot_annotationProcessor.iml b/.idea/modules/mobibot_annotationProcessor.iml new file mode 100644 index 0000000..599f09a --- /dev/null +++ b/.idea/modules/mobibot_annotationProcessor.iml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="mobibot:annotationProcessor" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/classes/annotationProcessor" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../../src/annotationProcessor"> + <sourceFolder url="file://$MODULE_DIR$/../../src/annotationProcessor/resources" type="java-resource" /> + </content> + <content url="file://$MODULE_DIR$/../../src/generated"> + <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/modules/mobibot_main.iml b/.idea/modules/mobibot_main.iml new file mode 100644 index 0000000..7d47b66 --- /dev/null +++ b/.idea/modules/mobibot_main.iml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/classes/main" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../../src/main"> + <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.5" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> + <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-fetcher:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> + <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> + <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> + <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> + <orderEntry type="library" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/modules/mobibot_test.iml b/.idea/modules/mobibot_test.iml new file mode 100644 index 0000000..2f8372f --- /dev/null +++ b/.idea/modules/mobibot_test.iml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> + <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../../src/test"> + <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="mobibot_main" /> + <orderEntry type="library" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.5" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> + <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-fetcher:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> + <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> + <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> + <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> + <orderEntry type="library" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + </component> + <component name="TestModuleProperties" production-module="mobibot_main" /> +</module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 7e06eaf..128d696 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,17 @@ buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.coders-kitchen:compileonlyplugin:1.0.0' - } + repositories { + mavenCentral() + } + dependencies { + classpath 'com.coders-kitchen:compileonlyplugin:1.0.0' + } } + plugins { id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" id "com.github.ben-manes.versions" version "0.12.0" } + apply plugin: 'java' apply plugin: 'idea' apply plugin: 'application' @@ -21,14 +23,16 @@ def packageName = 'net.thauvin.erik.mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -def getVersion(isIncrement = false) { +def getVersion(isIncrement = false) +{ def propsFile = 'version.properties' def majorKey = 'version.major' def minorKey = 'version.minor' def patchKey = 'version.patch' def metaKey = 'version.buildmeta' def preKey = 'version.prerelease' - if (isIncrement) { + if (isIncrement) + { ant.propertyfile(file: propsFile) { entry(key: patchKey, type: 'int', @@ -67,11 +71,8 @@ dependencies { compile 'oro:oro:2.0.8' - compile 'org.jdom:jdom:1.1.3' - compile 'org.jdom:jdom2:2.0.6' compile 'org.jsoup:jsoup:1.9.2' - compile 'rome:rome:1.0@jar' - compile 'rome:rome-fetcher:1.0@jar' + compile 'com.rometools:rome:1.6.1' compile 'org.json:json:20160212' compile 'org.ostermiller:utils:1.07.00' @@ -109,6 +110,10 @@ clean { delete deployDir } +run { + args '--v' +} + task wrapper(type: Wrapper) { gradleVersion = gradle.gradleVersion } @@ -138,6 +143,7 @@ task deploy(dependsOn: ['build']) { mustRunAfter clean } + task release(dependsOn: ['deploy', 'wrapper']) << { group = 'Publishing' description = 'Releases new version.' diff --git a/mobibot.iml b/mobibot.iml deleted file mode 100644 index f60ef91..0000000 --- a/mobibot.iml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1-beta+002" relativePaths="false" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="false"> - <output url="file://$MODULE_DIR$/build/classes/main" /> - <output-test url="file://$MODULE_DIR$/build/classes/test" /> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/src/generated/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/annotationProcessor/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" /> - <excludeFolder url="file://$MODULE_DIR$/.gradle" /> - <excludeFolder url="file://$MODULE_DIR$/build" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" exported="" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" exported="" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" exported="" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" exported="" name="Gradle: rome:rome:1.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: rome:rome-fetcher:1.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" exported="" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" exported="" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.8.3" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.5" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> - <orderEntry type="library" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom:2.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20151123" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> - </component> -</module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index e9e1bc1..c8fa3e0 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -69,6 +69,11 @@ <processorPath useClasspath="true" /> </profile> </annotationProcessing> + <bytecodeTargetLevel> + <module name="mobibot_annotationProcessor" target="1.8" /> + <module name="mobibot_main" target="1.8" /> + <module name="mobibot_test" target="1.8" /> + </bytecodeTargetLevel> </component> <component name="CopyrightManager" default="Mobibot"> <copyright> @@ -121,9 +126,14 @@ <option name="linkedExternalProjectsSettings"> <GradleProjectSettings> <option name="createEmptyContentRootDirectories" value="true" /> - <option name="distributionType" value="DEFAULT_WRAPPED" /> + <option name="distributionType" value="LOCAL" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="gradleHome" value="C:/gradle" /> + <option name="modules"> + <set> + <option value="$PROJECT_DIR$" /> + </set> + </option> </GradleProjectSettings> </option> </component> @@ -364,10 +374,13 @@ </component> <component name="ProjectModuleManager"> <modules> - <module fileurl="file://$PROJECT_DIR$/mobibot.iml" filepath="$PROJECT_DIR$/mobibot.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot_annotationProcessor.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot_annotationProcessor.iml" group="mobibot" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot_main.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot_main.iml" group="mobibot" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot_test.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot_test.iml" group="mobibot" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="1.8.x" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" assert-keyword="true" jdk-15="true" project-jdk-name="1.8.x" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/out" /> </component> <component name="ResourceManagerContainer"> @@ -392,4 +405,204 @@ </value> </option> </component> + <component name="libraryTable"> + <library name="Gradle: com.rometools:rome-fetcher:1.6.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-fetcher/1.6.1/8bd348969d209e634317bf6db3b8be17d85f7674/rome-fetcher-1.6.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-fetcher/1.6.1/e412a4f83403d12bbf90370832d1649688c3fd/rome-fetcher-1.6.1-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: com.rometools:rome:1.6.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.6.1/fb844f8d9b7c1e324d150ca6ebb791cfd9b33243/rome-1.6.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.6.1/d5c50cf970dde36378a5f7bf8b2273e82723535/rome-1.6.1-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: commons-cli:commons-cli:1.3.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/1303efbc4b181e5a58bf2e967dc156a3132b97c0/commons-cli-1.3.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/12ec02d8cb9fbb33bd05506109a4fc8bcc3578/commons-cli-1.3.1-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: commons-codec:commons-codec:1.10"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/4b95f4897fa13f2cd904aee711aeafc0c5295cd8/commons-codec-1.10.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/11fb3d88ae7e3b757d70237064210ceb954a5a04/commons-codec-1.10-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: commons-collections:commons-collections:3.2.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/761ea405b9b37ced573d2df0d1e3a4e0f9edc668/commons-collections-3.2.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/fa095ef874374e5b2a11f8b06c26a5d68c7cb3a4/commons-collections-3.2.1-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: commons-lang:commons-lang:2.4"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/16313e02a793435009f1e458fa4af5d879f6fb11/commons-lang-2.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/2b8c4b3035e45520ef42033e823c7d33e4b4402c/commons-lang-2.4-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: commons-logging:commons-logging:1.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/ecf26c7507d67782a3bbd148d170b31dfad001aa/commons-logging-1.2-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: commons-net:commons-net:3.5"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.5/342fc284019f590e1308056990fdb24a08f06318/commons-net-3.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.5/185dea3736a8583323da4d6ce647719ddf452ccf/commons-net-3.5-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: log4j:log4j:1.2.17"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: net.objecthunter:exp4j:0.4.7"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.7/f910cbcc730e5bd5e7699d8c14905c07c6c12d47/exp4j-0.4.7.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.7/26e7ad8ecde8e7bb1e9d465e8a0b6ffe8dbca2ea/exp4j-0.4.7-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: net.sf.delicious-java:delicious:1.14"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: net.sourceforge.jweather:jweather:0.3.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: net.thauvin.erik:semver:0.9.5-beta"> + <CLASSES> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.apache.velocity:velocity:1.7"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/2ceb567b8f3f21118ecdec129fe1271dbc09aa7a/velocity-1.7.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/eb11eb70171ed64842b2e5216d5904e21ed162ac/velocity-1.7-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.jdom:jdom2:2.0.6"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.json:json:20160212"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160212/a742e3f85161835b95877478c5dd5b405cefaab9/json-20160212.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160212/6b4ee9a02a4ed9eb92efe6104c424f70c822c8c8/json-20160212-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.jsoup:jsoup:1.9.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.9.2/5e3bda828a80c7a21dfbe2308d1755759c2fd7b4/jsoup-1.9.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.9.2/97441f4a0f6bb4c2d9f1e19321f4a45c9ffd0e2c/jsoup-1.9.2-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.ostermiller:utils:1.07.00"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.twitter4j:twitter4j-core:4.0.4"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/1f3c896c7b2f20c51103078ccf0bc2ea97ac012a/twitter4j-core-4.0.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/5fb9d7d5700c56e9a5202a95a2fd6c2159cb33b7/twitter4j-core-4.0.4-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: oro:oro:2.0.8"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: pircbot:pircbot:1.5.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> + </SOURCES> + </library> + </component> </project> \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 5bc23df..2637a05 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "004"; - private final static Date date = new Date(1466883211201L); + private final static String buildmeta = "005"; + private final static Date date = new Date(1466989732969L); private final static int major = 0; private final static int minor = 6; private final static int patch = 1; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 141f761..2396358 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -31,10 +31,10 @@ */ package net.thauvin.erik.mobibot; -import com.sun.syndication.feed.synd.*; -import com.sun.syndication.io.FeedException; -import com.sun.syndication.io.SyndFeedInput; -import com.sun.syndication.io.SyndFeedOutput; +import com.rometools.rome.feed.synd.*; +import com.rometools.rome.io.FeedException; +import com.rometools.rome.io.SyndFeedInput; +import com.rometools.rome.io.SyndFeedOutput; import java.io.*; import java.util.ArrayList; @@ -139,8 +139,8 @@ final class EntriesMgr * * @return The feed's last published date. * - * @throws java.io.FileNotFoundException If the file was not found. - * @throws com.sun.syndication.io.FeedException If an error occurred while reading the feed. + * @throws FileNotFoundException If the file was not found. + * @throws FeedException If an error occurred while reading the feed. */ @SuppressWarnings("unchecked") public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) @@ -222,7 +222,8 @@ final class EntriesMgr * @param history The history array. * @param isDayBackup Set the true if the daily backup file should also be created. */ - public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, final boolean isDayBackup) + public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, + final boolean isDayBackup) { if (bot.getLogger().isDebugEnabled()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 881f960..0cc3b52 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -31,7 +31,8 @@ */ package net.thauvin.erik.mobibot; -import com.sun.syndication.feed.synd.SyndCategoryImpl; +import com.rometools.rome.feed.synd.SyndCategory; +import com.rometools.rome.feed.synd.SyndCategoryImpl; import java.io.Serializable; import java.util.Calendar; @@ -57,7 +58,7 @@ public class EntryLink implements Serializable private final List<EntryComment> comments = new CopyOnWriteArrayList<EntryComment>(); // The tags/categories - private final List<SyndCategoryImpl> tags = new CopyOnWriteArrayList<SyndCategoryImpl>(); + private final List<SyndCategory> tags = new CopyOnWriteArrayList<SyndCategory>(); // The channel private String channel = ""; @@ -87,7 +88,8 @@ public class EntryLink implements Serializable * @param channel The channel. * @param tags The entry's tags/categories. */ - public EntryLink(final String link, final String title, final String nick, final String login, final String channel, final String tags) + public EntryLink(final String link, final String title, final String nick, final String login, final String channel, + final String tags) { this.link = link; this.title = title; @@ -163,7 +165,8 @@ public class EntryLink implements Serializable * @param date The entry date. * @param tags The entry's tags/categories. */ - public EntryLink(final String link, final String title, final String nick, final String channel, final Date date, final List<SyndCategoryImpl> tags) + public EntryLink(final String link, final String title, final String nick, final String channel, final Date date, + final List<SyndCategory> tags) { this.link = link; this.title = title; @@ -349,7 +352,7 @@ public class EntryLink implements Serializable * * @return The tags. */ - public final List getTags() + public final List<SyndCategory> getTags() { return tags; } @@ -359,7 +362,7 @@ public class EntryLink implements Serializable * * @param tags The tags. */ - private void setTags(final List<SyndCategoryImpl> tags) + private void setTags(final List<SyndCategory> tags) { this.tags.addAll(tags); } diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 8640f37..791e574 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -31,11 +31,11 @@ */ package net.thauvin.erik.mobibot; -import com.sun.syndication.feed.synd.SyndEntry; -import com.sun.syndication.feed.synd.SyndEntryImpl; -import com.sun.syndication.feed.synd.SyndFeed; -import com.sun.syndication.fetcher.FeedFetcher; -import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher; +import com.rometools.rome.feed.synd.SyndEntry; +import com.rometools.rome.feed.synd.SyndEntryImpl; +import com.rometools.rome.feed.synd.SyndFeed; +import com.rometools.rome.io.SyndFeedInput; +import com.rometools.rome.io.XmlReader; import java.net.MalformedURLException; import java.net.URL; @@ -94,11 +94,11 @@ class FeedReader implements Runnable */ public final void run() { - final FeedFetcher fetcher = new HttpURLFeedFetcher(bot.getFeedInfoCache()); - try { - final SyndFeed feed = fetcher.retrieveFeed(new URL(url)); + final SyndFeedInput input = new SyndFeedInput(); + final SyndFeed feed = input.build(new XmlReader(new URL(url))); + SyndEntry item; final List items = feed.getEntries(); diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 9310a7f..4240665 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -31,9 +31,7 @@ */ package net.thauvin.erik.mobibot; -import com.sun.syndication.fetcher.impl.FeedFetcherCache; -import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache; -import com.sun.syndication.io.FeedException; +import com.rometools.rome.io.FeedException; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import net.thauvin.erik.semver.Version; @@ -174,10 +172,6 @@ public class Mobibot extends PircBot */ private final List<EntryLink> entries = new ArrayList<EntryLink>(0); - /** - * The feed info cache. - */ - private final FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance(); /** * The history/backlogs array. @@ -943,15 +937,6 @@ public class Mobibot extends PircBot return channel; } - /** - * Returns the {@link FeedFetcherCache feed info cache}. - * - * @return The feed info cache. - */ - public final FeedFetcherCache getFeedInfoCache() - { - return this.feedInfoCache; - } /** * Returns the irc server. diff --git a/version.properties b/version.properties index 27d832c..392747d 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=6 version.patch=1 version.prerelease=beta -version.buildmeta=004 +version.buildmeta=005 From e5064f0e0ee3c796a272c72cdc3a6cc2fdc1716e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jun 2016 23:50:13 -0700 Subject: [PATCH 033/842] Added support for Google Custom Search Engine. Optimized for Java 8. --- mobibot.ipr | 2 +- properties/mobibot.properties | 3 + .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../erik/mobibot/CurrencyConverter.java | 7 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 2 +- .../net/thauvin/erik/mobibot/EntryLink.java | 4 +- .../net/thauvin/erik/mobibot/FeedReader.java | 2 +- .../thauvin/erik/mobibot/GoogleSearch.java | 18 ++- .../net/thauvin/erik/mobibot/Mobibot.java | 140 ++++++++++++------ .../thauvin/erik/mobibot/TellMessagesMgr.java | 16 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 10 +- .../java/net/thauvin/erik/mobibot/Utils.java | 20 ++- .../net/thauvin/erik/mobibot/WorldTime.java | 2 +- version.properties | 2 +- 14 files changed, 152 insertions(+), 80 deletions(-) diff --git a/mobibot.ipr b/mobibot.ipr index c8fa3e0..d69d43e 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -94,7 +94,7 @@ <option name="myForwardDirection" value="false" /> </component> <component name="DependencyValidationManager"> - <scope name="Source" pattern="file:src/main/java/net/thauvin/erik/mobibot/*&&!file:src/main/java/net/thauvin/erik/mobibot/SwingWorker.java&&!file:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> + <scope name="Source" pattern="file[mobibot_annotationProcessor]:java/net/thauvin/erik/mobibot/*||file[mobibot_main]:java/net/thauvin/erik/mobibot/*" /> <option name="SKIP_IMPORT_STATEMENTS" value="false" /> </component> <component name="EclipseCompilerSettings"> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 3acc8ab..2e0b8f1 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -25,3 +25,6 @@ tell-max-size=50 #twitter-consumerSecret= #twitter-token= #twitter-tokenSecret= + +#google-api= +#google-cse-cx= \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 2637a05..a97f667 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "005"; - private final static Date date = new Date(1466989732969L); + private final static String buildmeta = "006"; + private final static Date date = new Date(1467010048201L); private final static int major = 0; private final static int minor = 6; private final static int patch = 1; diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java index 781edc4..121d452 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java @@ -40,7 +40,10 @@ import org.jdom2.input.SAXBuilder; import java.io.IOException; import java.net.URL; import java.text.NumberFormat; -import java.util.*; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; /** * Processes the {@link Commands#CURRENCY_CMD} command. @@ -54,7 +57,7 @@ class CurrencyConverter implements Runnable /** * The exchange rates. */ - private static final Map<String, String> EXCHANGE_RATES = new TreeMap<String, String>(); + private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); /** * The exchange rates table URL. diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 2396358..f65d7e1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -249,7 +249,7 @@ final class EntriesMgr EntryLink entry; StringBuffer buff; EntryComment comment; - final List<SyndEntry> items = new ArrayList<SyndEntry>(0); + final List<SyndEntry> items = new ArrayList<>(0); SyndEntry item; SyndContent description; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 0cc3b52..e1fe035 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -55,10 +55,10 @@ public class EntryLink implements Serializable static final long serialVersionUID = 3676245542270899086L; // The link's comments - private final List<EntryComment> comments = new CopyOnWriteArrayList<EntryComment>(); + private final List<EntryComment> comments = new CopyOnWriteArrayList<>(); // The tags/categories - private final List<SyndCategory> tags = new CopyOnWriteArrayList<SyndCategory>(); + private final List<SyndCategory> tags = new CopyOnWriteArrayList<>(); // The channel private String channel = ""; diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 791e574..8a7058d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -106,7 +106,7 @@ class FeedReader implements Runnable { item = (SyndEntryImpl) items.get(i); bot.send(sender, item.getTitle()); - bot.send(sender, TAB_INDENT + item.getLink()); + bot.send(sender, TAB_INDENT + Utils.green(item.getLink())); } } catch (MalformedURLException e) diff --git a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java index 25298a1..f2bb11e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java @@ -59,6 +59,11 @@ class GoogleSearch implements Runnable */ private final Mobibot bot; + /** + * The Google Custom Search Engine ID. + */ + private final String cseCx; + /** * The search query. */ @@ -73,12 +78,14 @@ class GoogleSearch implements Runnable * Creates a new {@link GoogleSearch} instance. * * @param bot The bot's instance. + * @param cseCx The Google Custom Search Engine ID. * @param sender The nick of the person who sent the message. * @param query The Google query */ - public GoogleSearch(final Mobibot bot, final String sender, final String query) + public GoogleSearch(final Mobibot bot, final String cseCx, final String sender, final String query) { this.bot = bot; + this.cseCx = cseCx; this.sender = sender; this.query = query; } @@ -93,7 +100,8 @@ class GoogleSearch implements Runnable final String query = URLEncoder.encode(this.query, "UTF-8"); final URL url = - new URL("http://ajax.googleapis.com/ajax/services/search/web?start=0&rsz=small&v=1.0&q=" + query); + new URL("https://www.googleapis.com/customsearch/v1?key=" + bot.getGoogleApiKey() + "&cx=" + cseCx + + "&q=" + query + "&filter=1&num=5&alt=json"); final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); @@ -106,13 +114,13 @@ class GoogleSearch implements Runnable } final JSONObject json = new JSONObject(sb.toString()); - final JSONArray ja = json.getJSONObject("responseData").getJSONArray("results"); + final JSONArray ja = json.getJSONArray("items"); for (int i = 0; i < ja.length(); i++) { final JSONObject j = ja.getJSONObject(i); - bot.send(sender, Utils.unescapeXml(j.getString("titleNoFormatting"))); - bot.send(sender, TAB_INDENT + j.getString("url")); + bot.send(sender, Utils.unescapeXml(j.getString("title"))); + bot.send(sender, TAB_INDENT + Utils.green(j.getString("link"))); } reader.close(); diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 4240665..fc256e4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -160,7 +160,7 @@ public class Mobibot extends PircBot /** * The commands list. */ - private final List<String> commandsList = new ArrayList<String>(); + private final List<String> commandsList = new ArrayList<>(); /** * The currency converter. @@ -170,18 +170,17 @@ public class Mobibot extends PircBot /** * The entries array. */ - private final List<EntryLink> entries = new ArrayList<EntryLink>(0); - + private final List<EntryLink> entries = new ArrayList<>(0); /** * The history/backlogs array. */ - private final List<String> history = new ArrayList<String>(0); + private final List<String> history = new ArrayList<>(0); /** * The ignored nicks array. */ - private final List<String> ignoredNicks = new ArrayList<String>(0); + private final List<String> ignoredNicks = new ArrayList<>(0); /** * The IRC port. @@ -211,7 +210,7 @@ public class Mobibot extends PircBot /** * The recap array. */ - private final List<String> recap = new ArrayList<String>(0); + private final List<String> recap = new ArrayList<>(0); /** * The serialized object file. @@ -221,7 +220,7 @@ public class Mobibot extends PircBot /** * Processes the {@link Commands#TELL_CMD} messages queue. */ - private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<TellMessage>(); + private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<>(); /** * Time command. @@ -248,6 +247,16 @@ public class Mobibot extends PircBot */ private String feedURL = ""; + /** + * The Google API Key. + */ + private String googleApiKey = ""; + + /** + * The Google Custom Search Engine ID. + */ + private String googleCseCx = ""; + /** * The NickServ ident password. */ @@ -521,6 +530,10 @@ public class Mobibot extends PircBot final String ttoken = p.getProperty("twitter-token", ""); final String ttokenSecret = p.getProperty("twitter-tokenSecret", ""); + // Get the Google properties + final String googleApiKey = p.getProperty("google-api-key"); + final String googleCseCx = p.getProperty("google-cse-cx"); + // Get the tell command settings final int tellMaxDays = Utils.getIntProperty(p.getProperty("tell-max-days"), DEFAULT_TELL_MAX_DAYS); final int tellMaxSize = Utils.getIntProperty(p.getProperty("tell-max-size"), DEFAULT_TELL_MAX_SIZE); @@ -560,6 +573,14 @@ public class Mobibot extends PircBot bot.setTwitterAuth(tconsumerKey, tconsumerSecret, ttoken, ttokenSecret); } + if (Utils.isValidString(googleApiKey)) + { + if (Utils.isValidString(googleCseCx)) + { + bot.setGoogleAuth(googleApiKey, googleCseCx); + } + } + // Set the tags bot.setTags(tags); @@ -695,6 +716,15 @@ public class Mobibot extends PircBot twitterTokenSecret = tokenSecret; } + /** + * Sets the Google authentication. + */ + private void setGoogleAuth(final String apiKey, final String cseCx) + { + this.googleApiKey = apiKey; + this.googleCseCx = cseCx; + } + /** * Sets the default tags/categories. * @@ -937,6 +967,15 @@ public class Mobibot extends PircBot return channel; } + /** + * Returns the Google API Key, if any. + * + * @return The Google API key or <code>empty</code>. + */ + public String getGoogleApiKey() + { + return this.googleApiKey; + } /** * Returns the irc server. @@ -1025,13 +1064,20 @@ public class Mobibot extends PircBot */ private void googleResponse(final String sender, final String query) { - if (query.length() > 0) + if (isGseEnabled()) { - new Thread(new GoogleSearch(this, sender, query)).start(); + if (query.length() > 0) + { + new Thread(new GoogleSearch(this, googleCseCx, sender, query)).start(); + } + else + { + helpResponse(sender, Commands.GOOGLE_CMD); + } } else { - helpResponse(sender, Commands.GOOGLE_CMD); + send(sender, "The Google searching facility is disabled."); } } @@ -1101,7 +1147,7 @@ public class Mobibot extends PircBot send(sender, "To list the last 5 posts from the channel's weblog:"); send(sender, helpIndent(getNick() + ": " + channel.substring(1))); } - else if (lcTopic.endsWith(Commands.GOOGLE_CMD)) + else if (lcTopic.endsWith(Commands.GOOGLE_CMD) && isGseEnabled()) { send(sender, "To search Google:"); send(sender, helpIndent(getNick() + ": " + Commands.GOOGLE_CMD + " <query>")); @@ -1255,7 +1301,6 @@ public class Mobibot extends PircBot commandsList.add(Commands.CALC_CMD); commandsList.add(Commands.CURRENCY_CMD); commandsList.add(Commands.DICE_CMD); - commandsList.add(Commands.GOOGLE_CMD); commandsList.add(Commands.IGNORE_CMD); commandsList.add(Commands.INFO_CMD); commandsList.add(Commands.JOKE_CMD); @@ -1281,6 +1326,11 @@ public class Mobibot extends PircBot commandsList.add(Commands.TWITTER_CMD); } + if (isGseEnabled()) + { + commandsList.add(Commands.GOOGLE_CMD); + } + Collections.sort(commandsList); } @@ -1412,6 +1462,16 @@ public class Mobibot extends PircBot isPrivate); } + /** + * Returns <code>true</code> if Google search is enabled. + * + * @return <code>true</code> or <code>false</code> + */ + private boolean isGseEnabled() + { + return Utils.isValidString(googleApiKey) && Utils.isValidString(googleCseCx); + } + /** * Determines whether the specified nick should be ignored. * @@ -1787,7 +1847,7 @@ public class Mobibot extends PircBot viewResponse(sender, args, false); } // mobibot: google - else if (cmd.startsWith(Commands.GOOGLE_CMD)) + else if (cmd.startsWith(Commands.GOOGLE_CMD) && isGseEnabled()) { googleResponse(sender, args); } @@ -2316,53 +2376,49 @@ public class Mobibot extends PircBot { if (!nickname.equals(getNick()) && isTellEnabled()) { - for (final TellMessage message : tellMessages) - { - if (message.isMatch(nickname)) + tellMessages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> { + if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { - if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) + if (message.getSender().equals(nickname)) { - if (message.getSender().equals(nickname)) - { - if (!isMessage) - { - send(nickname, - Utils.bold("You") + " wanted me to remind you: " + Colors.REVERSE + message - .getMessage() + Colors.REVERSE, true); - - message.setIsReceived(); - message.setIsNotified(); - - saveTellMessages(); - } - } - else + if (!isMessage) { send(nickname, - message.getSender() + " wanted me to tell you: " + Colors.REVERSE + message - .getMessage() + Colors.REVERSE, - true); + Utils.bold("You") + " wanted me to remind you: " + Colors.REVERSE + message + .getMessage() + Colors.NORMAL, true); message.setIsReceived(); + message.setIsNotified(); saveTellMessages(); } } - else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message - .isNotified()) + else { send(nickname, - "Your message " + Colors.REVERSE + "[ID " + message.getId() + ']' + Colors.REVERSE - + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF - .format(message.getReceived()), + message.getSender() + " wanted me to tell you: " + Colors.REVERSE + message.getMessage() + + Colors.NORMAL, true); - message.setIsNotified(); + message.setIsReceived(); saveTellMessages(); } } - } + else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message + .isNotified()) + { + send(nickname, + "Your message " + Colors.REVERSE + "[ID " + message.getId() + ']' + Colors.NORMAL + + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF + .format(message.getReceived()), + true); + + message.setIsNotified(); + + saveTellMessages(); + } + }); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index cc659bd..058bfd6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -101,9 +101,8 @@ final class TellMessagesMgr { try { - final ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file))); - try + try (ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { if (logger.isDebugEnabled()) { @@ -112,10 +111,6 @@ final class TellMessagesMgr return ((List<TellMessage>) input.readObject()); } - finally - { - input.close(); - } } catch (FileNotFoundException ignore) { @@ -130,7 +125,7 @@ final class TellMessagesMgr logger.getLogger().error("An error occurred loading the messages queue.", e); } - return new ArrayList<TellMessage>(); + return new ArrayList<>(); } /** @@ -144,9 +139,8 @@ final class TellMessagesMgr { try { - final ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file))); - try + try (ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { if (logger.isDebugEnabled()) { @@ -155,10 +149,6 @@ final class TellMessagesMgr output.writeObject(messages); } - finally - { - output.close(); - } } catch (IOException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 04fcf26..45efe3b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -10,7 +10,8 @@ import java.io.InputStreamReader; /** * The <code>TwitterOAuth</code> class. <p> Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> - * to register your bot. </p> Then execute: <p> <code>java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth + * to register your bot. </p> Then execute: <p> <code>java -cp "mobibot.jar:lib/*" + * net.thauvin.erik.mobibot.TwitterOAuth * <consumerKey> <consumerSecret></code> </p> and follow the prompts/instructions. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> @@ -18,9 +19,9 @@ import java.io.InputStreamReader; * @created Sep 13, 2010 * @since 1.0 */ -public class TwitterOAuth +public final class TwitterOAuth { - public static void main(String args[]) + public static void main(final String[] args) throws Exception { if (args.length == 2) @@ -50,8 +51,7 @@ public class TwitterOAuth System.out.println( "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" - + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken.getTokenSecret() - ); + + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken.getTokenSecret()); } catch (TwitterException te) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index d56de0d..176c1ac 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -142,14 +142,14 @@ final class Utils if (Mobibot.NO_TITLE.equals(entry.getTitle())) { - buff.append(bold(entry.getTitle())); + buff.append(entry.getTitle()); } else { - buff.append(entry.getTitle()); + buff.append(bold(entry.getTitle())); } - buff.append(" ( ").append(entry.getLink()).append(" )"); + buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )"); return buff.toString(); } @@ -163,7 +163,19 @@ final class Utils */ public static String bold(final String s) { - return Colors.BOLD + s + Colors.BOLD; + return Colors.BOLD + s + Colors.NORMAL; + } + + /** + * Makes the given string green. + * + * @param s The string. + * + * @return The bold string. + */ + public static String green(final String s) + { + return Colors.DARK_GREEN + s + Colors.NORMAL; } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java index da6364c..50a10f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/WorldTime.java @@ -54,7 +54,7 @@ class WorldTime /** * The countries supported by the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. */ - private static final Map<String, String> COUNTRIES_MAP = new TreeMap<String, String>(); + private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); /** * The date/time format for the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. diff --git a/version.properties b/version.properties index 392747d..4d62098 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=6 version.patch=1 version.prerelease=beta -version.buildmeta=005 +version.buildmeta=006 From 9b8ba57e6886579c54bb4382c38b8bced84d08eb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Jun 2016 12:11:54 -0700 Subject: [PATCH 034/842] Fixed colors. Added last player. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +-- .../java/net/thauvin/erik/mobibot/Dice.java | 6 ++-- .../java/net/thauvin/erik/mobibot/Joke.java | 4 +-- .../net/thauvin/erik/mobibot/Mobibot.java | 30 +++++++++++++------ .../java/net/thauvin/erik/mobibot/Utils.java | 6 ++-- .../java/net/thauvin/erik/mobibot/War.java | 6 ++-- version.properties | 2 +- 7 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index a97f667..ac16dd0 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "006"; - private final static Date date = new Date(1467010048201L); + private final static String buildmeta = "007"; + private final static Date date = new Date(1467054540566L); private final static int major = 0; private final static int minor = 6; private final static int patch = 1; diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/Dice.java index 127e9c1..eeb9f8b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/Dice.java @@ -68,7 +68,7 @@ final class Dice final int playerTotal = i + y; bot.send(bot.getChannel(), - sender + " rolled two dice: " + Utils.reverseColor(i) + " and " + Utils.reverseColor(y) + " for a total of " + Utils + sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils .bold(playerTotal)); i = r.nextInt(6) + 1; @@ -76,7 +76,7 @@ final class Dice final int total = i + y; bot.action( - "rolled two dice: " + Utils.reverseColor(i) + " and " + Utils.reverseColor(y) + " for a total of " + Utils.bold(total)); + "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); if (playerTotal < total) { @@ -91,4 +91,4 @@ final class Dice bot.action("tied."); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Joke.java b/src/main/java/net/thauvin/erik/mobibot/Joke.java index facffa7..3649b8d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/Joke.java @@ -99,7 +99,7 @@ class Joke implements Runnable bot.send(bot.getChannel(), Colors.CYAN + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") - .replaceAll("\\\"", "\"") + Colors.CYAN); + .replaceAll("\\\"", "\"") + Colors.NORMAL); reader.close(); } @@ -109,4 +109,4 @@ class Joke implements Runnable bot.send(sender, "An error has occurred: " + e.getMessage()); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index fc256e4..39946f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -267,6 +267,11 @@ public class Mobibot extends PircBot */ private String identMsg = ""; + /** + * The last player. + */ + private String lastPlayer = ""; + /** * The ident nick. */ @@ -1359,8 +1364,8 @@ public class Mobibot extends PircBot { send(sender, "The op commands are:"); send(sender, - helpIndent(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); + helpIndent(Commands.CYCLE_CMD + " " + Commands.INFO_CMD + " " + Commands.ME_CMD + " " + + Commands.MSG_CMD + " " + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); } } } @@ -1814,14 +1819,22 @@ public class Mobibot extends PircBot // mobibot: dice else if (cmd.equals(Commands.DICE_CMD)) { - send(channel, SHALL_WE_PLAY_A_GAME); + if (!lastPlayer.equalsIgnoreCase(sender)) + { + send(channel, SHALL_WE_PLAY_A_GAME); + lastPlayer = sender; + } Dice.roll(this, sender); } // mobibot: war else if (cmd.equals(Commands.WAR_CMD)) { - send(channel, SHALL_WE_PLAY_A_GAME); + if (!lastPlayer.equalsIgnoreCase(sender)) + { + send(channel, SHALL_WE_PLAY_A_GAME); + lastPlayer = sender; + } War.play(this, sender); } @@ -2384,8 +2397,8 @@ public class Mobibot extends PircBot if (!isMessage) { send(nickname, - Utils.bold("You") + " wanted me to remind you: " + Colors.REVERSE + message - .getMessage() + Colors.NORMAL, true); + Utils.bold("You") + " wanted me to remind you: " + Utils.reverseColor(message + .getMessage()), true); message.setIsReceived(); message.setIsNotified(); @@ -2396,8 +2409,7 @@ public class Mobibot extends PircBot else { send(nickname, - message.getSender() + " wanted me to tell you: " + Colors.REVERSE + message.getMessage() - + Colors.NORMAL, + message.getSender() + " wanted me to tell you: " + Utils.reverseColor(message.getMessage()), true); message.setIsReceived(); @@ -2409,7 +2421,7 @@ public class Mobibot extends PircBot .isNotified()) { send(nickname, - "Your message " + Colors.REVERSE + "[ID " + message.getId() + ']' + Colors.NORMAL + "Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF .format(message.getReceived()), true); diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 176c1ac..cc6294a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -85,7 +85,7 @@ final class Utils */ public static String bold(final int i) { - return Colors.BOLD + i + Colors.BOLD; + return bold(Integer.toString(i)); } /** @@ -163,7 +163,7 @@ final class Utils */ public static String bold(final String s) { - return Colors.BOLD + s + Colors.NORMAL; + return Colors.BOLD + s + Colors.BOLD; } /** @@ -387,7 +387,7 @@ final class Utils */ public static String reverseColor(final int i) { - return Colors.REVERSE + i + Colors.REVERSE; + return reverseColor(Integer.toString(i)); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/War.java index f4c5b0e..4798a3b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/War.java @@ -83,8 +83,8 @@ final class War y = r.nextInt(WAR_DECK.length); bot.send(bot.getChannel(), - sender + " drew the " + Utils.reverseColor(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - bot.action("drew the " + Utils.reverseColor(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); if (i != y) { @@ -107,4 +107,4 @@ final class War bot.action("tied."); } } -} \ No newline at end of file +} diff --git a/version.properties b/version.properties index 4d62098..7ccf793 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=6 version.patch=1 version.prerelease=beta -version.buildmeta=006 +version.buildmeta=007 From 7fce4e009de4ed61d37bfdbfb346dddd2f489373 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Jun 2016 13:08:01 -0700 Subject: [PATCH 035/842] Removed reverseColor(int) from Utils class. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 2 +- .../net/thauvin/erik/mobibot/Mobibot.java | 25 +++++++------ .../net/thauvin/erik/mobibot/Twitter.java | 4 +-- .../java/net/thauvin/erik/mobibot/Utils.java | 36 +++++++------------ 4 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index ac16dd0..e4ab872 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,7 +14,7 @@ import java.util.Date; */ public final class ReleaseInfo { private final static String buildmeta = "007"; - private final static Date date = new Date(1467054540566L); + private final static Date date = new Date(1467057655408L); private final static int major = 0; private final static int minor = 6; private final static int patch = 1; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 39946f7..45afd5f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -38,7 +38,6 @@ import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; import org.apache.commons.logging.impl.Log4JLogger; import org.apache.log4j.Level; -import org.jibble.pircbot.Colors; import org.jibble.pircbot.PircBot; import org.jibble.pircbot.User; import org.jsoup.Jsoup; @@ -267,16 +266,16 @@ public class Mobibot extends PircBot */ private String identMsg = ""; - /** - * The last player. - */ - private String lastPlayer = ""; - /** * The ident nick. */ private String identNick = ""; + /** + * The last player. + */ + private String lastPlayer = ""; + /** * The number of days message are kept. */ @@ -1365,7 +1364,7 @@ public class Mobibot extends PircBot send(sender, "The op commands are:"); send(sender, helpIndent(Commands.CYCLE_CMD + " " + Commands.INFO_CMD + " " + Commands.ME_CMD + " " - + Commands.MSG_CMD + " " + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); + + Commands.MSG_CMD + " " + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); } } } @@ -2397,8 +2396,8 @@ public class Mobibot extends PircBot if (!isMessage) { send(nickname, - Utils.bold("You") + " wanted me to remind you: " + Utils.reverseColor(message - .getMessage()), true); + Utils.bold("You") + " wanted me to remind you: " + Utils + .reverseColor(message.getMessage()), true); message.setIsReceived(); message.setIsNotified(); @@ -2409,7 +2408,8 @@ public class Mobibot extends PircBot else { send(nickname, - message.getSender() + " wanted me to tell you: " + Utils.reverseColor(message.getMessage()), + message.getSender() + " wanted me to tell you: " + Utils + .reverseColor(message.getMessage()), true); message.setIsReceived(); @@ -2421,9 +2421,8 @@ public class Mobibot extends PircBot .isNotified()) { send(nickname, - "Your message " + Utils.reverseColor("[ID " + message.getId() + ']') - + " was sent to " + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF - .format(message.getReceived()), + "Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + Utils + .bold(message.getRecipient()) + " on " + Utils.UTC_SDF.format(message.getReceived()), true); message.setIsNotified(); diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/Twitter.java index 3bf3030..9096603 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/Twitter.java @@ -90,8 +90,8 @@ class Twitter implements Runnable * @param accessTokenSecret The Twitter access token secret. * @param message The Twitter message. */ - public Twitter(final Mobibot bot, final String sender, final String consumerKey, final String consumerSecret, final String accessToken, - final String accessTokenSecret, final String message) + public Twitter(final Mobibot bot, final String sender, final String consumerKey, final String consumerSecret, + final String accessToken, final String accessTokenSecret, final String message) { this.bot = bot; this.consumerKey = consumerKey; diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index cc6294a..69267e9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -88,6 +88,18 @@ final class Utils return bold(Integer.toString(i)); } + /** + * Makes the given string bold. + * + * @param s The string. + * + * @return The bold string. + */ + public static String bold(final String s) + { + return Colors.BOLD + s + Colors.BOLD; + } + /** * Builds an entry's comment for display on the channel. * @@ -154,18 +166,6 @@ final class Utils return buff.toString(); } - /** - * Makes the given string bold. - * - * @param s The string. - * - * @return The bold string. - */ - public static String bold(final String s) - { - return Colors.BOLD + s + Colors.BOLD; - } - /** * Makes the given string green. * @@ -378,18 +378,6 @@ final class Utils return false; } - /** - * Makes the given int reverse color. - * - * @param i The int. - * - * @return The reverse color string. - */ - public static String reverseColor(final int i) - { - return reverseColor(Integer.toString(i)); - } - /** * Makes the given string reverse color. * From 33c820a140c4cc4c8654bed183f92d24d98afd7f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Jun 2016 18:16:55 -0700 Subject: [PATCH 036/842] Excluded explicit jokes. --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/Joke.java | 3 ++- version.properties | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index e4ab872..b9773c3 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "007"; - private final static Date date = new Date(1467057655408L); + private final static String buildmeta = "008"; + private final static Date date = new Date(1467076393978L); private final static int major = 0; private final static int minor = 6; private final static int patch = 1; diff --git a/src/main/java/net/thauvin/erik/mobibot/Joke.java b/src/main/java/net/thauvin/erik/mobibot/Joke.java index 3649b8d..69a6475 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/Joke.java @@ -52,7 +52,8 @@ class Joke implements Runnable /** * The ICNDB URL. */ - private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript"; + private static final String JOKE_URL = + "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; /** * The bot's instance. diff --git a/version.properties b/version.properties index 7ccf793..c4f5d54 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=6 version.patch=1 version.prerelease=beta -version.buildmeta=007 +version.buildmeta=008 From de50cbd10ea297bee619a9b1c0c281e4f45ed7bf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Jun 2016 18:24:43 -0700 Subject: [PATCH 037/842] Delete License.txt on GitHub --- licenses/License.txt | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 licenses/License.txt diff --git a/licenses/License.txt b/licenses/License.txt deleted file mode 100644 index 4d3d187..0000000 --- a/licenses/License.txt +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of this project nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From 7c420645611633ed5d0536f2f9058a0845d431a5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Jun 2016 18:25:43 -0700 Subject: [PATCH 038/842] Delete fetcher.properties on GitHub --- properties/fetcher.properties | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 properties/fetcher.properties diff --git a/properties/fetcher.properties b/properties/fetcher.properties deleted file mode 100644 index e69de29..0000000 From b120e1da878e5ae5dc1eb1a68ab4bb166cb76da2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 30 Jun 2016 11:47:25 -0700 Subject: [PATCH 039/842] Changed to compileOnly dependencies. --- .idea/modules/mobibot.iml | 2 +- .idea/modules/mobibot_annotationProcessor.iml | 2 +- .idea/modules/mobibot_main.iml | 15 ++++++++------- .idea/modules/mobibot_test.iml | 11 ++++------- build.gradle | 10 ---------- mobibot.ipr | 15 ++++++++++++--- 6 files changed, 26 insertions(+), 29 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index ca3a54c..de72d7d 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$/../.."> diff --git a/.idea/modules/mobibot_annotationProcessor.iml b/.idea/modules/mobibot_annotationProcessor.iml index 599f09a..9ac6b73 100644 --- a/.idea/modules/mobibot_annotationProcessor.iml +++ b/.idea/modules/mobibot_annotationProcessor.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:annotationProcessor" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:annotationProcessor" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> <output url="file://$MODULE_DIR$/../../build/classes/annotationProcessor" /> <exclude-output /> diff --git a/.idea/modules/mobibot_main.iml b/.idea/modules/mobibot_main.iml index 7d47b66..039e934 100644 --- a/.idea/modules/mobibot_main.iml +++ b/.idea/modules/mobibot_main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> <output url="file://$MODULE_DIR$/../../build/classes/main" /> <exclude-output /> @@ -17,19 +17,20 @@ <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-fetcher:1.6.1" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> - <orderEntry type="library" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.16" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot_test.iml b/.idea/modules/mobibot_test.iml index 2f8372f..02aaa94 100644 --- a/.idea/modules/mobibot_test.iml +++ b/.idea/modules/mobibot_test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+004" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> <exclude-output /> @@ -18,20 +18,17 @@ <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-fetcher:1.6.1" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> - <orderEntry type="library" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.16" level="project" /> </component> <component name="TestModuleProperties" production-module="mobibot_main" /> </module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 128d696..ee895e3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,3 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.coders-kitchen:compileonlyplugin:1.0.0' - } -} - plugins { id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" id "com.github.ben-manes.versions" version "0.12.0" @@ -15,7 +6,6 @@ plugins { apply plugin: 'java' apply plugin: 'idea' apply plugin: 'application' -apply plugin: 'compileOnly' defaultTasks 'deploy' diff --git a/mobibot.ipr b/mobibot.ipr index d69d43e..cdc8c10 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -406,13 +406,13 @@ </option> </component> <component name="libraryTable"> - <library name="Gradle: com.rometools:rome-fetcher:1.6.1"> + <library name="Gradle: com.rometools:rome-utils:1.6.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-fetcher/1.6.1/8bd348969d209e634317bf6db3b8be17d85f7674/rome-fetcher-1.6.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.6.1/b66628b06492dd8a9fa44d90d1733f843c285589/rome-utils-1.6.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-fetcher/1.6.1/e412a4f83403d12bbf90370832d1649688c3fd/rome-fetcher-1.6.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.6.1/450c47fd73c6ec2d815059f284dfe3d613e45df/rome-utils-1.6.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.rometools:rome:1.6.1"> @@ -577,6 +577,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.slf4j:slf4j-api:1.7.16"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.16/3a6274f658487d5bfff9af3862beff6da1e7fd52/slf4j-api-1.7.16.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.16/974c5dd98f38036bd98b3a8f7bf00ee0d91adbdd/slf4j-api-1.7.16-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.twitter4j:twitter4j-core:4.0.4"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/1f3c896c7b2f20c51103078ccf0bc2ea97ac012a/twitter4j-core-4.0.4.jar!/" /> From 015a368bd125a2c76726ebfa7a04907656177713 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 2 Jul 2016 02:27:18 -0700 Subject: [PATCH 040/842] Moved most commands to modules. --- .idea/modules/mobibot.iml | 33 +- .idea/modules/mobibot_annotationProcessor.iml | 15 - .idea/modules/mobibot_main.iml | 36 - .idea/modules/mobibot_test.iml | 34 - build.gradle | 1 + mobibot.ipr | 542 ++++++------ .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../net/thauvin/erik/mobibot/Commands.java | 70 -- .../net/thauvin/erik/mobibot/Mobibot.java | 780 ++++-------------- .../java/net/thauvin/erik/mobibot/Utils.java | 77 +- .../erik/mobibot/modules/AbstractModule.java | 138 ++++ .../thauvin/erik/mobibot/modules/Calc.java | 98 +++ .../{ => modules}/CurrencyConverter.java | 96 ++- .../erik/mobibot/{ => modules}/Dice.java | 43 +- .../mobibot/{ => modules}/GoogleSearch.java | 108 ++- .../erik/mobibot/{ => modules}/Joke.java | 47 +- .../erik/mobibot/{ => modules}/Lookup.java | 98 ++- .../thauvin/erik/mobibot/modules/Ping.java | 67 ++ .../mobibot/{ => modules}/StockQuote.java | 61 +- .../erik/mobibot/{ => modules}/Twitter.java | 116 +-- .../erik/mobibot/{ => modules}/War.java | 45 +- .../erik/mobibot/{ => modules}/Weather.java | 69 +- .../erik/mobibot/{ => modules}/WorldTime.java | 49 +- version.properties | 4 +- 24 files changed, 1197 insertions(+), 1436 deletions(-) delete mode 100644 .idea/modules/mobibot_annotationProcessor.iml delete mode 100644 .idea/modules/mobibot_main.iml delete mode 100644 .idea/modules/mobibot_test.iml create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Calc.java rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/CurrencyConverter.java (78%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/Dice.java (73%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/GoogleSearch.java (62%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/Joke.java (80%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/Lookup.java (62%) create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Ping.java rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/StockQuote.java (81%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/Twitter.java (54%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/War.java (74%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/Weather.java (77%) rename src/main/java/net/thauvin/erik/mobibot/{ => modules}/WorldTime.java (82%) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index de72d7d..58ccfb9 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,12 +1,43 @@ <?xml version="1.0" encoding="UTF-8"?> <module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/classes/main" /> + <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> <exclude-output /> <content url="file://$MODULE_DIR$/../.."> + <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/annotationProcessor/resources" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> <excludeFolder url="file://$MODULE_DIR$/../../build" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.16" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> + <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> + <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.5" level="project" /> + <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot_annotationProcessor.iml b/.idea/modules/mobibot_annotationProcessor.iml deleted file mode 100644 index 9ac6b73..0000000 --- a/.idea/modules/mobibot_annotationProcessor.iml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:annotationProcessor" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> - <output url="file://$MODULE_DIR$/../../build/classes/annotationProcessor" /> - <exclude-output /> - <content url="file://$MODULE_DIR$/../../src/annotationProcessor"> - <sourceFolder url="file://$MODULE_DIR$/../../src/annotationProcessor/resources" type="java-resource" /> - </content> - <content url="file://$MODULE_DIR$/../../src/generated"> - <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - </component> -</module> \ No newline at end of file diff --git a/.idea/modules/mobibot_main.iml b/.idea/modules/mobibot_main.iml deleted file mode 100644 index 039e934..0000000 --- a/.idea/modules/mobibot_main.iml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> - <output url="file://$MODULE_DIR$/../../build/classes/main" /> - <exclude-output /> - <content url="file://$MODULE_DIR$/../../src/main"> - <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.5" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> - <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> - <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> - <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.16" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> - </component> -</module> \ No newline at end of file diff --git a/.idea/modules/mobibot_test.iml b/.idea/modules/mobibot_test.iml deleted file mode 100644 index 02aaa94..0000000 --- a/.idea/modules/mobibot_test.iml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false"> - <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> - <exclude-output /> - <content url="file://$MODULE_DIR$/../../src/test"> - <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="mobibot_main" /> - <orderEntry type="library" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.5" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> - <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> - <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> - <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.16" level="project" /> - </component> - <component name="TestModuleProperties" production-module="mobibot_main" /> -</module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index ee895e3..f2027cb 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,7 @@ dependencies { compile 'org.jsoup:jsoup:1.9.2' compile 'com.rometools:rome:1.6.1' + compile 'org.slf4j:slf4j-log4j12:1.7.21' compile 'org.json:json:20160212' compile 'org.ostermiller:utils:1.07.00' diff --git a/mobibot.ipr b/mobibot.ipr index cdc8c10..bd77a17 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1,146 +1,117 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="BSFConsole"> - <sendcommandbycontrolenter>false</sendcommandbycontrolenter> - <restoresystemstreams>false</restoresystemstreams> - <outwaitsforerr>false</outwaitsforerr> - <errwaitsforout>false</errwaitsforout> - <storedupsinrecentcommands>false</storedupsinrecentcommands> - <moduleforclasspath /> - <includeoutputpath>false</includeoutputpath> - <includetestsoutputpath>false</includetestsoutputpath> - <languages> - <language name="ant" engine="org.kos.bsfconsoleplugin.languages.AntConsoleBSFEngine" /> - <language name="netrexx" engine="org.apache.bsf.engines.netrexx.NetRexxEngine" /> - <language name="xslt" engine="org.apache.bsf.engines.xslt.XSLTEngine" /> - <language name="beanbasic" engine="org.apache.bsf.engines.beanbasic.BeanBasicEngine" /> - <language name="beanshell" engine="bsh.util.BeanShellBSFEngine" /> - <language name="bml" engine="org.apache.bml.ext.BMLEngine" /> - <language name="groovy" engine="org.codehaus.groovy.bsf.GroovyEngine" /> - <language name="jacl" engine="org.apache.bsf.engines.jacl.JaclEngine" /> - <language name="java" engine="org.apache.bsf.engines.java.JavaEngine" /> - <language name="javaclass" engine="org.apache.bsf.engines.javaclass.JavaClassEngine" /> - <language name="javascript" engine="org.apache.bsf.engines.javascript.JavaScriptEngine" /> - <language name="jpython" engine="org.apache.bsf.engines.jpython.JPythonEngine" /> - <language name="jscript" engine="org.apache.bsf.engines.activescript.ActiveScriptEngine" /> - <language name="judoscript" engine="com.judoscript.BSFJudoEngine" /> - <language name="jython" engine="org.apache.bsf.engines.jython.JythonEngine" /> - <language name="lotusscript" engine="org.apache.bsf.engines.lotusscript.LsEngine" /> - <language name="perl" engine="org.apache.bsf.engines.perl.PerlEngine" /> - <language name="perlscript" engine="org.apache.bsf.engines.activescript.ActiveScriptEngine" /> - <language name="pnuts" engine="pnuts.ext.PnutsBSFEngine" /> - <language name="ruby" engine="org.jruby.javasupport.bsf.JRubyEngine" /> - <language name="vbscript" engine="org.apache.bsf.engines.activescript.ActiveScriptEngine" /> - </languages> - <startupscripts /> - <preferredrecentcommandsdividerlocations> - <dividerlocationd language="beanshell" position="1028" /> - </preferredrecentcommandsdividerlocations> - <BSFConsoleSearchOptions searchfromcursor="false"> - <recentsearches /> - </BSFConsoleSearchOptions> - </component> - <component name="BuildJarProjectSettings"> - <option name="BUILD_JARS_ON_MAKE" value="false" /> - </component> - <component name="CodeStyleProjectProfileManger"> - <option name="PROJECT_PROFILE" /> - <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" /> + <component name="ClientPropertiesManager"> + <properties class="javax.swing.AbstractButton"> + <property name="hideActionText" class="java.lang.Boolean" /> + </properties> + <properties class="javax.swing.JComponent"> + <property name="html.disable" class="java.lang.Boolean" /> + </properties> + <properties class="javax.swing.JEditorPane"> + <property name="JEditorPane.w3cLengthUnits" class="java.lang.Boolean" /> + <property name="JEditorPane.honorDisplayProperties" class="java.lang.Boolean" /> + <property name="charset" class="java.lang.String" /> + </properties> + <properties class="javax.swing.JList"> + <property name="List.isFileList" class="java.lang.Boolean" /> + </properties> + <properties class="javax.swing.JPasswordField"> + <property name="JPasswordField.cutCopyAllowed" class="java.lang.Boolean" /> + </properties> + <properties class="javax.swing.JSlider"> + <property name="Slider.paintThumbArrowShape" class="java.lang.Boolean" /> + <property name="JSlider.isFilled" class="java.lang.Boolean" /> + </properties> + <properties class="javax.swing.JTable"> + <property name="Table.isFileList" class="java.lang.Boolean" /> + <property name="JTable.autoStartsEdit" class="java.lang.Boolean" /> + <property name="terminateEditOnFocusLost" class="java.lang.Boolean" /> + </properties> + <properties class="javax.swing.JToolBar"> + <property name="JToolBar.isRollover" class="java.lang.Boolean" /> + </properties> + <properties class="javax.swing.JTree"> + <property name="JTree.lineStyle" class="java.lang.String" /> + </properties> + <properties class="javax.swing.text.JTextComponent"> + <property name="caretAspectRatio" class="java.lang.Double" /> + <property name="caretWidth" class="java.lang.Integer" /> + </properties> </component> <component name="CompilerConfiguration"> <option name="DEFAULT_COMPILER" value="Javac" /> - <resourceExtensions> - <entry name=".+\.(properties|xml|html|dtd|tld)" /> - <entry name=".+\.(gif|png|jpeg|jpg)" /> - </resourceExtensions> + <resourceExtensions /> <wildcardResourcePatterns> - <entry name="?*.properties" /> - <entry name="?*.xml" /> - <entry name="?*.html" /> - <entry name="?*.dtd" /> - <entry name="?*.tld" /> - <entry name="?*.gif" /> - <entry name="?*.png" /> - <entry name="?*.jpeg" /> - <entry name="?*.jpg" /> + <entry name="!?*.java" /> + <entry name="!?*.form" /> + <entry name="!?*.class" /> + <entry name="!?*.groovy" /> + <entry name="!?*.scala" /> + <entry name="!?*.flex" /> + <entry name="!?*.kt" /> + <entry name="!?*.clj" /> + <entry name="!?*.aj" /> </wildcardResourcePatterns> <annotationProcessing> <profile default="true" name="Default" enabled="false"> <processorPath useClasspath="true" /> </profile> </annotationProcessing> - <bytecodeTargetLevel> - <module name="mobibot_annotationProcessor" target="1.8" /> - <module name="mobibot_main" target="1.8" /> - <module name="mobibot_test" target="1.8" /> - </bytecodeTargetLevel> - </component> - <component name="CopyrightManager" default="Mobibot"> - <copyright> - <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> - <option name="keyword" value="Copyright" /> - <option name="allowReplaceKeyword" value="Erik C. Thauvin" /> - <option name="myName" value="Mobibot" /> - <option name="myLocal" value="true" /> - </copyright> - <module2copyright> - <element module="Source" copyright="Mobibot" /> - </module2copyright> - <LanguageOptions name="__TEMPLATE__"> - <option name="addBlankAfter" value="false" /> - </LanguageOptions> - </component> - <component name="DependenciesAnalyzeManager"> - <option name="myForwardDirection" value="false" /> </component> + <component name="CopyrightManager" default="" /> <component name="DependencyValidationManager"> - <scope name="Source" pattern="file[mobibot_annotationProcessor]:java/net/thauvin/erik/mobibot/*||file[mobibot_main]:java/net/thauvin/erik/mobibot/*" /> - <option name="SKIP_IMPORT_STATEMENTS" value="false" /> + <scope name="Source" pattern="file[mobibot]:src/generated/java//*||file[mobibot]:src/main/java//*" /> </component> - <component name="EclipseCompilerSettings"> - <option name="GENERATE_NO_WARNINGS" value="true" /> - <option name="DEPRECATION" value="false" /> - </component> - <component name="EclipseEmbeddedCompilerSettings"> - <option name="DEBUGGING_INFO" value="true" /> - <option name="GENERATE_NO_WARNINGS" value="true" /> - <option name="DEPRECATION" value="false" /> - <option name="ADDITIONAL_OPTIONS_STRING" value="" /> - <option name="MAXIMUM_HEAP_SIZE" value="128" /> - </component> - <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"> + <component name="Encoding"> <file url="PROJECT" charset="UTF-8" /> </component> <component name="EntryPointsManager"> <entry_points version="2.0" /> </component> - <component name="ExportToHTMLSettings"> - <option name="PRINT_LINE_NUMBERS" value="false" /> - <option name="OPEN_IN_BROWSER" value="false" /> - <option name="OUTPUT_DIRECTORY" /> - </component> - <component name="FrameworkDetectionExcludesConfiguration"> - <file type="web" url="file://$PROJECT_DIR$" /> + <component name="GradleLocalSettings"> + <option name="modificationStamps"> + <map> + <entry key="K:/GitHub/CompileOnlyPlugin" value="2933943731758" /> + <entry key="K:/ect/groovy/SshotCleaner" value="2892856534549" /> + <entry key="K:/ect/java/Captcha" value="2894636758390" /> + <entry key="K:/ect/java/FileSplitter" value="2846829504112" /> + <entry key="K:/ect/java/GZIPFilter" value="2894567636586" /> + <entry key="K:/ect/java/Lotto" value="2894619520256" /> + <entry key="K:/ect/java/SshotCleaner" value="1446415498070" /> + <entry key="K:/ect/java/TorrentCleaner" value="2894593616839" /> + <entry key="K:/ect/java/click" value="2894595659515" /> + <entry key="K:/ect/java/iFit2Workout" value="2894645931218" /> + <entry key="$PROJECT_DIR$/../HttpStatus" value="2906561643260" /> + <entry key="$PROJECT_DIR$/../SemanticVersion" value="2905451385982" /> + <entry key="$PROJECT_DIR$" value="2865248599040" /> + <entry key="$PROJECT_DIR$/../semver" value="2905990126118" /> + </map> + </option> + <option name="externalProjectsViewState"> + <projects_view /> + </option> </component> <component name="GradleSettings"> <option name="linkedExternalProjectsSettings"> <GradleProjectSettings> - <option name="createEmptyContentRootDirectories" value="true" /> <option name="distributionType" value="LOCAL" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="gradleHome" value="C:/gradle" /> + <option name="gradleJvm" value="#JAVA_HOME" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> </set> </option> + <option name="resolveModulePerSourceSet" value="false" /> </GradleProjectSettings> </option> </component> - <component name="IdProvider" IDEtalkID="ADF4C98C99466E4EF52C8D21B83FF938" /> <component name="InspectionProjectProfileManager"> <profile version="1.0"> <option name="myName" value="Project Default" /> + <option name="myLocal" value="true" /> + <inspection_tool class="FieldMayBeFinal" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> <option name="TOP_LEVEL_CLASS_OPTIONS"> <value> @@ -172,239 +143,172 @@ <option name="IGNORE_POINT_TO_ITSELF" value="false" /> <option name="myAdditionalJavadocTags" value="created" /> </inspection_tool> + <inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="REPORT_VARIABLES" value="true" /> + <option name="REPORT_PARAMETERS" value="false" /> + <option name="REPORT_CATCH_PARAMETERS" value="false" /> + </inspection_tool> <inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false"> <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" /> <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" /> </inspection_tool> <inspection_tool class="UnnecessarySemicolon" enabled="false" level="WARNING" enabled_by_default="false" /> + <inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" /> + <option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="false" /> + <option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" /> + </inspection_tool> </profile> <option name="PROJECT_PROFILE" /> <option name="USE_PROJECT_PROFILE" value="false" /> <version value="1.0" /> </component> - <component name="JavadocGenerationManager"> - <option name="OUTPUT_DIRECTORY" /> - <option name="OPTION_SCOPE" value="protected" /> - <option name="OPTION_HIERARCHY" value="true" /> - <option name="OPTION_NAVIGATOR" value="true" /> - <option name="OPTION_INDEX" value="true" /> - <option name="OPTION_SEPARATE_INDEX" value="true" /> - <option name="OPTION_DOCUMENT_TAG_USE" value="true" /> - <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="true" /> - <option name="OPTION_DOCUMENT_TAG_VERSION" value="true" /> - <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" /> - <option name="OPTION_DEPRECATED_LIST" value="true" /> - <option name="OTHER_OPTIONS" /> - <option name="HEAP_SIZE" /> - <option name="LOCALE" /> - <option name="OPEN_IN_BROWSER" value="true" /> - </component> - <component name="LogConsolePreferences"> - <option name="FILTER_ERRORS" value="false" /> - <option name="FILTER_WARNINGS" value="false" /> - <option name="FILTER_INFO" value="true" /> - <option name="CUSTOM_FILTER" /> - </component> - <component name="PDMPlugin"> - <option name="skipTestSources" value="false" /> - </component> - <component name="PEExternalization"> - <option name="IGNORE_METHOD_NAMES" value="" /> - <option name="IGNORE_METHOD_NAMES_ENABLED" value="false" /> - <option name="IGNORE_CLASS_CONSTRUCTORS"> - <value> - <option class="Exception" includeInheritors="true" /> - </value> + <component name="MavenImportPreferences"> + <option name="generalSettings"> + <MavenGeneralSettings> + <option name="mavenHome" value="Bundled (Maven 3)" /> + </MavenGeneralSettings> </option> - <option name="IGNORE_CLASS_CONSTRUCTOR_NAMES_ENABLED" value="true" /> - <option name="IGNORE_CLASS_METHODS_ENABLED" value="true" /> - <option name="IGNORE_STRINGS_CONTAINS_ONLY_ENABLED" value="true" /> - <option name="IGNORE_STRINGS_CONTAINS_ONLY_WHITESPACES" value="true" /> - <option name="IGNORE_STRINGS_CONTAINS_ONLY_PUNCTUATIONS" value="true" /> - <option name="IGNORE_STRINGS_CONTAINS_ONLY_SPECIFIED_ENABLED" value="true" /> - <option name="IGNORE_STRINGS_CONTAINS_ONLY_SPECIFIED" value="" /> - <option name="IGNORE_CLASS_METHODS"> - <value> - <option class="Exception" includeInheritors="false" /> - <option class="java.util.ResourceBundle" includeInheritors="true" /> - </value> - </option> - <option name="IGNORE_CONSTANTS_DECLARATIONS" value="true" /> </component> - <component name="Palette2"> - <group name="Swing"> - <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> - </item> - <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> - </item> - <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> - </item> - <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> - <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> - </item> - <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> - <initial-values> - <property name="text" value="Button" /> - </initial-values> - </item> - <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> - <initial-values> - <property name="text" value="RadioButton" /> - </initial-values> - </item> - <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> - <initial-values> - <property name="text" value="CheckBox" /> - </initial-values> - </item> - <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> - <initial-values> - <property name="text" value="Label" /> - </initial-values> - </item> - <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> - <preferred-size width="150" height="-1" /> - </default-constraints> - </item> - <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> - <preferred-size width="150" height="-1" /> - </default-constraints> - </item> - <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> - <preferred-size width="150" height="-1" /> - </default-constraints> - </item> - <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> - </item> - <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> - <preferred-size width="200" height="200" /> - </default-constraints> - </item> - <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> - <preferred-size width="200" height="200" /> - </default-constraints> - </item> - <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> - </item> - <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> - </item> - <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> - </item> - <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> - </item> - <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> - <preferred-size width="-1" height="20" /> - </default-constraints> - </item> - <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> - </item> - <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> - </item> - </group> + <component name="ProjectInspectionProfilesVisibleTreeState"> + <entry key="Project Default"> + <profile-state> + <expanded-state> + <State> + <id /> + </State> + <State> + <id>Android Lint</id> + </State> + <State> + <id>Java</id> + </State> + <State> + <id>Portability issuesJava</id> + </State> + </expanded-state> + </profile-state> + </entry> </component> - <component name="ProjectCodeStyleSettingsManager"> - <option name="PER_PROJECT_SETTINGS"> - <value> - <XML> - <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> - </XML> - </value> - </option> - <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> - </component> - <component name="ProjectDetails"> - <option name="projectName" value="mobibot" /> - </component> - <component name="ProjectDictionaryState"> - <dictionary name="erik"> - <words> - <w>mobibot</w> - </words> - </dictionary> + <component name="ProjectLevelVcsManager" settingsEditedManually="false"> + <OptionsSetting value="true" id="Add" /> + <OptionsSetting value="true" id="Remove" /> + <OptionsSetting value="true" id="Checkout" /> + <OptionsSetting value="true" id="Update" /> + <OptionsSetting value="true" id="Status" /> + <OptionsSetting value="true" id="Edit" /> + <ConfirmationsSetting value="0" id="Add" /> + <ConfirmationsSetting value="0" id="Remove" /> </component> <component name="ProjectModuleManager"> <modules> <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" /> - <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot_annotationProcessor.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot_annotationProcessor.iml" group="mobibot" /> - <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot_main.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot_main.iml" group="mobibot" /> - <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot_test.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot_test.iml" group="mobibot" /> </modules> </component> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" assert-keyword="true" jdk-15="true" project-jdk-name="1.8.x" project-jdk-type="JavaSDK"> - <output url="file://$PROJECT_DIR$/out" /> + <output url="file://$PROJECT_DIR$/build/classes" /> </component> - <component name="ResourceManagerContainer"> - <option name="myResourceBundles"> - <value> - <list size="0" /> - </value> - </option> + <component name="PropertiesComponent"> + <property name="GoToClass.includeLibraries" value="false" /> + <property name="GoToClass.toSaveIncludeLibraries" value="false" /> + <property name="GoToFile.includeJavaFiles" value="false" /> + <property name="MemberChooser.sorted" value="false" /> + <property name="MemberChooser.showClasses" value="true" /> + <property name="MemberChooser.copyJavadoc" value="false" /> + <property name="settings.editor.selected.configurable" value="Errors" /> + <property name="settings.editor.splitter.proportion" value="0.2" /> </component> - <component name="SvnBranchConfigurationManager"> - <option name="mySupportsUserInfoFilter" value="true" /> + <component name="RunManager"> + <configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin"> + <module name="" /> + <option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" /> + <option name="PROGRAM_PARAMETERS" /> + <method /> + </configuration> + <configuration default="true" type="Applet" factoryName="Applet"> + <option name="WIDTH" value="400" /> + <option name="HEIGHT" value="300" /> + <option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" /> + <module /> + <method /> + </configuration> + <configuration default="true" type="Application" factoryName="Application"> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <option name="MAIN_CLASS_NAME" /> + <option name="VM_PARAMETERS" /> + <option name="PROGRAM_PARAMETERS" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> + <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> + <option name="ALTERNATIVE_JRE_PATH" /> + <option name="ENABLE_SWING_INSPECTOR" value="false" /> + <option name="ENV_VARIABLES" /> + <option name="PASS_PARENT_ENVS" value="true" /> + <module name="" /> + <envs /> + <method /> + </configuration> + <configuration default="true" type="JUnit" factoryName="JUnit"> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <module name="" /> + <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> + <option name="ALTERNATIVE_JRE_PATH" /> + <option name="PACKAGE_NAME" /> + <option name="MAIN_CLASS_NAME" /> + <option name="METHOD_NAME" /> + <option name="TEST_OBJECT" value="class" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="PARAMETERS" /> + <option name="WORKING_DIRECTORY" value="$MODULE_DIR$" /> + <option name="ENV_VARIABLES" /> + <option name="PASS_PARENT_ENVS" value="true" /> + <option name="TEST_SEARCH_SCOPE"> + <value defaultName="singleModule" /> + </option> + <envs /> + <patterns /> + <method /> + </configuration> + <configuration default="true" type="Remote" factoryName="Remote"> + <option name="USE_SOCKET_TRANSPORT" value="true" /> + <option name="SERVER_MODE" value="false" /> + <option name="SHMEM_ADDRESS" value="javadebug" /> + <option name="HOST" value="localhost" /> + <option name="PORT" value="5005" /> + <method /> + </configuration> + <configuration default="true" type="TestNG" factoryName="TestNG"> + <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> + <module name="" /> + <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> + <option name="ALTERNATIVE_JRE_PATH" /> + <option name="SUITE_NAME" /> + <option name="PACKAGE_NAME" /> + <option name="MAIN_CLASS_NAME" /> + <option name="METHOD_NAME" /> + <option name="GROUP_NAME" /> + <option name="TEST_OBJECT" value="CLASS" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="PARAMETERS" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> + <option name="OUTPUT_DIRECTORY" /> + <option name="ANNOTATION_TYPE" /> + <option name="ENV_VARIABLES" /> + <option name="PASS_PARENT_ENVS" value="true" /> + <option name="TEST_SEARCH_SCOPE"> + <value defaultName="singleModule" /> + </option> + <option name="USE_DEFAULT_REPORTERS" value="false" /> + <option name="PROPERTIES_FILE" /> + <envs /> + <properties /> + <listeners /> + <method /> + </configuration> </component> <component name="VcsDirectoryMappings"> <mapping directory="" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" /> </component> - <component name="WebServicesPlugin" addRequiredLibraries="true" /> - <component name="com.intellij.jsf.UserDefinedFacesConfigs"> - <option name="USER_DEFINED_CONFIGS"> - <value> - <list size="0" /> - </value> - </option> - </component> <component name="libraryTable"> <library name="Gradle: com.rometools:rome-utils:1.6.1"> <CLASSES> @@ -614,4 +518,20 @@ </SOURCES> </library> </component> + <component name="masterDetails"> + <states> + <state key="ProjectJDKs.UI"> + <settings> + <last-edited>1.8.x</last-edited> + <splitter-proportions> + <option name="proportions"> + <list> + <option value="0.2" /> + </list> + </option> + </splitter-proportions> + </settings> + </state> + </states> + </component> </project> \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index b9773c3..95241ad 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,11 +13,11 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "008"; - private final static Date date = new Date(1467076393978L); + private final static String buildmeta = "001"; + private final static Date date = new Date(1467451308717L); private final static int major = 0; private final static int minor = 6; - private final static int patch = 1; + private final static int patch = 5; private final static String prerelease = "beta"; private final static String project = "mobibot"; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 81509c5..b849b5e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -45,21 +45,6 @@ final class Commands */ public static final String ADDLOG_CMD = "addlog"; - /** - * The math command. - */ - public static final String CALC_CMD = "calc"; - - /** - * The currency command. - */ - public static final String CURRENCY_CMD = "currency"; - - /** - * The rates keyword. - */ - public static final String CURRENCY_RATES_KEYWORD = "rates"; - /** * The cycle command. */ @@ -75,21 +60,11 @@ final class Commands */ public static final String DEBUG_CMD = "debug"; - /** - * The dices command. - */ - public static final String DICE_CMD = "dice"; - /** * The die command. */ public static final String DIE_CMD = "die"; - /** - * The Google command. - */ - public static final String GOOGLE_CMD = "google"; - /** * Help command line argument. */ @@ -125,21 +100,11 @@ final class Commands */ public static final String INFO_CMD = "info"; - /** - * The joke command. - */ - public static final String JOKE_CMD = "joke"; - /** * The link command. */ public static final String LINK_CMD = "L"; - /** - * The lookup command. - */ - public static final String LOOKUP_CMD = "lookup"; - /** * The me command. */ @@ -155,16 +120,6 @@ final class Commands */ public static final String NICK_CMD = "nick"; - /** - * The ping command. - */ - public static final String PING_CMD = "ping"; - - /** - * The pong command. - */ - public static final String PONG_CMD = "pong"; - /** * Properties command line argument. */ @@ -180,11 +135,6 @@ final class Commands */ public static final String SAY_CMD = "say"; - /** - * The stock command. - */ - public static final String STOCK_CMD = "stock"; - /** * The {@link #TELL_CMD} all command. */ @@ -200,16 +150,6 @@ final class Commands */ public static final String TELL_DEL_CMD = "del"; - /** - * The time command. - */ - public static final String TIME_CMD = "time"; - - /** - * The Twitter command. - */ - public static final String TWITTER_CMD = "twitter"; - /** * The users command. */ @@ -230,16 +170,6 @@ final class Commands */ public static final String VIEW_CMD = "view"; - /** - * The war command. - */ - public static final String WAR_CMD = "war"; - - /** - * The weather command. - */ - public static final String WEATHER_CMD = "weather"; - /** * Disables the default constructor. * diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 45afd5f..f1e97a4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -32,8 +32,7 @@ package net.thauvin.erik.mobibot; import com.rometools.rome.io.FeedException; -import net.objecthunter.exp4j.Expression; -import net.objecthunter.exp4j.ExpressionBuilder; +import net.thauvin.erik.mobibot.modules.*; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; import org.apache.commons.logging.impl.Log4JLogger; @@ -44,8 +43,6 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.*; -import java.net.UnknownHostException; -import java.text.DecimalFormat; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; @@ -67,7 +64,7 @@ public class Mobibot extends PircBot /** * The empty title string. */ - public static final String NO_TITLE = "No Title"; + static final String NO_TITLE = "No Title"; /** * The default port. @@ -118,16 +115,16 @@ public class Mobibot extends PircBot */ private static final long MESSAGE_DELAY = 1000L; + /** + * The modules. + */ + private static final List<AbstractModule> MODULES = new ArrayList<>(0); + /** * The serialized object file extension. */ private static final String SER_EXT = ".ser"; - /** - * Shall we play a game? - */ - private static final String SHALL_WE_PLAY_A_GAME = "Shall we play a game?"; - /** * The start time. */ @@ -161,11 +158,6 @@ public class Mobibot extends PircBot */ private final List<String> commandsList = new ArrayList<>(); - /** - * The currency converter. - */ - private final CurrencyConverter currencyConverter; - /** * The entries array. */ @@ -221,11 +213,6 @@ public class Mobibot extends PircBot */ private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<>(); - /** - * Time command. - */ - private final WorldTime worldTime = new WorldTime(); - /** * The backlogs URL. */ @@ -246,16 +233,6 @@ public class Mobibot extends PircBot */ private String feedURL = ""; - /** - * The Google API Key. - */ - private String googleApiKey = ""; - - /** - * The Google Custom Search Engine ID. - */ - private String googleCseCx = ""; - /** * The NickServ ident password. */ @@ -271,11 +248,6 @@ public class Mobibot extends PircBot */ private String identNick = ""; - /** - * The last player. - */ - private String lastPlayer = ""; - /** * The number of days message are kept. */ @@ -291,26 +263,6 @@ public class Mobibot extends PircBot */ private String today = Utils.today(); - /** - * The Twitter consumer key. - */ - private String twitterConsumerKey = ""; - - /** - * The Twitter consumer secret. - */ - private String twitterConsumerSecret = ""; - - /** - * The Twitter token. - */ - private String twitterToken = ""; - - /** - * The Twitter token secret. - */ - private String twitterTokenSecret = ""; - /** * The weblog URL. */ @@ -345,7 +297,6 @@ public class Mobibot extends PircBot // Initialization Utils.UTC_SDF.setTimeZone(TimeZone.getTimeZone("UTC")); - currencyConverter = new CurrencyConverter(this); // Load the current entries, if any. try @@ -385,6 +336,20 @@ public class Mobibot extends PircBot { logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); } + + // Load the modules + MODULES.add(new Calc()); + MODULES.add(new CurrencyConverter()); + MODULES.add(new Dice()); + MODULES.add(new GoogleSearch()); + MODULES.add(new Joke()); + MODULES.add(new Lookup()); + MODULES.add(new Ping()); + MODULES.add(new StockQuote()); + MODULES.add(new Twitter()); + MODULES.add(new War()); + MODULES.add(new Weather()); + MODULES.add(new WorldTime()); } /** @@ -528,16 +493,6 @@ public class Mobibot extends PircBot final String dname = p.getProperty("delicious-user"); final String dpwd = p.getProperty("delicious-pwd"); - // Get the Twitter properties - final String tconsumerKey = p.getProperty("twitter-consumerKey"); - final String tconsumerSecret = p.getProperty("twitter-consumerSecret"); - final String ttoken = p.getProperty("twitter-token", ""); - final String ttokenSecret = p.getProperty("twitter-tokenSecret", ""); - - // Get the Google properties - final String googleApiKey = p.getProperty("google-api-key"); - final String googleCseCx = p.getProperty("google-cse-cx"); - // Get the tell command settings final int tellMaxDays = Utils.getIntProperty(p.getProperty("tell-max-days"), DEFAULT_TELL_MAX_DAYS); final int tellMaxSize = Utils.getIntProperty(p.getProperty("tell-max-size"), DEFAULT_TELL_MAX_SIZE); @@ -570,20 +525,13 @@ public class Mobibot extends PircBot bot.setDeliciousAuth(dname, dpwd); } - if (Utils.isValidString(tconsumerKey) && Utils.isValidString(tconsumerSecret) && Utils.isValidString(ttoken) - && Utils.isValidString(ttokenSecret)) - { - // Set the Twitter authentication - bot.setTwitterAuth(tconsumerKey, tconsumerSecret, ttoken, ttokenSecret); - } - - if (Utils.isValidString(googleApiKey)) - { - if (Utils.isValidString(googleCseCx)) + // Load the modules properties + MODULES.stream().filter(AbstractModule::hasProperties).forEach(module -> { + for (final String s : module.getPropertyKeys()) { - bot.setGoogleAuth(googleApiKey, googleCseCx); + module.setProperty(s, p.getProperty(s, "")); } - } + }); // Set the tags bot.setTags(tags); @@ -703,32 +651,6 @@ public class Mobibot extends PircBot delicious = new DeliciousPoster(username, password, ircServer); } - /** - * Sets the Twitter consumerSecret and password.. - * - * @param consumerKey The Twitter consumer key. - * @param consumerSecret The Twitter consumer secret. - * @param token The Twitter token. - * @param tokenSecret The Twitter token secret. - */ - private void setTwitterAuth(final String consumerKey, final String consumerSecret, final String token, - final String tokenSecret) - { - twitterConsumerKey = consumerKey; - twitterConsumerSecret = consumerSecret; - twitterToken = token; - twitterTokenSecret = tokenSecret; - } - - /** - * Sets the Google authentication. - */ - private void setGoogleAuth(final String apiKey, final String cseCx) - { - this.googleApiKey = apiKey; - this.googleCseCx = cseCx; - } - /** * Sets the default tags/categories. * @@ -844,7 +766,7 @@ public class Mobibot extends PircBot * * @param action The action. */ - final void action(final String action) + final public void action(final String action) { action(channel, action); } @@ -863,40 +785,6 @@ public class Mobibot extends PircBot } } - /** - * Processes the {@link Commands#CALC_CMD} command. - * - * @param sender The nick of the person who sent the message - * @param args The command arguments. - * @param message The actual message. - */ - private void calcResponse(final String sender, final String args, final String message) - { - if (Utils.isValidString(args)) - { - final DecimalFormat decimalFormat = new DecimalFormat("#.##"); - - try - { - final Expression calc = new ExpressionBuilder(args).build(); - send(channel, args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate())); - } - catch (Exception e) - { - if (logger.isDebugEnabled()) - { - logger.debug("Unable to calculate: " + message, e); - } - - send(channel, "No idea. This is the kind of math I don't get."); - } - } - else - { - helpResponse(sender, Commands.CALC_CMD); - } - } - /** * Responds with the title and links from the RSS feed. * @@ -971,16 +859,6 @@ public class Mobibot extends PircBot return channel; } - /** - * Returns the Google API Key, if any. - * - * @return The Google API key or <code>empty</code>. - */ - public String getGoogleApiKey() - { - return this.googleApiKey; - } - /** * Returns the irc server. * @@ -1060,31 +938,6 @@ public class Mobibot extends PircBot this.weblogUrl = weblogUrl; } - /** - * Responds with the Google search results for the specified query. - * - * @param sender The nick of the person who sent the private message. - * @param query The Google query to execute. - */ - private void googleResponse(final String sender, final String query) - { - if (isGseEnabled()) - { - if (query.length() > 0) - { - new Thread(new GoogleSearch(this, googleCseCx, sender, query)).start(); - } - else - { - helpResponse(sender, Commands.GOOGLE_CMD); - } - } - else - { - send(sender, "The Google searching facility is disabled."); - } - } - /** * Returns indented and bold help string. * @@ -1092,7 +945,7 @@ public class Mobibot extends PircBot * * @return The indented help string. */ - private String helpIndent(final String help) + final public String helpIndent(final String help) { return helpIndent(help, true); } @@ -1116,11 +969,11 @@ public class Mobibot extends PircBot * @param sender The nick of the person who sent the private message. * @param topic The help topic, if any. */ - public final void helpResponse(final String sender, final String topic) + private void helpResponse(final String sender, final String topic) { - final String lcTopic = topic.toLowerCase(); + final String lcTopic = topic.toLowerCase().trim(); - if (lcTopic.endsWith(Commands.HELP_POSTING_KEYWORD)) + if (lcTopic.equals(Commands.HELP_POSTING_KEYWORD)) { send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); @@ -1136,145 +989,63 @@ public class Mobibot extends PircBot send(sender, helpIndent(Commands.LINK_CMD + "1.1:-")); send(sender, "You can also view a posting by saying its label."); } - else if (lcTopic.endsWith(Commands.HELP_TAGS_KEYWORD)) + else if (lcTopic.equals(Commands.HELP_TAGS_KEYWORD)) { send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:")); send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); } - else if (lcTopic.endsWith(Commands.VIEW_CMD)) + else if (lcTopic.equals(Commands.VIEW_CMD)) { send(sender, "To list or search the current URL posts:"); send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); } - else if (lcTopic.endsWith(channel.substring(1).toLowerCase())) + else if (lcTopic.equals(channel.substring(1).toLowerCase())) { send(sender, "To list the last 5 posts from the channel's weblog:"); send(sender, helpIndent(getNick() + ": " + channel.substring(1))); } - else if (lcTopic.endsWith(Commands.GOOGLE_CMD) && isGseEnabled()) - { - send(sender, "To search Google:"); - send(sender, helpIndent(getNick() + ": " + Commands.GOOGLE_CMD + " <query>")); - } - else if (lcTopic.endsWith(Commands.TWITTER_CMD) && isTwitterEnabled()) - { - send(sender, "To post to Twitter:"); - send(sender, helpIndent(getNick() + ": " + Commands.TWITTER_CMD + " <message>")); - } - else if (lcTopic.endsWith(Commands.RECAP_CMD)) + else if (lcTopic.equals(Commands.RECAP_CMD)) { send(sender, "To list the last 10 public channel messages:"); send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); } - else if (lcTopic.endsWith(Commands.CALC_CMD)) - { - send(sender, "To solve a mathematical calculation:"); - send(sender, helpIndent(getNick() + ": " + Commands.CALC_CMD + " <calculation>")); - } - else if (lcTopic.endsWith(Commands.LOOKUP_CMD)) - { - send(sender, "To perform a DNS lookup query:"); - send(sender, helpIndent(getNick() + ": " + Commands.LOOKUP_CMD + " <ip address or hostname>")); - } - else if (lcTopic.endsWith(Commands.TIME_CMD)) - { - send(sender, "To display a country's current date/time:"); - send(sender, helpIndent(getNick() + ": " + Commands.TIME_CMD) + " [<country code>]"); - - send(sender, "For a listing of the supported countries:"); - send(sender, helpIndent(getNick() + ": " + Commands.TIME_CMD)); - } - else if (lcTopic.endsWith(Commands.JOKE_CMD)) - { - send(sender, "To retrieve a random joke:"); - send(sender, helpIndent(getNick() + ": " + Commands.JOKE_CMD)); - } - else if (lcTopic.endsWith(Commands.STOCK_CMD)) - { - send(sender, "To retrieve a stock quote:"); - send(sender, helpIndent(getNick() + ": " + Commands.STOCK_CMD + " <symbol[.country code]>")); - } - else if (lcTopic.endsWith(Commands.DICE_CMD)) - { - send(sender, "To roll the dice:"); - send(sender, helpIndent(getNick() + ": " + Commands.DICE_CMD)); - } - else if (lcTopic.endsWith(Commands.WAR_CMD)) - { - send(sender, "To play war:"); - send(sender, helpIndent(getNick() + ": " + Commands.WAR_CMD)); - } - else if (lcTopic.endsWith(Commands.WEATHER_CMD)) - { - send(sender, "To display weather information:"); - send(sender, helpIndent(getNick() + ": " + Commands.WEATHER_CMD + " <station id>")); - send(sender, "For a listing of the ICAO station IDs, please visit: " + Weather.STATIONS_URL); - } - else if (lcTopic.endsWith(Commands.USERS_CMD)) + else if (lcTopic.equals(Commands.USERS_CMD)) { send(sender, "To list the users present on the channel:"); send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); } - else if (lcTopic.endsWith(Commands.INFO_CMD)) + else if (lcTopic.equals(Commands.INFO_CMD) && isOp(sender)) { send(sender, "To view information about the bot:"); send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); } - else if (lcTopic.endsWith(Commands.CYCLE_CMD)) + else if (lcTopic.equals(Commands.CYCLE_CMD) && isOp(sender)) { - if (isOp(sender)) - { - send(sender, "To have the bot leave the channel and come back:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); - } - } - else if (lcTopic.endsWith(Commands.ME_CMD)) - { - if (isOp(sender)) - { - send(sender, "To have the bot perform an action:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); - } - } - else if (lcTopic.endsWith(Commands.SAY_CMD)) - { - if (isOp(sender)) - { - send(sender, "To have the bot say something on the channel:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); - } - } - else if (lcTopic.endsWith(Commands.VERSION_CMD)) - { - if (isOp(sender)) - { - send(sender, "To view the version data (bot, java, etc.):"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); - } - } - else if (lcTopic.endsWith(Commands.MSG_CMD)) - { - if (isOp(sender)) - { - send(sender, "To have the bot send a private message to someone:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); - } - } - else if (lcTopic.startsWith(Commands.CURRENCY_CMD)) - { - send(sender, "To convert from one currency to another:"); - send(sender, helpIndent(getNick() + ": " + Commands.CURRENCY_CMD + " [100 USD to EUR]")); + send(sender, "To have the bot leave the channel and come back:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); - if (lcTopic.endsWith(Commands.CURRENCY_CMD)) - { - send(sender, "For a listing of currency rates:"); - send(sender, - helpIndent(getNick() + ": " + Commands.CURRENCY_CMD) + ' ' + Commands.CURRENCY_RATES_KEYWORD); - send(sender, "For a listing of supported currencies:"); - send(sender, helpIndent(getNick() + ": " + Commands.CURRENCY_CMD)); - } } - else if (lcTopic.startsWith(Commands.IGNORE_CMD)) + else if (lcTopic.equals(Commands.ME_CMD) && isOp(sender)) + { + send(sender, "To have the bot perform an action:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); + } + else if (lcTopic.equals(Commands.SAY_CMD) && isOp(sender)) + { + send(sender, "To have the bot say something on the channel:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); + } + else if (lcTopic.equals(Commands.VERSION_CMD) && isOp(sender)) + { + send(sender, "To view the version data (bot, java, etc.):"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); + } + else if (lcTopic.equals(Commands.MSG_CMD) && isOp(sender)) + { + send(sender, "To have the bot send a private message to someone:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); + } + else if (lcTopic.equals(Commands.IGNORE_CMD)) { send(sender, "To check your ignore status:"); send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); @@ -1283,7 +1054,7 @@ public class Mobibot extends PircBot send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); } - else if (lcTopic.startsWith(Commands.TELL_CMD)) + else if (lcTopic.equals(Commands.TELL_CMD)) { send(sender, "To send a message to someone when they join the channel:"); send(sender, helpIndent(getNick() + ": " + Commands.TELL_CMD + " <nick> <message>")); @@ -1295,6 +1066,19 @@ public class Mobibot extends PircBot } else { + + for (final AbstractModule module : MODULES) + { + for (final String cmd : module.getCommands()) + { + if (lcTopic.equals(cmd)) + { + module.helpResponse(this, sender, topic, true); + return; + } + } + } + send(sender, Utils.bold("Type a URL on " + channel + " to post it.")); send(sender, "For more information on a specific command, type:"); send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); @@ -1302,39 +1086,23 @@ public class Mobibot extends PircBot if (commandsList.isEmpty()) { - commandsList.add(Commands.CALC_CMD); - commandsList.add(Commands.CURRENCY_CMD); - commandsList.add(Commands.DICE_CMD); commandsList.add(Commands.IGNORE_CMD); commandsList.add(Commands.INFO_CMD); - commandsList.add(Commands.JOKE_CMD); - commandsList.add(Commands.LOOKUP_CMD); commandsList.add(channel.substring(1)); commandsList.add(Commands.HELP_POSTING_KEYWORD); - commandsList.add(Commands.RECAP_CMD); - commandsList.add(Commands.STOCK_CMD); commandsList.add(Commands.HELP_TAGS_KEYWORD); - commandsList.add(Commands.TIME_CMD); + commandsList.add(Commands.RECAP_CMD); commandsList.add(Commands.USERS_CMD); commandsList.add(Commands.VIEW_CMD); - commandsList.add(Commands.WAR_CMD); - commandsList.add(Commands.WEATHER_CMD); + + MODULES.stream().filter(AbstractModule::isEnabled) + .forEach(module -> commandsList.addAll(module.getCommands())); if (isTellEnabled()) { commandsList.add(Commands.TELL_CMD); } - if (isTwitterEnabled()) - { - commandsList.add(Commands.TWITTER_CMD); - } - - if (isGseEnabled()) - { - commandsList.add(Commands.GOOGLE_CMD); - } - Collections.sort(commandsList); } @@ -1350,7 +1118,7 @@ public class Mobibot extends PircBot sb.append(commandsList.get(i)); // 5 commands per line or last command - if (sb.length() > 0 && (cmdCount == 5 || i == (commandsList.size() - 1))) + if (sb.length() > 0 && (cmdCount == 6 || i == (commandsList.size() - 1))) { send(sender, helpIndent(sb.toString())); @@ -1466,16 +1234,6 @@ public class Mobibot extends PircBot isPrivate); } - /** - * Returns <code>true</code> if Google search is enabled. - * - * @return <code>true</code> or <code>false</code> - */ - private boolean isGseEnabled() - { - return Utils.isValidString(googleApiKey) && Utils.isValidString(googleCseCx); - } - /** * Determines whether the specified nick should be ignored. * @@ -1511,81 +1269,6 @@ public class Mobibot extends PircBot return false; } - /** - * Returns <code>true</code> if twitter posting is enabled. - * - * @return <code>true</code> or <code>false</code> - */ - private boolean isTwitterEnabled() - { - return Utils.isValidString(twitterConsumerKey) && Utils.isValidString(twitterConsumerSecret) && Utils - .isValidString(twitterToken) && Utils.isValidString(twitterTokenSecret); - } - - /** - * Responds with the results of a DNS query. - * - * @param sender The nick of the person who sent the message - * @param query The hostname or IP address. - */ - private void lookupResponse(final String sender, final String query) - { - if (query.matches("(\\S.)+(\\S)+")) - { - try - { - send(channel, Lookup.lookup(query)); - } - catch (UnknownHostException ignore) - { - if (query.matches( - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) - { - try - { - final String[] lines = Lookup.whois(query); - - if ((lines != null) && (lines.length > 0)) - { - String line; - - for (final String rawLine : lines) - { - line = rawLine.trim(); - - if ((line.length() > 0) && (line.charAt(0) != '#')) - { - send(channel, line); - } - } - } - else - { - send(channel, "Unknown host."); - } - } - catch (IOException ioe) - { - if (logger.isDebugEnabled()) - { - logger.debug("Unable to perform whois IP lookup: " + query, ioe); - } - - send(channel, "Unable to perform whois IP lookup: " + ioe.getMessage()); - } - } - else - { - send(channel, "Unknown host."); - } - } - } - else - { - helpResponse(sender, Commands.LOOKUP_CMD); - } - } - @Override protected final void onDisconnect() { @@ -1768,33 +1451,6 @@ public class Mobibot extends PircBot { helpResponse(sender, args); } - // mobibot: ping - else if (cmd.equals(Commands.PING_CMD)) - { - final String[] pings = { - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!" - }; - - final Random r = new Random(); - - action(channel, pings[r.nextInt(pings.length)]); - } - // mobibot: pong - else if (cmd.equals(Commands.PONG_CMD)) - { - send(channel, Commands.PING_CMD, true); - } // mobibot: recap else if (cmd.equals(Commands.RECAP_CMD)) { @@ -1815,94 +1471,40 @@ public class Mobibot extends PircBot { versionResponse(sender, false); } - // mobibot: dice - else if (cmd.equals(Commands.DICE_CMD)) - { - if (!lastPlayer.equalsIgnoreCase(sender)) - { - send(channel, SHALL_WE_PLAY_A_GAME); - lastPlayer = sender; - } - - Dice.roll(this, sender); - } - // mobibot: war - else if (cmd.equals(Commands.WAR_CMD)) - { - if (!lastPlayer.equalsIgnoreCase(sender)) - { - send(channel, SHALL_WE_PLAY_A_GAME); - lastPlayer = sender; - } - - War.play(this, sender); - } // mobibot: <channel> else if (cmd.equalsIgnoreCase(channel.substring(1))) { feedResponse(sender); } - // mobibot: currency - else if (cmd.startsWith(Commands.CURRENCY_CMD)) - { - currencyConverter.setQuery(sender, args); - new Thread(currencyConverter).start(); - } - // mobibot: lookup - else if (cmd.startsWith(Commands.LOOKUP_CMD)) - { - lookupResponse(sender, args); - } // mobibot: view else if (cmd.startsWith(Commands.VIEW_CMD)) { viewResponse(sender, args, false); } - // mobibot: google - else if (cmd.startsWith(Commands.GOOGLE_CMD) && isGseEnabled()) - { - googleResponse(sender, args); - } - // mobibot: twitter - else if (cmd.startsWith(Commands.TWITTER_CMD) && isTwitterEnabled()) - { - twitterResponse(sender, args); - } - // mobibot: stock - else if (cmd.startsWith(Commands.STOCK_CMD)) - { - stockResponse(sender, args); - } - // mobibot: joke - else if (cmd.startsWith(Commands.JOKE_CMD)) - { - new Thread(new Joke(this, sender)).start(); - } - // mobibot: calc - else if (cmd.startsWith(Commands.CALC_CMD)) - { - calcResponse(sender, args, message); - } - // mobibot: time - else if (cmd.startsWith(Commands.TIME_CMD)) - { - worldTime.timeResponse(this, sender, args, false); - } // mobibot: tell else if (cmd.startsWith(Commands.TELL_CMD) && isTellEnabled()) { tellResponse(sender, args); } - // mobibot: weather - else if (cmd.startsWith(Commands.WEATHER_CMD)) - { - weatherResponse(sender, args, false); - } // mobibot: ignore else if (cmd.startsWith(Commands.IGNORE_CMD)) { ignoreResponse(sender, args); } + // modules + else + { + for (final AbstractModule module : MODULES) + { + for (final String c : module.getCommands()) + { + if (cmd.startsWith(c)) + { + module.commandResponse(this, sender, args, false); + } + } + } + } } // L1:<comment>, L1:-, L1:|<title>, etc. else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) @@ -2172,24 +1774,18 @@ public class Mobibot extends PircBot { helpResponse(sender, args); } - else if ("kill".equals(cmd)) + else if ("kill".equals(cmd) && isOp(sender)) { - if (isOp(sender)) - { - sendRawLine("QUIT : Poof!"); - System.exit(0); - } + sendRawLine("QUIT : Poof!"); + System.exit(0); } - else if (cmd.equals(Commands.DIE_CMD)) + else if (cmd.equals(Commands.DIE_CMD) && isOp(sender)) { - if (isOp(sender)) - { - send(channel, sender + " has just signed my death sentence."); - saveEntries(true); - sleep(3); - quitServer("The Bot Is Out There!"); - System.exit(0); - } + send(channel, sender + " has just signed my death sentence."); + saveEntries(true); + sleep(3); + quitServer("The Bot Is Out There!"); + System.exit(0); } else if (cmd.equals(Commands.CYCLE_CMD)) { @@ -2207,97 +1803,77 @@ public class Mobibot extends PircBot { usersResponse(sender, true); } - else if (cmd.startsWith(Commands.ADDLOG_CMD) && (cmds.length > 1)) + else if (cmd.equals(Commands.ADDLOG_CMD) && (cmds.length > 1) && isOp(sender)) { - if (isOp(sender)) + // e.g. 2014-04-01 + final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); + if (backlog.exists()) { - // e.g. 2014-04-01 - final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); - if (backlog.exists()) - { - history.add(0, args); - send(sender, history.toString(), true); - } - else - { - send(sender, "The specified log could not be found."); - } + history.add(0, args); + send(sender, history.toString(), true); + } + else + { + send(sender, "The specified log could not be found."); } } - else if (cmd.startsWith(Commands.ME_CMD)) + else if (cmd.equals(Commands.ME_CMD) && isOp(sender)) { - if (isOp(sender)) + if (args.length() > 1) { - if (args.length() > 1) - { - action(args); - } - else - { - helpResponse(sender, Commands.ME_CMD); - } + action(args); + } + else + { + helpResponse(sender, Commands.ME_CMD); } } - else if (cmd.startsWith(Commands.NICK_CMD) && (cmds.length > 1)) + else if (cmd.equals(Commands.NICK_CMD) && (cmds.length > 1)) { if (isOp(sender)) { changeNick(args); } } - else if (cmd.startsWith(Commands.SAY_CMD)) + else if (cmd.equals(Commands.SAY_CMD) && isOp(sender)) { - if (isOp(sender)) + if (cmds.length > 1) { - if (cmds.length > 1) - { - send(channel, args, true); - } - else - { - helpResponse(sender, Commands.SAY_CMD); - } + send(channel, args, true); + } + else + { + helpResponse(sender, Commands.SAY_CMD); } } - else if (cmd.startsWith(Commands.MSG_CMD)) + else if (cmd.equals(Commands.MSG_CMD) && isOp(sender)) { - if (isOp(sender)) + if (cmds.length > 1) { - if (cmds.length > 1) - { - final String[] msg = args.split(" ", 2); + final String[] msg = args.split(" ", 2); - if (args.length() > 2) - { - send(msg[0], msg[1], true); - } - else - { - helpResponse(sender, Commands.MSG_CMD); - } + if (args.length() > 2) + { + send(msg[0], msg[1], true); } else { helpResponse(sender, Commands.MSG_CMD); } } + else + { + helpResponse(sender, Commands.MSG_CMD); + } } - else if (cmd.startsWith(Commands.VIEW_CMD)) + else if (cmd.equals(Commands.VIEW_CMD)) { viewResponse(sender, args, true); } - else if (cmd.startsWith(Commands.TIME_CMD)) - { - worldTime.timeResponse(this, sender, args, true); - } - else if (cmd.startsWith(Commands.TELL_CMD) && isTellEnabled()) + else if (cmd.equals(Commands.TELL_CMD) && isTellEnabled()) { tellResponse(sender, args); } - else if (cmd.startsWith(Commands.WEATHER_CMD)) - { - weatherResponse(sender, args, true); - } else if (cmd.equals(Commands.INFO_CMD)) { infoResponse(sender, true); @@ -2324,6 +1900,21 @@ public class Mobibot extends PircBot } else { + for (final AbstractModule module : MODULES) + { + if (module.isPrivateMsgEnabled()) + { + for (final String c : module.getCommands()) + { + if (cmd.equals(c)) + { + module.commandResponse(this, sender, args, true); + return; + } + } + } + } + helpResponse(sender, ""); } } @@ -2501,24 +2092,6 @@ public class Mobibot extends PircBot send(sender, message, false); } - /** - * Responds with the specified stock quote. - * - * @param sender The nick of the person who sent the message. - * @param symbol The stock symbol to lookup. - */ - private void stockResponse(final String sender, final String symbol) - { - if (symbol.length() > 0) - { - new Thread(new StockQuote(this, sender, symbol)).start(); - } - else - { - helpResponse(sender, Commands.STOCK_CMD); - } - } - /** * Processes the {@link Commands#TELL_CMD} commands. * @@ -2702,37 +2275,6 @@ public class Mobibot extends PircBot } } - /** - * Posts a message to Twitter. - * - * @param sender The sender's nick. - * @param message The message. - */ - private void twitterResponse(final String sender, final String message) - { - if (isTwitterEnabled()) - { - if (message.length() > 0) - { - new Thread(new Twitter(this, - sender, - twitterConsumerKey, - twitterConsumerSecret, - twitterToken, - twitterTokenSecret, - message)).start(); - } - else - { - helpResponse(sender, Commands.TWITTER_CMD); - } - } - else - { - send(sender, "The Twitter posting facility is disabled."); - } - } - /** * Responds with the users on a channel. * @@ -2885,16 +2427,4 @@ public class Mobibot extends PircBot send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate); } } - - /** - * Responds with weather from the specified station ID. - * - * @param sender The nick of the person who sent the message. - * @param id The station's ID. - * @param isPrivate Set to true is the response should be send as a private message. - */ - private void weatherResponse(final String sender, final String id, final boolean isPrivate) - { - new Thread(new Weather(this, sender, id, isPrivate)).start(); - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 69267e9..424fb80 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -34,10 +34,6 @@ package net.thauvin.erik.mobibot; import org.jibble.pircbot.Colors; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -48,7 +44,7 @@ import java.util.Calendar; * @created 2014-04-26 * @since 1.0 */ -final class Utils +final public class Utils { /** * The timestamp simple date format. @@ -58,12 +54,12 @@ final class Utils /** * The ISO (YYYY-MM-DD) simple date format. */ - static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); + public static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); /** * The UTC (yyyy-MM-dd HH:mm) simple date format. */ - static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + public static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); /** * Disables the default constructor. @@ -191,71 +187,6 @@ final class Utils return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); } - /** - * Copies a file. - * - * @param in The source file. - * @param out The destination file. - * - * @throws java.io.IOException If the file could not be copied. - */ - @SuppressWarnings("UnusedDeclaration") - public static void copyFile(final File in, final File out) - throws IOException - { - FileChannel inChannel = null; - FileChannel outChannel = null; - FileInputStream input = null; - FileOutputStream output = null; - - try - { - input = new FileInputStream(in); - output = new FileOutputStream(out); - - inChannel = input.getChannel(); - outChannel = output.getChannel(); - - inChannel.transferTo(0L, inChannel.size(), outChannel); - } - finally - { - try - { - if (inChannel != null) - { - inChannel.close(); - } - - if (input != null) - { - input.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - - try - { - if (outChannel != null) - { - outChannel.close(); - } - - if (output != null) - { - output.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - } - } - /** * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. * @@ -264,7 +195,7 @@ final class Utils * * @return The location ending with a slash. */ - public static String ensureDir(final String location, final boolean isUrl) + static String ensureDir(final String location, final boolean isUrl) { if (isUrl) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java new file mode 100644 index 0000000..7c4d90d --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -0,0 +1,138 @@ +/* + * Module.java + * + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; + +import java.util.*; + +/** + * The <code>Module</code> class. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2016-07-01 + * @since 1.0 + */ +public abstract class AbstractModule +{ + + final List<String> commands = new ArrayList<>(); + + final Map<String, String> properties = new HashMap<>(); + + /** + * Responds to a command. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + public abstract void commandResponse(final Mobibot bot, final String sender, final String args, + final boolean isPrivate); + + /** + * Returns the module's commands, if any. + * + * @return The commands. + */ + public List<String> getCommands() + { + return commands; + } + + /** + * Returns the module's property keys. + * + * @return The keys. + */ + public Set<String> getPropertyKeys() + { + return properties.keySet(); + } + + /** + * Returns <code>true</code> if the module has properties. + * + * @return <code>true</code> or <code>false</code> . + */ + public boolean hasProperties() + { + return !properties.isEmpty(); + } + + /** + * Responds with the module's help. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The help arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + public abstract void helpResponse(final Mobibot bot, final String sender, final String args, + final boolean isPrivate); + + /** + * Returns <code>true</code> if the module is enabled. + * + * @return <code>true</code> or <code>false</code> + */ + public boolean isEnabled() + { + return true; + } + + /** + * Returns <codde>true</codde> if the module responds to private messages. + * + * @return <code>true</code> or <code>false</code> + */ + public boolean isPrivateMsgEnabled() + { + return false; + } + + /** + * Sets a property key and value. + * + * @param key The key. + * @param value The value. + */ + public void setProperty(final String key, final String value) + { + if (Utils.isValidString(key)) + { + properties.put(key, value); + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java new file mode 100644 index 0000000..e5dbe92 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -0,0 +1,98 @@ +/* + * Calc.java + * + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; + +import java.text.DecimalFormat; + +/** + * The Calc module. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2016-07-01 + * @since 1.0 + */ +public class Calc extends AbstractModule +{ + /** + * The Calc command. + */ + private static final String CALC_CMD = "calc"; + + /** + * The default constructor. + */ + public Calc() + { + commands.add(CALC_CMD); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + if (Utils.isValidString(args)) + { + final DecimalFormat decimalFormat = new DecimalFormat("#.##"); + + try + { + final Expression calc = new ExpressionBuilder(args).build(); + bot.send(bot.getChannel(), args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate())); + } + catch (Exception e) + { + if (bot.getLogger().isDebugEnabled()) + { + bot.getLogger().debug("Unable to calculate: " + args, e); + } + + bot.send(bot.getChannel(), "No idea. This is the kind of math I don't get."); + } + } + else + { + helpResponse(bot, sender, args, isPrivate); + } + + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To solve a mathematical calculation:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>")); + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java similarity index 78% rename from src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java rename to src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 121d452..b0616b6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -29,8 +29,10 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; @@ -46,14 +48,24 @@ import java.util.Map; import java.util.TreeMap; /** - * Processes the {@link Commands#CURRENCY_CMD} command. + * The CurrentConverter module. * * @author Erik C. Thauvin * @created Feb 11, 2004 * @since 1.0 */ -class CurrencyConverter implements Runnable +final public class CurrencyConverter extends AbstractModule { + /** + * The currency command. + */ + private static final String CURRENCY_CMD = "currency"; + + /** + * The rates keyword. + */ + private static final String CURRENCY_RATES_KEYWORD = "rates"; + /** * The exchange rates. */ @@ -64,40 +76,54 @@ class CurrencyConverter implements Runnable */ private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; - /** - * The bot. - */ - private final Mobibot bot; - /** * The last exchange rates table publication date. */ private String pubDate = ""; - /** - * The actual currency query. - */ - private String query; - - /** - * The nick of the person who sent the message. - */ - private String sender; - /** * Creates a new {@link CurrencyConverter} instance. - * - * @param bot The bot's instance. */ - public CurrencyConverter(final Mobibot bot) + public CurrencyConverter() { - this.bot = bot; + commands.add(CURRENCY_CMD); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + synchronized (this) + { + if (!pubDate.equals(Utils.today())) + { + EXCHANGE_RATES.clear(); + } + } + + new Thread(() -> { + run(bot, sender, args); + }).start(); + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To convert from one currency to another:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); + + if (args.endsWith(CURRENCY_CMD)) + { + bot.send(sender, "For a listing of currency rates:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); + bot.send(sender, "For a listing of supported currencies:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); + } } /** * Converts the specified currencies. */ - public final void run() + private void run(final Mobibot bot, final String sender, final String query) { if (Utils.isValidString(sender)) { @@ -183,7 +209,7 @@ class CurrencyConverter implements Runnable } } } - else if (query.equals(Commands.CURRENCY_RATES_KEYWORD)) + else if (query.equals(CURRENCY_RATES_KEYWORD)) { bot.send(sender, "Last Update: " + pubDate); @@ -204,27 +230,9 @@ class CurrencyConverter implements Runnable } else { - bot.helpResponse(sender, Commands.CURRENCY_CMD + ' ' + query); + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); } } } - - /** - * Sets the query. - * - * @param sender The nick of the person who sent the message. - * @param query The currency query. - */ - - public synchronized void setQuery(final String sender, final String query) - { - this.query = query; - this.sender = sender; - - if (!pubDate.equals(Utils.today())) - { - EXCHANGE_RATES.clear(); - } - } -} +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java similarity index 73% rename from src/main/java/net/thauvin/erik/mobibot/Dice.java rename to src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index eeb9f8b..e9d251f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -29,37 +29,45 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import java.util.Random; /** - * Processes the {@link Commands#DICE_CMD} command. + * The Dice module. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ -final class Dice +final public class Dice extends AbstractModule { /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. + * The dice command. */ - private Dice() - throws UnsupportedOperationException + private final String DICE_CMD = "dice"; + + /** + * The default constructor. + */ + public Dice() { - throw new UnsupportedOperationException("Illegal constructor call."); + commands.add(DICE_CMD); } /** * Rolls the dice. * * @param bot The bot's instance. - * @param sender The sender's nickname. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public static void roll(final Mobibot bot, final String sender) + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { final Random r = new Random(); @@ -91,4 +99,17 @@ final class Dice bot.action("tied."); } } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To roll the dice:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD)); + } + + @Override + public boolean isEnabled() + { + return true; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java similarity index 62% rename from src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java rename to src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index f2bb11e..9becd44 100644 --- a/src/main/java/net/thauvin/erik/mobibot/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -29,8 +29,10 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import org.json.JSONArray; import org.json.JSONObject; @@ -41,67 +43,78 @@ import java.net.URLConnection; import java.net.URLEncoder; /** - * Processes the {@link Commands#GOOGLE_CMD} command. + * The GoogleSearch module. * * @author Erik C. Thauvin * @created Feb 7, 2004 * @since 1.0 */ -class GoogleSearch implements Runnable +final public class GoogleSearch extends AbstractModule { + /** + * The Google API Key property. + */ + private static final String GOOGLE_API_KEY_PROP = "google-api-key"; + + /** + * The google command. + */ + private static final String GOOGLE_CMD = "google"; + + /** + * The Google Custom Search Engine ID property. + */ + private static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; + /** * The tab indent (4 spaces). */ private static final String TAB_INDENT = " "; - /** - * The bot. - */ - private final Mobibot bot; - - /** - * The Google Custom Search Engine ID. - */ - private final String cseCx; - - /** - * The search query. - */ - private final String query; - - /** - * The nick of the person who sent the message. - */ - private final String sender; - /** * Creates a new {@link GoogleSearch} instance. - * - * @param bot The bot's instance. - * @param cseCx The Google Custom Search Engine ID. - * @param sender The nick of the person who sent the message. - * @param query The Google query */ - public GoogleSearch(final Mobibot bot, final String cseCx, final String sender, final String query) + public GoogleSearch() { - this.bot = bot; - this.cseCx = cseCx; - this.sender = sender; - this.query = query; + commands.add(GOOGLE_CMD); + properties.put(GOOGLE_API_KEY_PROP, ""); + properties.put(GOOGLE_CSE_KEY_PROP, ""); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + if (isEnabled() && args.length() > 0) + { + if (args.length() > 0) + { + new Thread(() -> { + run(bot, sender, args); + }).start(); + } + else + { + helpResponse(bot, sender, args, isPrivate); + } + } + else + { + helpResponse(bot, sender, args, isPrivate); + } } /** * Searches Google. */ - public final void run() + private void run(final Mobibot bot, final String sender, final String query) { try { - final String query = URLEncoder.encode(this.query, "UTF-8"); + final String q = URLEncoder.encode(query, "UTF-8"); final URL url = - new URL("https://www.googleapis.com/customsearch/v1?key=" + bot.getGoogleApiKey() + "&cx=" + cseCx - + "&q=" + query + "&filter=1&num=5&alt=json"); + new URL("https://www.googleapis.com/customsearch/v1?key=" + properties.get(GOOGLE_API_KEY_PROP) + + "&cx=" + properties.get(GOOGLE_CSE_KEY_PROP) + "&q=" + q + "&filter=1&num=5&alt=json"); final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); @@ -131,4 +144,25 @@ class GoogleSearch implements Runnable bot.send(sender, "An error has occurred: " + e.getMessage()); } } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + if (isEnabled()) + { + bot.send(sender, "To search Google:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); + } + else + { + bot.send(sender, "The Google searching facility is disabled."); + } + } + + @Override + public boolean isEnabled() + { + return Utils.isValidString(properties.get(GOOGLE_API_KEY_PROP)) && Utils + .isValidString(properties.get(GOOGLE_CSE_KEY_PROP)); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java similarity index 80% rename from src/main/java/net/thauvin/erik/mobibot/Joke.java rename to src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 69a6475..8d3f19a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -29,8 +29,9 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Mobibot; import org.jibble.pircbot.Colors; import org.json.JSONObject; @@ -40,47 +41,53 @@ import java.net.URL; import java.net.URLConnection; /** - * Processes the {@link Commands#JOKE_CMD} command. + * The Joke module. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-20 * @since 1.0 */ -class Joke implements Runnable +final public class Joke extends AbstractModule { + /** + * The joke command. + */ + private static final String JOKE_CMD = "joke"; + /** * The ICNDB URL. */ private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; - /** - * The bot's instance. - */ - private final Mobibot bot; - - /** - * The nick of the person who sent the message. - */ - private final String sender; - /** * Creates a new {@link Joke} instance. - * - * @param bot The bot's instance. - * @param sender The nick of the person who sent the message. */ - public Joke(final Mobibot bot, final String sender) + public Joke() { - this.bot = bot; - this.sender = sender; + commands.add(JOKE_CMD); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + new Thread(() -> { + run(bot, sender); + }).start(); + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To retrieve a random joke:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD)); } /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> */ - public final void run() + private void run(final Mobibot bot, final String sender) { try { diff --git a/src/main/java/net/thauvin/erik/mobibot/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java similarity index 62% rename from src/main/java/net/thauvin/erik/mobibot/Lookup.java rename to src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 632184e..b42c686 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -29,8 +29,9 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Mobibot; import org.apache.commons.net.whois.WhoisClient; import java.io.IOException; @@ -38,14 +39,18 @@ import java.net.InetAddress; import java.net.UnknownHostException; /** - * Processes the {@link Commands#LOOKUP_CMD} command. + * The Lookup module. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ -final class Lookup +final public class Lookup extends AbstractModule { + /** + * THe lookup command. + */ + private static final String LOOKUP_CMD = "lookup"; /** * The whois host. @@ -53,14 +58,78 @@ final class Lookup private static final String WHOIS_HOST = "whois.arin.net"; /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. + * The default constructor */ - private Lookup() - throws UnsupportedOperationException + public Lookup() { - throw new UnsupportedOperationException("Illegal constructor call."); + commands.add(LOOKUP_CMD); + } + + /** + * Process a command. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + if (args.matches("(\\S.)+(\\S)+")) + { + try + { + bot.send(bot.getChannel(), Lookup.lookup(args)); + } + catch (UnknownHostException ignore) + { + if (args.matches( + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) + { + try + { + final String[] lines = Lookup.whois(args); + + if ((lines != null) && (lines.length > 0)) + { + String line; + + for (final String rawLine : lines) + { + line = rawLine.trim(); + + if ((line.length() > 0) && (line.charAt(0) != '#')) + { + bot.send(bot.getChannel(), line); + } + } + } + else + { + bot.send(bot.getChannel(), "Unknown host."); + } + } + catch (IOException ioe) + { + if (bot.getLogger().isDebugEnabled()) + { + bot.getLogger().debug("Unable to perform whois IP lookup: " + args, ioe); + } + + bot.send(bot.getChannel(), "Unable to perform whois IP lookup: " + ioe.getMessage()); + } + } + else + { + bot.send(bot.getChannel(), "Unknown host."); + } + } + } + else + { + helpResponse(bot, sender, args, true); + } } /** @@ -72,7 +141,7 @@ final class Lookup * * @throws java.net.UnknownHostException If the host is unknown. */ - public static String lookup(final String query) + private static String lookup(final String query) throws UnknownHostException { final StringBuilder buffer = new StringBuilder(""); @@ -116,7 +185,7 @@ final class Lookup * * @throws java.io.IOException If a connection error occurs. */ - public static String[] whois(final String query) + private static String[] whois(final String query) throws IOException { return whois(query, WHOIS_HOST); @@ -155,4 +224,11 @@ final class Lookup return lines; } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To perform a DNS lookup query:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>")); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java new file mode 100644 index 0000000..efed060 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -0,0 +1,67 @@ +/* + * Ping.java + * + * Copyright (c) 2016 Erik C. Thauvin (http://erik.thauvin.net/) + * All rights reserved. + */ +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.Mobibot; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * The <code>Ping</code> class. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2016-07-02 + * @since 1.0 + */ +public class Ping extends AbstractModule +{ + /** + * The ping responses. + */ + private static final List<String> PINGS = Arrays.asList("is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!"); + + /** + * The ping command. + */ + private static final String PING_CMD = "ping"; + + /** + * The default constructor. + */ + public Ping() + { + commands.add(PING_CMD); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + final Random r = new Random(); + + bot.action(PINGS.get(r.nextInt(PINGS.size()))); + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To ping the bot:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + PING_CMD)); + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java similarity index 81% rename from src/main/java/net/thauvin/erik/mobibot/StockQuote.java rename to src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index e026841..1ec40bf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -29,61 +29,68 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; import com.Ostermiller.util.CSVParser; +import net.thauvin.erik.mobibot.Mobibot; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; import java.io.IOException; /** - * Processes the {@link Commands#STOCK_CMD} command. + * The StockQuote module. * * @author Erik C. Thauvin * @created Feb 7, 2004 * @since 1.0 */ -class StockQuote implements Runnable +final public class StockQuote extends AbstractModule { + /** + * The quote command. + */ + private static final String STOCK_CMD = "stock"; + /** * The Yahoo! stock quote URL. */ private static final String YAHOO_URL = "http://finance.yahoo.com/d/quotes.csv?&f=snl1d1t1c1oghv&e=.csv&s="; - /** - * The bot. - */ - private final Mobibot bot; - - /** - * The nick of the person who sent the message. - */ - private final String sender; - - /** - * The stock symbol. - */ - private final String symbol; - /** * Creates a new {@link StockQuote} instance. - * - * @param bot The bot's instance. - * @param sender The nick of the person who sent the message. - * @param symbol The stock symbol. */ - public StockQuote(final Mobibot bot, final String sender, final String symbol) + public StockQuote() { - this.bot = bot; - this.sender = sender; - this.symbol = symbol; + commands.add(STOCK_CMD); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + if (args.length() > 0) + { + new Thread(() -> { + run(bot, sender, args); + }).start(); + } + else + { + helpResponse(bot, sender, args, isPrivate); + } + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To retrieve a stock quote:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol[.country code]>")); } /** * Returns the specified stock quote from Yahoo! */ - public final void run() + private void run(final Mobibot bot, final String sender, final String symbol) { try { diff --git a/src/main/java/net/thauvin/erik/mobibot/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java similarity index 54% rename from src/main/java/net/thauvin/erik/mobibot/Twitter.java rename to src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 9096603..e518442 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -29,89 +29,97 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import twitter4j.Status; import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; /** - * Processes the {@link Commands#TWITTER_CMD} command. + * The Twitter module. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created Sept 10, 2008 * @since 1.0 */ -class Twitter implements Runnable +final public class Twitter extends AbstractModule { - /** - * The Twitter access token. - */ - private final String accessToken; + private final static String CONSUMER_KEY_PROP = "twitter-consumerKey"; + + private final static String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; + + private final static String TOKEN_PROP = "twitter-token"; + + private final static String TOKEN_SECRET_PROP = "twitter-tokenSecret"; /** - * The Twitter access token secret. + * The twitter command. */ - private final String accessTokenSecret; - - /** - * The bot. - */ - private final Mobibot bot; - - /** - * The Twitter consumer key. - */ - private final String consumerKey; - - /** - * The Twitter consumer secret. - */ - private final String consumerSecret; - - /** - * The Twitter message. - */ - private final String message; - - /** - * The nick of the person who sent the message. - */ - private final String sender; + private final static String TWITTER_CMD = "twitter"; /** * Creates a new {@link Twitter} instance. - * - * @param bot The bot's instance. - * @param sender The nick of the person who sent the message. - * @param consumerKey The Twitter consumer key. - * @param consumerSecret The Twitter consumer secret. - * @param accessToken The Twitter access token. - * @param accessTokenSecret The Twitter access token secret. - * @param message The Twitter message. */ - public Twitter(final Mobibot bot, final String sender, final String consumerKey, final String consumerSecret, - final String accessToken, final String accessTokenSecret, final String message) + public Twitter() { - this.bot = bot; - this.consumerKey = consumerKey; - this.consumerSecret = consumerSecret; - this.accessToken = accessToken; - this.accessTokenSecret = accessTokenSecret; - this.message = message; - this.sender = sender; + commands.add(TWITTER_CMD); + properties.put(CONSUMER_SECRET_PROP, ""); + properties.put(CONSUMER_KEY_PROP, ""); + properties.put(TOKEN_PROP, ""); + properties.put(TOKEN_SECRET_PROP, ""); + } + + @Override + public boolean isEnabled() + { + return Utils.isValidString(properties.get(CONSUMER_KEY_PROP)) && Utils + .isValidString(properties.get(CONSUMER_SECRET_PROP)) && Utils.isValidString(properties.get(TOKEN_PROP)) + && Utils.isValidString(properties.get(TOKEN_SECRET_PROP)); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + if (isEnabled() && args.length() > 0) + { + new Thread(() -> { + run(bot, sender, args); + }).start(); + } + else + { + helpResponse(bot, sender, args, isPrivate); + } + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + if (isEnabled()) + { + bot.send(sender, "To post to Twitter:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>")); + } + else + { + bot.send(sender, "The Twitter posting facility is disabled."); + } } /** * Posts to twitter. */ - public final void run() + private void run(final Mobibot bot, final String sender, final String message) { try { final ConfigurationBuilder cb = new ConfigurationBuilder(); - cb.setDebugEnabled(true).setOAuthConsumerKey(consumerKey).setOAuthConsumerSecret(consumerSecret) - .setOAuthAccessToken(accessToken).setOAuthAccessTokenSecret(accessTokenSecret); + cb.setDebugEnabled(true).setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) + .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) + .setOAuthAccessToken(properties.get(TOKEN_PROP)) + .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); final TwitterFactory tf = new TwitterFactory(cb.build()); final twitter4j.Twitter twitter = tf.getInstance(); diff --git a/src/main/java/net/thauvin/erik/mobibot/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java similarity index 74% rename from src/main/java/net/thauvin/erik/mobibot/War.java rename to src/main/java/net/thauvin/erik/mobibot/modules/War.java index 4798a3b..182d5ee 100644 --- a/src/main/java/net/thauvin/erik/mobibot/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -29,48 +29,56 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import java.util.Random; /** - * Processes the {@link Commands#WAR_CMD} command. + * The War module. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ -final class War +final public class War extends AbstractModule { /** - * The deck of card for the {@link net.thauvin.erik.mobibot.Commands#WAR_CMD war} command. + * The war command. + */ + private static final String WAR_CMD = "war"; + + /** + * The deck of card. */ private static final String[] WAR_DECK = new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; /** - * The suits for the deck of card for the {@link Commands#WAR_CMD war} command. + * The suits for the deck of card. */ private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. + * The default constructor. */ - private War() - throws UnsupportedOperationException + public War() { - throw new UnsupportedOperationException("Illegal constructor call."); + commands.add(WAR_CMD); } /** * Plays war. * * @param bot The bot's instance. - * @param sender The sender's nickname. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public static void play(final Mobibot bot, final String sender) + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { final Random r = new Random(); @@ -102,9 +110,12 @@ final class War { bot.action("wins."); } - else - { - bot.action("tied."); - } + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To play war:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WAR_CMD)); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Weather.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java similarity index 77% rename from src/main/java/net/thauvin/erik/mobibot/Weather.java rename to src/main/java/net/thauvin/erik/mobibot/modules/Weather.java index e0b457d..832678b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java @@ -29,74 +29,75 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; import net.sf.jweather.metar.Metar; import net.sf.jweather.metar.SkyCondition; import net.sf.jweather.metar.WeatherCondition; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import java.text.DecimalFormat; import java.util.Date; /** - * Processes the {@link Commands#LOOKUP_CMD} command. + * The Weather module * * @author Erik C. Thauvin * @created Feb 7, 2004 * @since 1.0 */ -class Weather implements Runnable +final public class Weather extends AbstractModule { - /** - * The URL where the stations are listed. - */ - public static final String STATIONS_URL = "http://www.rap.ucar.edu/weather/surface/stations.txt"; - /** * The decimal number format. */ private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("0.##"); /** - * The bot. + * The URL where the stations are listed. */ - private final Mobibot bot; + private static final String STATIONS_URL = "http://www.rap.ucar.edu/weather/surface/stations.txt"; /** - * The private message flag. + * THe weather command. */ - private final boolean isPrivate; - - /** - * The nick of the person who sent the message. - */ - private final String sender; - - /** - * The station ID. - */ - private final String station; + private static final String WEATHER_CMD = "weather"; /** * Creates a new {@link Weather} instance. - * - * @param bot The bot's instance. - * @param sender The nick of the person who sent the message. - * @param station The station ID. - * @param isPrivate Set to true is the response should be send as a private message. */ - public Weather(final Mobibot bot, final String sender, final String station, final boolean isPrivate) + public Weather() { - this.bot = bot; - this.sender = sender; - this.station = station.toUpperCase(); - this.isPrivate = isPrivate; + commands.add(WEATHER_CMD); + } + + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + new Thread(() -> { + run(bot, sender, args.toUpperCase(), isPrivate); + }).start(); + } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To display weather information:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <station id>")); + bot.send(sender, "For a listing of the ICAO station IDs, please visit: " + STATIONS_URL); + } + + @Override + public boolean isPrivateMsgEnabled() + { + return true; } /** * Fetches the weather data from a specific station ID. */ - public final void run() + private void run(final Mobibot bot, final String sender, final String station, final boolean isPrivate) { if (station.length() == 4) { @@ -177,6 +178,6 @@ class Weather implements Runnable } } - bot.helpResponse(sender, Commands.WEATHER_CMD); + helpResponse(bot, sender, station, isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java similarity index 82% rename from src/main/java/net/thauvin/erik/mobibot/WorldTime.java rename to src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 50a10f7..19fbcd0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -29,7 +29,10 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -38,13 +41,13 @@ import java.util.TimeZone; import java.util.TreeMap; /** - * The {@link Commands#TIME_CMD} command. + * The WorldTime module. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-27 * @since 1.0 */ -class WorldTime +final public class WorldTime extends AbstractModule { /** * The beats (Internet Time) keyword. @@ -52,12 +55,17 @@ class WorldTime private static final String BEATS_KEYWORD = ".beats"; /** - * The countries supported by the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. + * The supported countries. */ private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); /** - * The date/time format for the {@link net.thauvin.erik.mobibot.Commands#TIME_CMD time} command. + * The time command. + */ + private static final String TIME_CMD = "time"; + + /** + * The date/time format. */ private static final SimpleDateFormat TIME_SDF = new SimpleDateFormat("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"); @@ -67,6 +75,8 @@ class WorldTime */ public WorldTime() { + commands.add(TIME_CMD); + // Initialize the countries map COUNTRIES_MAP.put("AU", "Australia/Sydney"); COUNTRIES_MAP.put("BE", "Europe/Brussels"); @@ -140,12 +150,13 @@ class WorldTime /** * Responds with the current time in the specified timezone/country. * - * @param bot The bot instance. - * @param sender The nick of the person who sent the message. - * @param args The time command arguments. - * @param isPrivate Set to true is the response should be send as a private message. + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public final void timeResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { boolean isInvalidTz = false; final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); @@ -167,7 +178,7 @@ class WorldTime else { isInvalidTz = true; - response = "The supported time zones/countries are: " + COUNTRIES_MAP.keySet().toString(); + response = "The supported time zones are: " + COUNTRIES_MAP.keySet().toString(); } if (isPrivate) @@ -186,4 +197,20 @@ class WorldTime } } } + + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To display a country's current date/time:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); + + bot.send(sender, "For a listing of the supported countries:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD)); + } + + @Override + public boolean isPrivateMsgEnabled() + { + return true; + } } \ No newline at end of file diff --git a/version.properties b/version.properties index c4f5d54..5d34ed4 100644 --- a/version.properties +++ b/version.properties @@ -3,6 +3,6 @@ version.project=mobibot version.major=0 version.minor=6 -version.patch=1 +version.patch=5 version.prerelease=beta -version.buildmeta=008 +version.buildmeta=001 From 3e11fd9ee6a6bf8113cbda03ba12e2a6a211a739 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 2 Jul 2016 12:32:24 -0700 Subject: [PATCH 041/842] Moved tell methods to their own class. --- mobibot.ipr | 15 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 8 +- .../net/thauvin/erik/mobibot/Commands.java | 14 - .../net/thauvin/erik/mobibot/Mobibot.java | 451 ++---------------- .../java/net/thauvin/erik/mobibot/Tell.java | 428 +++++++++++++++++ .../thauvin/erik/mobibot/TellMessagesMgr.java | 4 +- .../erik/mobibot/modules/AbstractModule.java | 4 +- .../thauvin/erik/mobibot/modules/Ping.java | 29 +- version.properties | 6 +- 9 files changed, 527 insertions(+), 432 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/Tell.java diff --git a/mobibot.ipr b/mobibot.ipr index bd77a17..accde1d 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -58,9 +58,22 @@ </profile> </annotationProcessing> </component> - <component name="CopyrightManager" default="" /> + <component name="CopyrightManager" default=""> + <copyright> + <option name="myName" value="Copyright" /> + <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> + </copyright> + <module2copyright> + <element module="Copyright" copyright="Copyright" /> + </module2copyright> + <LanguageOptions name="JAVA"> + <option name="fileTypeOverride" value="3" /> + <option name="addBlankAfter" value="false" /> + </LanguageOptions> + </component> <component name="DependencyValidationManager"> <scope name="Source" pattern="file[mobibot]:src/generated/java//*||file[mobibot]:src/main/java//*" /> + <scope name="Copyright" pattern="file[mobibot]:src/main/java//*&&!file:src/main/java/net/thauvin/erik/mobibot/SwingWorker.java&&!file:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> </component> <component name="Encoding"> <file url="PROJECT" charset="UTF-8" /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 95241ad..fab3b47 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,11 +13,11 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "001"; - private final static Date date = new Date(1467451308717L); + private final static String buildmeta = "002"; + private final static Date date = new Date(1467486806272L); private final static int major = 0; - private final static int minor = 6; - private final static int patch = 5; + private final static int minor = 7; + private final static int patch = 0; private final static String prerelease = "beta"; private final static String project = "mobibot"; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index b849b5e..05d94a3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -135,20 +135,6 @@ final class Commands */ public static final String SAY_CMD = "say"; - /** - * The {@link #TELL_CMD} all command. - */ - public static final String TELL_ALL_CMD = "all"; - - /** - * The tell command. - */ - public static final String TELL_CMD = "tell"; - - /** - * The {@link #TELL_CMD} delete command. - */ - public static final String TELL_DEL_CMD = "del"; /** * The users command. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index f1e97a4..b6fa9c2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -44,7 +44,6 @@ import org.jsoup.nodes.Document; import java.io.*; import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; /** * Implements the #mobitopia bot. @@ -71,17 +70,6 @@ public class Mobibot extends PircBot */ private static final int DEFAULT_PORT = 6667; - /** - * The default maximum number of days to keep {@link Commands#TELL_CMD} messages. - */ - - private static final int DEFAULT_TELL_MAX_DAYS = 7; - - /** - * The default {@link Commands#TELL_CMD) message max queue size. - */ - private static final int DEFAULT_TELL_MAX_SIZE = 50; - /** * The info strings. */ @@ -120,11 +108,6 @@ public class Mobibot extends PircBot */ private static final List<AbstractModule> MODULES = new ArrayList<>(0); - /** - * The serialized object file extension. - */ - private static final String SER_EXT = ".ser"; - /** * The start time. */ @@ -148,6 +131,11 @@ public class Mobibot extends PircBot + System.getProperty("java.vm.info") + ')' }; + /** + * The tell object. + */ + private static Tell tell; + /** * The main channel. */ @@ -203,16 +191,6 @@ public class Mobibot extends PircBot */ private final List<String> recap = new ArrayList<>(0); - /** - * The serialized object file. - */ - private final String serializedObject; - - /** - * Processes the {@link Commands#TELL_CMD} messages queue. - */ - private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<>(); - /** * The backlogs URL. */ @@ -248,16 +226,6 @@ public class Mobibot extends PircBot */ private String identNick = ""; - /** - * The number of days message are kept. - */ - private int tellMaxDays = DEFAULT_TELL_MAX_DAYS; - - /** - * The maximum number of {@link Commands#TELL_CMD} messages allowed. - */ - private int tellMaxSize = DEFAULT_TELL_MAX_SIZE; - /** * Today's date. */ @@ -290,7 +258,6 @@ public class Mobibot extends PircBot ircPort = port; this.channel = channel; this.logsDir = logsDir; - this.serializedObject = logsDir + getName() + SER_EXT; // Set the logger level loggerLevel = logger.getLogger().getLevel(); @@ -493,13 +460,12 @@ public class Mobibot extends PircBot final String dname = p.getProperty("delicious-user"); final String dpwd = p.getProperty("delicious-pwd"); - // Get the tell command settings - final int tellMaxDays = Utils.getIntProperty(p.getProperty("tell-max-days"), DEFAULT_TELL_MAX_DAYS); - final int tellMaxSize = Utils.getIntProperty(p.getProperty("tell-max-size"), DEFAULT_TELL_MAX_SIZE); - // Create the bot final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir); + // Get the tell command settings + tell = new Tell(bot, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); + // Initialize the bot bot.setVerbose(true); bot.setAutoNickChange(true); @@ -539,9 +505,6 @@ public class Mobibot extends PircBot // Set the ignored nicks bot.setIgnoredNicks(ignoredNicks); - // Set the tell command - bot.setTell(tellMaxDays, tellMaxSize); - // Save the entries bot.saveEntries(true); @@ -590,13 +553,6 @@ public class Mobibot extends PircBot } bot.joinChannel(channel); - - // Load the messages queue - bot.tellMessages.addAll(TellMessagesMgr.load(bot.getSerializedObject(), bot.getLogger())); - if (bot.cleanTellMessages()) - { - bot.saveTellMessages(); - } } } @@ -679,18 +635,6 @@ public class Mobibot extends PircBot } } - /** - * Set the {@link Commands#TELL_CMD} parameters - * - * @param tellMaxDays The max number of days to hold messages for. - * @param tellMaxSize The maximum number of messages to hold - */ - private void setTell(final int tellMaxDays, final int tellMaxSize) - { - this.tellMaxDays = tellMaxDays; - this.tellMaxSize = tellMaxSize; - } - /** * Saves the entries. * @@ -718,49 +662,6 @@ public class Mobibot extends PircBot } } - /** - * Reruns the serialized object file. - * - * @return The file location. - */ - private String getSerializedObject() - { - return serializedObject; - } - - /** - * Returns the bot's logger. - * - * @return The bot's logger. - */ - public final Log4JLogger getLogger() - { - return logger; - } - - /** - * Cleans the {@link #tellMessages} messages queue. - * - * @return <code>True</code> if the queue was cleaned. - */ - private boolean cleanTellMessages() - { - if (logger.isDebugEnabled()) - { - logger.debug("Cleaning the messages."); - } - - return TellMessagesMgr.cleanTellMessages(tellMessages, tellMaxDays); - } - - /** - * Saves the {@link #tellMessages} messages queue. - */ - private void saveTellMessages() - { - TellMessagesMgr.save(getSerializedObject(), tellMessages, logger); - } - /** * Sends an action to the current channel. * @@ -869,6 +770,16 @@ public class Mobibot extends PircBot return this.ircServer; } + /** + * Returns the bot's logger. + * + * @return The bot's logger. + */ + public final Log4JLogger getLogger() + { + return logger; + } + /** * Returns the log directory. * @@ -958,7 +869,7 @@ public class Mobibot extends PircBot * * @return The indented help string. */ - private String helpIndent(final String help, final boolean isBold) + public String helpIndent(final String help, final boolean isBold) { return " " + (isBold ? Utils.bold(help) : help); } @@ -1054,15 +965,9 @@ public class Mobibot extends PircBot send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); } - else if (lcTopic.equals(Commands.TELL_CMD)) + else if (lcTopic.equals(Tell.TELL_CMD) && tell.isEnabled()) { - send(sender, "To send a message to someone when they join the channel:"); - send(sender, helpIndent(getNick() + ": " + Commands.TELL_CMD + " <nick> <message>")); - - send(sender, "To view queued and sent messages:"); - send(sender, helpIndent(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.VIEW_CMD)); - - send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days."); + tell.helpResponse(sender); } else { @@ -1098,9 +1003,9 @@ public class Mobibot extends PircBot MODULES.stream().filter(AbstractModule::isEnabled) .forEach(module -> commandsList.addAll(module.getCommands())); - if (isTellEnabled()) + if (tell.isEnabled()) { - commandsList.add(Commands.TELL_CMD); + commandsList.add(Tell.TELL_CMD); } Collections.sort(commandsList); @@ -1230,7 +1135,7 @@ public class Mobibot extends PircBot send(sender, "Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size() - + (isTellEnabled() && isOp(sender) ? ", Messages: " + tellMessages.size() : "") + ']', + + (tell.isEnabled() && isOp(sender) ? ", Messages: " + tell.size() : "") + ']', isPrivate); } @@ -1254,7 +1159,7 @@ public class Mobibot extends PircBot * * @return true, if the sender is an Op. */ - private boolean isOp(final String sender) + public boolean isOp(final String sender) { final User[] users = getUsers(channel); @@ -1482,9 +1387,9 @@ public class Mobibot extends PircBot viewResponse(sender, args, false); } // mobibot: tell - else if (cmd.startsWith(Commands.TELL_CMD) && isTellEnabled()) + else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) { - tellResponse(sender, args); + tell.response(sender, args); } // mobibot: ignore else if (cmd.startsWith(Commands.IGNORE_CMD)) @@ -1749,7 +1654,7 @@ public class Mobibot extends PircBot recap(sender, message, false); } - tellSendMessages(sender, true); + tell.send(sender, true); } @Override @@ -1870,9 +1775,9 @@ public class Mobibot extends PircBot { viewResponse(sender, args, true); } - else if (cmd.equals(Commands.TELL_CMD) && isTellEnabled()) + else if (cmd.equals(Tell.TELL_CMD) && tell.isEnabled()) { - tellResponse(sender, args); + tell.response(sender, args); } else if (cmd.equals(Commands.INFO_CMD)) { @@ -1882,21 +1787,18 @@ public class Mobibot extends PircBot { versionResponse(sender, true); } - else if (cmd.equals(Commands.DEBUG_CMD)) + else if (cmd.equals(Commands.DEBUG_CMD) && isOp(sender)) { - if (isOp(sender)) + if (logger.isDebugEnabled()) { - if (logger.isDebugEnabled()) - { - logger.getLogger().setLevel(loggerLevel); - } - else - { - logger.getLogger().setLevel(Level.DEBUG); - } - - send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); + logger.getLogger().setLevel(loggerLevel); } + else + { + logger.getLogger().setLevel(Level.DEBUG); + } + + send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); } else { @@ -1950,91 +1852,29 @@ public class Mobibot extends PircBot @Override protected void onJoin(final String channel, final String sender, final String login, final String hostname) { - tellSendMessages(sender); + tell.send(sender); } @Override protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { - tellSendMessages(newNick); + tell.send(newNick); } /** - * Checks and sends {@link Commands#TELL_CMD} messages. + * Responds with the last 10 public messages. * - * @param nickname The user's nickname. + * @param sender The nick of the person who sent the private message. + * @param isPrivate Set to true is the response should be send as a private message. */ - private void tellSendMessages(final String nickname) + private void recapResponse(final String sender, final boolean isPrivate) { - tellSendMessages(nickname, false); - } - - /** - * Checks and sends {@link Commands#TELL_CMD} messages. - * - * @param nickname The user's nickname. - * @param isMessage The message flag. - */ - private void tellSendMessages(final String nickname, final boolean isMessage) - { - if (!nickname.equals(getNick()) && isTellEnabled()) + for (final String recap : this.recap) { - tellMessages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> { - if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) - { - if (message.getSender().equals(nickname)) - { - if (!isMessage) - { - send(nickname, - Utils.bold("You") + " wanted me to remind you: " + Utils - .reverseColor(message.getMessage()), true); - - message.setIsReceived(); - message.setIsNotified(); - - saveTellMessages(); - } - } - else - { - send(nickname, - message.getSender() + " wanted me to tell you: " + Utils - .reverseColor(message.getMessage()), - true); - - message.setIsReceived(); - - saveTellMessages(); - } - } - else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message - .isNotified()) - { - send(nickname, - "Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + Utils - .bold(message.getRecipient()) + " on " + Utils.UTC_SDF.format(message.getReceived()), - true); - - message.setIsNotified(); - - saveTellMessages(); - } - }); + send(sender, recap, isPrivate); } } - /** - * Returns <code>true</code> if {@link Commands#TELL_CMD} is enabled. - * - * @return <code>true</code> or <code>false</code> - */ - - private boolean isTellEnabled() - { - return tellMaxSize > 0 && tellMaxDays > 0; - } - /** * Sends a private message or notice. * @@ -2067,20 +1907,6 @@ public class Mobibot extends PircBot } } - /** - * Responds with the last 10 public messages. - * - * @param sender The nick of the person who sent the private message. - * @param isPrivate Set to true is the response should be send as a private message. - */ - private void recapResponse(final String sender, final boolean isPrivate) - { - for (final String recap : this.recap) - { - send(sender, recap, isPrivate); - } - } - /** * Sends a private notice. * @@ -2092,189 +1918,6 @@ public class Mobibot extends PircBot send(sender, message, false); } - /** - * Processes the {@link Commands#TELL_CMD} commands. - * - * @param sender The sender's nick. - * @param cmds The commands string. - */ - private void tellResponse(final String sender, final String cmds) - { - if (!Utils.isValidString(cmds)) - { - helpResponse(sender, Commands.TELL_CMD); - } - else if (cmds.startsWith(Commands.VIEW_CMD)) - { - if (isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + Commands.TELL_ALL_CMD)) - { - if (tellMessages.size() > 0) - { - for (final TellMessage message : tellMessages) - { - send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [ID: " - + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - true); - } - } - else - { - send(sender, "There are no messages in the queue.", true); - } - } - else - { - boolean hasMessage = false; - - for (final TellMessage message : tellMessages) - { - if (message.isMatch(sender)) - { - if (!hasMessage) - { - hasMessage = true; - send(sender, "Here are your messages: ", true); - } - - if (message.isReceived()) - { - send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [" - + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId() - + ", DELIVERED]", - true); - - } - else - { - send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [" - + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() + ", QUEUED]", - true); - } - - send(sender, helpIndent(message.getMessage(), false), true); - } - } - - if (!hasMessage) - { - send(sender, "You have no messages in the queue.", true); - } - else - { - send(sender, "To delete one or all delivered messages:"); - send(sender, - helpIndent(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id|" - + Commands.TELL_ALL_CMD + '>')); - send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days."); - } - } - } - else if (cmds.startsWith(Commands.TELL_DEL_CMD + ' ')) - { - final String[] split = cmds.split(" "); - - if (split.length == 2) - { - final String id = split[1]; - boolean deleted = false; - - if (id.equalsIgnoreCase(Commands.TELL_ALL_CMD)) - { - for (final TellMessage message : tellMessages) - { - if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) - { - tellMessages.remove(message); - deleted = true; - } - } - - if (deleted) - { - saveTellMessages(); - send(sender, "Delivered messages have been deleted.", true); - } - else - { - send(sender, "No delivered messages were found.", true); - } - - } - else - { - boolean found = false; - - for (final TellMessage message : tellMessages) - { - found = message.isMatchId(id); - - if (found && (message.getSender().equalsIgnoreCase(sender) || isOp(sender))) - { - tellMessages.remove(message); - - saveTellMessages(); - send(sender, "Your message was deleted from the queue.", true); - deleted = true; - break; - } - } - - if (!deleted) - { - if (found) - { - send(sender, "Only messages that you sent can be deleted.", true); - } - else - { - send(sender, "The specified message [ID " + id + "] could not be found.", true); - } - } - } - } - else - { - helpResponse(sender, Commands.TELL_CMD); - } - } - else - { - final String[] split = cmds.split(" ", 2); - - if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" "))) - { - if (tellMessages.size() < tellMaxSize) - { - final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); - - tellMessages.add(message); - - saveTellMessages(); - - send(sender, - "Message [ID " + message.getId() + "] was queued for " + Utils.bold(message.getRecipient()), - true); - } - else - { - send(sender, "Sorry, the messages queue is currently full.", true); - } - } - else - { - helpResponse(sender, Commands.TELL_CMD); - } - } - - if (cleanTellMessages()) - { - saveTellMessages(); - } - } - /** * Responds with the users on a channel. * diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java new file mode 100644 index 0000000..ea74164 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -0,0 +1,428 @@ +/* + * Tell.java + * + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * The <code>Tell</code> command. + * + * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @created 2016-07-02 + * @since 1.0 + */ +public class Tell +{ + /** + * The all keyword. + */ + public static final String TELL_ALL_KEYWORD = "all"; + + /** + * The tell command. + */ + public static final String TELL_CMD = "tell"; + + /** + * The delete command. + */ + public static final String TELL_DEL_KEYWORD = "del"; + + /** + * The default maximum number of days to keep messages. + */ + + private static final int DEFAULT_TELL_MAX_DAYS = 7; + + /** + * The default message max queue size. + */ + private static final int DEFAULT_TELL_MAX_SIZE = 50; + + /** + * The serialized object file extension. + */ + private static final String SER_EXT = ".ser"; + + /** + * The bot instance. + */ + final private Mobibot bot; + + /** + * The maximum number of days to keep messages. + */ + final private int maxDays; + + /** + * The message maximum queue size. + */ + final private int maxSize; + + /** + * The messages queue. + */ + private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); + + /** + * The serialized object file. + */ + private final String serializedObject; + + public Tell(final Mobibot bot, final String maxDays, final String maxSize) + { + this.bot = bot; + this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS); + this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE); + + // Load the message queue. + serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT; + messages.addAll(TellMessagesMgr.load(serializedObject, bot.getLogger())); + + if (clean()) + { + save(); + } + } + + /** + * Cleans the messages queue. + * + * @return <code>True</code> if the queue was cleaned. + */ + private boolean clean() + { + if (bot.getLogger().isDebugEnabled()) + { + bot.getLogger().debug("Cleaning the messages."); + } + + return TellMessagesMgr.clean(messages, maxDays); + } + + /** + * Saves the messages queue. + */ + private void save() + { + TellMessagesMgr.save(serializedObject, messages, bot.getLogger()); + } + + /** + * Processes the commands. + * + * @param sender The sender's nick. + * @param cmds The commands string. + */ + public void response(final String sender, final String cmds) + { + if (!Utils.isValidString(cmds)) + { + helpResponse(sender); + } + else if (cmds.startsWith(Commands.VIEW_CMD)) + { + if (bot.isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD)) + { + if (messages.size() > 0) + { + for (final TellMessage message : messages) + { + bot.send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + + ']', + true); + } + } + else + { + bot.send(sender, "There are no messages in the queue.", true); + } + } + else + { + boolean hasMessage = false; + + for (final TellMessage message : messages) + { + if (message.isMatch(sender)) + { + if (!hasMessage) + { + hasMessage = true; + bot.send(sender, "Here are your messages: ", true); + } + + if (message.isReceived()) + { + bot.send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId() + + ", DELIVERED]", + true); + + } + else + { + bot.send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() + + ", QUEUED]", + true); + } + + bot.send(sender, bot.helpIndent(message.getMessage(), false), true); + } + } + + if (!hasMessage) + { + bot.send(sender, "You have no messages in the queue.", true); + } + else + { + bot.send(sender, "To delete one or all delivered messages:"); + bot.send(sender, + bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + + TELL_ALL_KEYWORD + '>')); + bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + " days."); + } + } + } + else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) + { + final String[] split = cmds.split(" "); + + if (split.length == 2) + { + final String id = split[1]; + boolean deleted = false; + + if (id.equalsIgnoreCase(TELL_ALL_KEYWORD)) + { + for (final TellMessage message : messages) + { + if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) + { + messages.remove(message); + deleted = true; + } + } + + if (deleted) + { + save(); + bot.send(sender, "Delivered messages have been deleted.", true); + } + else + { + bot.send(sender, "No delivered messages were found.", true); + } + + } + else + { + boolean found = false; + + for (final TellMessage message : messages) + { + found = message.isMatchId(id); + + if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender))) + { + messages.remove(message); + + save(); + bot.send(sender, "Your message was deleted from the queue.", true); + deleted = true; + break; + } + } + + if (!deleted) + { + if (found) + { + bot.send(sender, "Only messages that you sent can be deleted.", true); + } + else + { + bot.send(sender, "The specified message [ID " + id + "] could not be found.", true); + } + } + } + } + else + { + helpResponse(sender); + } + } + else + { + final String[] split = cmds.split(" ", 2); + + if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" "))) + { + if (messages.size() < maxSize) + { + final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); + + messages.add(message); + + save(); + + bot.send(sender, + "Message [ID " + message.getId() + "] was queued for " + Utils + .bold(message.getRecipient()), true); + } + else + { + bot.send(sender, "Sorry, the messages queue is currently full.", true); + } + } + else + { + helpResponse(sender); + } + } + + if (clean()) + { + save(); + } + } + + /** + * Responds with help. + * + * @param sender The sender. + */ + public void helpResponse(final String sender) + { + bot.send(sender, "To send a message to someone when they join the channel:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>")); + + bot.send(sender, "To view queued and sent messages:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD)); + + bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + " days."); + } + + /** + * Checks and sends messages. + * + * @param nickname The user's nickname. + */ + public void send(final String nickname) + { + send(nickname, false); + } + + /** + * Checks and sends messages. + * + * @param nickname The user's nickname. + * @param isMessage The message flag. + */ + public void send(final String nickname, final boolean isMessage) + { + if (!nickname.equals(bot.getNick()) && isEnabled()) + { + messages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> { + if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) + { + if (message.getSender().equals(nickname)) + { + if (!isMessage) + { + bot.send(nickname, + Utils.bold("You") + " wanted me to remind you: " + Utils + .reverseColor(message.getMessage()), + true); + + message.setIsReceived(); + message.setIsNotified(); + + save(); + } + } + else + { + bot.send(nickname, + message.getSender() + " wanted me to tell you: " + Utils + .reverseColor(message.getMessage()), + true); + + message.setIsReceived(); + + save(); + } + } + else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message + .isNotified()) + { + bot.send(nickname, + "Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF + .format(message.getReceived()), + true); + + message.setIsNotified(); + + save(); + } + }); + } + } + + /** + * Returns <code>true</code> if enabled. + * + * @return <code>true</code> or <code>false</code> + */ + public boolean isEnabled() + { + return maxSize > 0 && maxDays > 0; + } + + /** + * Returns the messages queue size. + * + * @return The size. + */ + public int size() + { + return messages.size(); + } +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 058bfd6..1137f0b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -40,7 +40,7 @@ import java.util.Date; import java.util.List; /** - * Managers the {@link Commands#TELL_CMD} messages. + * The Tell Messages Manager. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2014-04-26 @@ -67,7 +67,7 @@ final class TellMessagesMgr * * @return <code>True</code> if the queue was cleaned. */ - public static boolean cleanTellMessages(final List<TellMessage> tellMessages, final int tellMaxDays) + public static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) { final Calendar maxDate = Calendar.getInstance(); final Date today = new Date(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 7c4d90d..268c72b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -1,5 +1,5 @@ /* - * Module.java + * AbstractModule.java * * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -37,7 +37,7 @@ import net.thauvin.erik.mobibot.Utils; import java.util.*; /** - * The <code>Module</code> class. + * The <code>Module</code> abstract class. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2016-07-01 diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index efed060..19feda8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -1,8 +1,33 @@ /* * Ping.java * - * Copyright (c) 2016 Erik C. Thauvin (http://erik.thauvin.net/) + * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.thauvin.erik.mobibot.modules; @@ -13,7 +38,7 @@ import java.util.List; import java.util.Random; /** - * The <code>Ping</code> class. + * The Ping module. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @created 2016-07-02 diff --git a/version.properties b/version.properties index 5d34ed4..1e42163 100644 --- a/version.properties +++ b/version.properties @@ -2,7 +2,7 @@ #Mon Dec 07 01:31:00 PST 2015 version.project=mobibot version.major=0 -version.minor=6 -version.patch=5 +version.minor=7 +version.patch=0 version.prerelease=beta -version.buildmeta=001 +version.buildmeta=002 From 36d109fbb328630fa05bba1208fdb9988e755873 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 2 Jul 2016 20:40:52 -0700 Subject: [PATCH 042/842] Updated licenses. --- ...{Apache LICENSE.txt => Apache License.txt} | 0 licenses/JSON License.txt | 21 ++ licenses/ROME License.txt | 15 -- licenses/SLF4J License.txt | 21 ++ licenses/Twitter4J LICENSE.txt | 179 ------------------ licenses/jsoup License.txt | 2 +- 6 files changed, 43 insertions(+), 195 deletions(-) rename licenses/{Apache LICENSE.txt => Apache License.txt} (100%) create mode 100644 licenses/JSON License.txt delete mode 100644 licenses/ROME License.txt create mode 100644 licenses/SLF4J License.txt delete mode 100644 licenses/Twitter4J LICENSE.txt diff --git a/licenses/Apache LICENSE.txt b/licenses/Apache License.txt similarity index 100% rename from licenses/Apache LICENSE.txt rename to licenses/Apache License.txt diff --git a/licenses/JSON License.txt b/licenses/JSON License.txt new file mode 100644 index 0000000..4933197 --- /dev/null +++ b/licenses/JSON License.txt @@ -0,0 +1,21 @@ +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/licenses/ROME License.txt b/licenses/ROME License.txt deleted file mode 100644 index 98d59a8..0000000 --- a/licenses/ROME License.txt +++ /dev/null @@ -1,15 +0,0 @@ -Copyright 2004 Sun Microsystems, Inc. -Copyright 2011 The ROME Team - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/licenses/SLF4J License.txt b/licenses/SLF4J License.txt new file mode 100644 index 0000000..2b57cf5 --- /dev/null +++ b/licenses/SLF4J License.txt @@ -0,0 +1,21 @@ +Copyright (c) 2004-2014 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/Twitter4J LICENSE.txt b/licenses/Twitter4J LICENSE.txt deleted file mode 100644 index 6b5c2d5..0000000 --- a/licenses/Twitter4J LICENSE.txt +++ /dev/null @@ -1,179 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -Twitter4J SUBCOMPONENTS: - -Twitter4J includes software from JSON.org to parse JSON response from the Twitter API. You can see the license term at http://www.JSON.org/license.html \ No newline at end of file diff --git a/licenses/jsoup License.txt b/licenses/jsoup License.txt index 570a40d..d5d19a2 100644 --- a/licenses/jsoup License.txt +++ b/licenses/jsoup License.txt @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2009, 2010, 2011, 2012, 2013 Jonathan Hedley <jonathan@hedley.net> +© 2009-2016, Jonathan Hedley <jonathan@hedley.net> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From f318f830cef5c5f645fb83badc268a54c64acf13 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 2 Jul 2016 21:14:07 -0700 Subject: [PATCH 043/842] Optimized Twitter module. --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 2 +- .../net/thauvin/erik/mobibot/modules/Twitter.java | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index fab3b47..087d0a7 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,7 +14,7 @@ import java.util.Date; */ public final class ReleaseInfo { private final static String buildmeta = "002"; - private final static Date date = new Date(1467486806272L); + private final static Date date = new Date(1467518031071L); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index e518442..4be0165 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -74,9 +74,15 @@ final public class Twitter extends AbstractModule @Override public boolean isEnabled() { - return Utils.isValidString(properties.get(CONSUMER_KEY_PROP)) && Utils - .isValidString(properties.get(CONSUMER_SECRET_PROP)) && Utils.isValidString(properties.get(TOKEN_PROP)) - && Utils.isValidString(properties.get(TOKEN_SECRET_PROP)); + for (final String s : getPropertyKeys()) + { + if (!Utils.isValidString(properties.get(s))) + { + return false; + } + } + + return true; } @Override From 85c7fe14fdaa750dc644f201fe6f7137df3996fd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 3 Jul 2016 10:36:34 -0700 Subject: [PATCH 044/842] Added basic help to properties file. --- properties/mobibot.properties | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 2e0b8f1..7bf2dca 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -18,13 +18,24 @@ backlogs=http://www.mobitopia.org/mobibot/logs tell-max-days=5 tell-max-size=50 +# +# Credentials for: http://del.icio.us/ +# #delicious-user= #delicious-pwd= +# +# Configure app at: https://apps.twitter.com/ +# and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> +# #twitter-consumerKey= #twitter-consumerSecret= #twitter-token= #twitter-tokenSecret= +# +# Create custom search engine at: https://cse.google.com/ +# and get API key from: https://console.developers.google.com/ +# #google-api= -#google-cse-cx= \ No newline at end of file +#google-cse-cx= From d4e46729525da481a3b046d3792080db77aea51b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 3 Jul 2016 18:39:42 -0700 Subject: [PATCH 045/842] Adeed Utils.plural() method. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- .../net/thauvin/erik/mobibot/Mobibot.java | 12 ++++++++++-- .../java/net/thauvin/erik/mobibot/Tell.java | 6 +++--- .../java/net/thauvin/erik/mobibot/Utils.java | 19 +++++++++++++++++++ version.properties | 2 +- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 087d0a7..94fd13a 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "002"; - private final static Date date = new Date(1467518031071L); + private final static String buildmeta = "004"; + private final static Date date = new Date(1467595869139L); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index b6fa9c2..9986482 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1120,7 +1120,14 @@ public class Mobibot extends PircBot { for (final String info : INFO_STRS) { - send(sender, info, isPrivate); + if (info.startsWith("http://")) + { + send(sender, Utils.green(info), isPrivate); + } + else + { + send(sender, info, isPrivate); + } } long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; @@ -1134,7 +1141,8 @@ public class Mobibot extends PircBot final long minutes = timeInSeconds / 60L; send(sender, - "Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size() + "Uptime: " + days + Utils.plural(days, " day ", " days ") + hours + Utils.plural(hours, " hour ", " hours ") + + minutes + Utils.plural(minutes, " minute ", " minutes ") + "[Entries: " + entries.size() + (tell.isEnabled() && isOp(sender) ? ", Messages: " + tell.size() : "") + ']', isPrivate); } diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index ea74164..804cefd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -216,7 +216,7 @@ public class Tell bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + TELL_ALL_KEYWORD + '>')); - bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + " days."); + bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); } } } @@ -336,7 +336,7 @@ public class Tell bot.send(sender, "To view queued and sent messages:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD)); - bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + " days."); + bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); } /** @@ -425,4 +425,4 @@ public class Tell { return messages.size(); } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 424fb80..e2e7531 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -309,6 +309,25 @@ final public class Utils return false; } + /** + * Returns the plural form of a word, if count > 1. + * + * @param count The count. + * @param word The word. + * @param plural The plural word. + */ + public static String plural(final long count, final String word, final String plural) + { + if (count > 1) + { + return plural; + } + else + { + return word; + } + } + /** * Makes the given string reverse color. * diff --git a/version.properties b/version.properties index 1e42163..eece5fb 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=002 +version.buildmeta=004 From 95c7c416cb50c0abee4dd7e3eec50f5ff0b2cab7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 3 Jul 2016 20:03:37 -0700 Subject: [PATCH 046/842] Added jcenter() --- build.gradle | 1 + src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index f2027cb..56d1bb3 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,7 @@ mainClassName = packageName + '.Mobibot' repositories { mavenLocal() mavenCentral() + jcenter() } dependencies { diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index b6fa9c2..0d8aa06 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -934,7 +934,6 @@ public class Mobibot extends PircBot { send(sender, "To have the bot leave the channel and come back:"); send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); - } else if (lcTopic.equals(Commands.ME_CMD) && isOp(sender)) { @@ -963,7 +962,6 @@ public class Mobibot extends PircBot send(sender, "To toggle your ignore status:"); send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); - } else if (lcTopic.equals(Tell.TELL_CMD) && tell.isEnabled()) { @@ -971,7 +969,6 @@ public class Mobibot extends PircBot } else { - for (final AbstractModule module : MODULES) { for (final String cmd : module.getCommands()) @@ -1149,7 +1146,6 @@ public class Mobibot extends PircBot private boolean isIgnoredNick(final String nick) { return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); - } /** From e89d6337441473b0e40b11afb8048a02d2b10e4e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 3 Jul 2016 21:10:51 -0700 Subject: [PATCH 047/842] Added more details to info command. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../net/thauvin/erik/mobibot/Commands.java | 1 - .../net/thauvin/erik/mobibot/Mobibot.java | 40 +++++++++--- .../java/net/thauvin/erik/mobibot/Tell.java | 3 +- .../java/net/thauvin/erik/mobibot/Utils.java | 64 ++++--------------- .../thauvin/erik/mobibot/modules/Twitter.java | 28 ++++---- .../erik/mobibot/modules/WorldTime.java | 43 ++++++++++++- version.properties | 2 +- 8 files changed, 102 insertions(+), 83 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 94fd13a..79fb3bf 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "004"; - private final static Date date = new Date(1467595869139L); + private final static String buildmeta = "005"; + private final static Date date = new Date(1467605028612L); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 05d94a3..e9dc7f3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -135,7 +135,6 @@ final class Commands */ public static final String SAY_CMD = "say"; - /** * The users command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index d4c7276..65e125c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -925,7 +925,7 @@ public class Mobibot extends PircBot send(sender, "To list the users present on the channel:"); send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); } - else if (lcTopic.equals(Commands.INFO_CMD) && isOp(sender)) + else if (lcTopic.equals(Commands.INFO_CMD)) { send(sender, "To view information about the bot:"); send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); @@ -1033,8 +1033,8 @@ public class Mobibot extends PircBot { send(sender, "The op commands are:"); send(sender, - helpIndent(Commands.CYCLE_CMD + " " + Commands.INFO_CMD + " " + Commands.ME_CMD + " " - + Commands.MSG_CMD + " " + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); + helpIndent(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); } } } @@ -1119,29 +1119,49 @@ public class Mobibot extends PircBot { if (info.startsWith("http://")) { - send(sender, Utils.green(info), isPrivate); + send(sender, Utils.green(info), isPrivate); } else { send(sender, info, isPrivate); - } + } } + final StringBuilder info = new StringBuilder("Uptime: "); + long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; final long days = timeInSeconds / 86400L; + + if (days > 0) + { + info.append(days).append(Utils.plural(days, " day ", " days ")); + } + timeInSeconds -= (days * 86400L); final long hours = timeInSeconds / 3600L; + + if (hours > 0) + { + info.append(hours).append(Utils.plural(hours, " hour ", " hours ")); + } + timeInSeconds -= (hours * 3600L); final long minutes = timeInSeconds / 60L; + info.append(minutes).append(Utils.plural(minutes, " minute ", " minutes ")); - send(sender, - "Uptime: " + days + Utils.plural(days, " day ", " days ") + hours + Utils.plural(hours, " hour ", " hours ") - + minutes + Utils.plural(minutes, " minute ", " minutes ") + "[Entries: " + entries.size() - + (tell.isEnabled() && isOp(sender) ? ", Messages: " + tell.size() : "") + ']', - isPrivate); + info.append("[Entries: ").append(entries.size()); + + if (tell.isEnabled() && isOp(sender)) + { + info.append(", Messages: ").append(tell.size()); + } + + info.append(']'); + + send(sender, info.toString(), isPrivate); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index 804cefd..dff6317 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -216,7 +216,8 @@ public class Tell bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + TELL_ALL_KEYWORD + '>')); - bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); + bot.send(sender, + "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index e2e7531..174d373 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -46,16 +46,16 @@ import java.util.Calendar; */ final public class Utils { - /** - * The timestamp simple date format. - */ - public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); - /** * The ISO (YYYY-MM-DD) simple date format. */ public static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); + /** + * The timestamp simple date format. + */ + public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); + /** * The UTC (yyyy-MM-dd HH:mm) simple date format. */ @@ -245,46 +245,6 @@ final public class Utils return prop; } - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - public static String internetTime() - { - final Calendar gc = Calendar.getInstance(); - - final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); - int hh = gc.get(Calendar.HOUR_OF_DAY); - final int mm = gc.get(Calendar.MINUTE); - final int ss = gc.get(Calendar.SECOND); - - hh -= offset; // GMT - hh += 1; // BMT - - long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); - - if (beats >= 1000) - { - beats -= (long) 1000; - } - else if (beats < 0) - { - beats += (long) 1000; - } - - if (beats < 10) - { - return ("@00" + beats); - } - else if (beats < 100) - { - return ("@0" + beats); - } - - return ('@' + String.valueOf(beats)); - } - /** * Returns <code>true</code> if the given string is <em>not</em> blank or null. * @@ -310,12 +270,12 @@ final public class Utils } /** - * Returns the plural form of a word, if count > 1. - * - * @param count The count. - * @param word The word. - * @param plural The plural word. - */ + * Returns the plural form of a word, if count > 1. + * + * @param count The count. + * @param word The word. + * @param plural The plural word. + */ public static String plural(final long count, final String word, final String plural) { if (count > 1) @@ -326,7 +286,7 @@ final public class Utils { return word; } - } + } /** * Makes the given string reverse color. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 4be0165..646c89b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -71,20 +71,6 @@ final public class Twitter extends AbstractModule properties.put(TOKEN_SECRET_PROP, ""); } - @Override - public boolean isEnabled() - { - for (final String s : getPropertyKeys()) - { - if (!Utils.isValidString(properties.get(s))) - { - return false; - } - } - - return true; - } - @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { @@ -114,6 +100,20 @@ final public class Twitter extends AbstractModule } } + @Override + public boolean isEnabled() + { + for (final String s : getPropertyKeys()) + { + if (!Utils.isValidString(properties.get(s))) + { + return false; + } + } + + return true; + } + /** * Posts to twitter. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 19fbcd0..0ab5a10 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -166,7 +165,7 @@ final public class WorldTime extends AbstractModule { if (tz.equals(BEATS_KEYWORD)) { - response = ("The current Internet Time is: " + Utils.internetTime() + ' ' + BEATS_KEYWORD); + response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); } else { @@ -198,6 +197,46 @@ final public class WorldTime extends AbstractModule } } + /** + * Returns the current Internet (beat) Time. + * + * @return The Internet Time string. + */ + private String internetTime() + { + final Calendar gc = Calendar.getInstance(); + + final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); + int hh = gc.get(Calendar.HOUR_OF_DAY); + final int mm = gc.get(Calendar.MINUTE); + final int ss = gc.get(Calendar.SECOND); + + hh -= offset; // GMT + hh += 1; // BMT + + long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); + + if (beats >= 1000) + { + beats -= (long) 1000; + } + else if (beats < 0) + { + beats += (long) 1000; + } + + if (beats < 10) + { + return ("@00" + beats); + } + else if (beats < 100) + { + return ("@0" + beats); + } + + return ('@' + String.valueOf(beats)); + } + @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { diff --git a/version.properties b/version.properties index eece5fb..6d69796 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=004 +version.buildmeta=005 From 86fdde261da9890b9eac217a22cc1e49f77256c7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 3 Jul 2016 21:10:51 -0700 Subject: [PATCH 048/842] Added more details to info command. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../net/thauvin/erik/mobibot/Commands.java | 1 - .../net/thauvin/erik/mobibot/Mobibot.java | 65 +++++++++++++++---- .../java/net/thauvin/erik/mobibot/Tell.java | 3 +- .../java/net/thauvin/erik/mobibot/Utils.java | 64 ++++-------------- .../thauvin/erik/mobibot/modules/Twitter.java | 28 ++++---- .../erik/mobibot/modules/WorldTime.java | 43 +++++++++++- version.properties | 2 +- 8 files changed, 124 insertions(+), 86 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 94fd13a..efcc019 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "004"; - private final static Date date = new Date(1467595869139L); + private final static String buildmeta = "006"; + private final static Date date = new Date(1467669326019L); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 05d94a3..e9dc7f3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -135,7 +135,6 @@ final class Commands */ public static final String SAY_CMD = "say"; - /** * The users command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index d4c7276..d44cc19 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -925,7 +925,7 @@ public class Mobibot extends PircBot send(sender, "To list the users present on the channel:"); send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); } - else if (lcTopic.equals(Commands.INFO_CMD) && isOp(sender)) + else if (lcTopic.equals(Commands.INFO_CMD)) { send(sender, "To view information about the bot:"); send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); @@ -1019,7 +1019,7 @@ public class Mobibot extends PircBot sb.append(commandsList.get(i)); - // 5 commands per line or last command + // 6 commands per line or last command if (sb.length() > 0 && (cmdCount == 6 || i == (commandsList.size() - 1))) { send(sender, helpIndent(sb.toString())); @@ -1033,8 +1033,8 @@ public class Mobibot extends PircBot { send(sender, "The op commands are:"); send(sender, - helpIndent(Commands.CYCLE_CMD + " " + Commands.INFO_CMD + " " + Commands.ME_CMD + " " - + Commands.MSG_CMD + " " + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); + helpIndent(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); } } } @@ -1119,29 +1119,68 @@ public class Mobibot extends PircBot { if (info.startsWith("http://")) { - send(sender, Utils.green(info), isPrivate); + send(sender, Utils.green(info), isPrivate); } else { send(sender, info, isPrivate); - } + } } + final StringBuilder info = new StringBuilder("Uptime: "); + long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; + final long years = timeInSeconds / 31540000L; + + if (years > 0) + { + info.append(years).append(Utils.plural(years, " year ", " years ")); + timeInSeconds -= (years * 31540000L); + } + + + final long weeks = timeInSeconds / 604800L; + + if (weeks > 0) + { + info.append(weeks).append(Utils.plural(weeks, " week ", " weeks ")); + timeInSeconds -= (weeks * 604800L); + } + + final long days = timeInSeconds / 86400L; - timeInSeconds -= (days * 86400L); + + if (days > 0) + { + info.append(days).append(Utils.plural(days, " day ", " days ")); + timeInSeconds -= (days * 86400L); + } + final long hours = timeInSeconds / 3600L; - timeInSeconds -= (hours * 3600L); + + if (hours > 0) + { + info.append(hours).append(Utils.plural(hours, " hour ", " hours ")); + timeInSeconds -= (hours * 3600L); + } + final long minutes = timeInSeconds / 60L; - send(sender, - "Uptime: " + days + Utils.plural(days, " day ", " days ") + hours + Utils.plural(hours, " hour ", " hours ") - + minutes + Utils.plural(minutes, " minute ", " minutes ") + "[Entries: " + entries.size() - + (tell.isEnabled() && isOp(sender) ? ", Messages: " + tell.size() : "") + ']', - isPrivate); + info.append(minutes).append(Utils.plural(minutes, " minute ", " minutes ")); + + info.append("[Entries: ").append(entries.size()); + + if (tell.isEnabled() && isOp(sender)) + { + info.append(", Messages: ").append(tell.size()); + } + + info.append(']'); + + send(sender, info.toString(), isPrivate); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index 804cefd..dff6317 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -216,7 +216,8 @@ public class Tell bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + TELL_ALL_KEYWORD + '>')); - bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); + bot.send(sender, + "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index e2e7531..174d373 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -46,16 +46,16 @@ import java.util.Calendar; */ final public class Utils { - /** - * The timestamp simple date format. - */ - public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); - /** * The ISO (YYYY-MM-DD) simple date format. */ public static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); + /** + * The timestamp simple date format. + */ + public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); + /** * The UTC (yyyy-MM-dd HH:mm) simple date format. */ @@ -245,46 +245,6 @@ final public class Utils return prop; } - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - public static String internetTime() - { - final Calendar gc = Calendar.getInstance(); - - final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); - int hh = gc.get(Calendar.HOUR_OF_DAY); - final int mm = gc.get(Calendar.MINUTE); - final int ss = gc.get(Calendar.SECOND); - - hh -= offset; // GMT - hh += 1; // BMT - - long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); - - if (beats >= 1000) - { - beats -= (long) 1000; - } - else if (beats < 0) - { - beats += (long) 1000; - } - - if (beats < 10) - { - return ("@00" + beats); - } - else if (beats < 100) - { - return ("@0" + beats); - } - - return ('@' + String.valueOf(beats)); - } - /** * Returns <code>true</code> if the given string is <em>not</em> blank or null. * @@ -310,12 +270,12 @@ final public class Utils } /** - * Returns the plural form of a word, if count > 1. - * - * @param count The count. - * @param word The word. - * @param plural The plural word. - */ + * Returns the plural form of a word, if count > 1. + * + * @param count The count. + * @param word The word. + * @param plural The plural word. + */ public static String plural(final long count, final String word, final String plural) { if (count > 1) @@ -326,7 +286,7 @@ final public class Utils { return word; } - } + } /** * Makes the given string reverse color. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 4be0165..646c89b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -71,20 +71,6 @@ final public class Twitter extends AbstractModule properties.put(TOKEN_SECRET_PROP, ""); } - @Override - public boolean isEnabled() - { - for (final String s : getPropertyKeys()) - { - if (!Utils.isValidString(properties.get(s))) - { - return false; - } - } - - return true; - } - @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { @@ -114,6 +100,20 @@ final public class Twitter extends AbstractModule } } + @Override + public boolean isEnabled() + { + for (final String s : getPropertyKeys()) + { + if (!Utils.isValidString(properties.get(s))) + { + return false; + } + } + + return true; + } + /** * Posts to twitter. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 19fbcd0..0ab5a10 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -166,7 +165,7 @@ final public class WorldTime extends AbstractModule { if (tz.equals(BEATS_KEYWORD)) { - response = ("The current Internet Time is: " + Utils.internetTime() + ' ' + BEATS_KEYWORD); + response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); } else { @@ -198,6 +197,46 @@ final public class WorldTime extends AbstractModule } } + /** + * Returns the current Internet (beat) Time. + * + * @return The Internet Time string. + */ + private String internetTime() + { + final Calendar gc = Calendar.getInstance(); + + final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); + int hh = gc.get(Calendar.HOUR_OF_DAY); + final int mm = gc.get(Calendar.MINUTE); + final int ss = gc.get(Calendar.SECOND); + + hh -= offset; // GMT + hh += 1; // BMT + + long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); + + if (beats >= 1000) + { + beats -= (long) 1000; + } + else if (beats < 0) + { + beats += (long) 1000; + } + + if (beats < 10) + { + return ("@00" + beats); + } + else if (beats < 100) + { + return ("@0" + beats); + } + + return ('@' + String.valueOf(beats)); + } + @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { diff --git a/version.properties b/version.properties index eece5fb..f79df4c 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=004 +version.buildmeta=006 From 9e601d45ef0023fbc6cfeae5eb6cec960ea59a42 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 8 Jul 2016 06:11:48 -0700 Subject: [PATCH 049/842] Fixed release task dependencies. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 56d1bb3..9038fc7 100644 --- a/build.gradle +++ b/build.gradle @@ -136,7 +136,7 @@ task deploy(dependsOn: ['build']) { } -task release(dependsOn: ['deploy', 'wrapper']) << { +task release(dependsOn: ['wrapper', 'clean', 'deploy']) << { group = 'Publishing' description = 'Releases new version.' isRelease = true From 5c8951a0a82ae64ab230f7287b6902612d6671c5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 16 Jul 2016 00:54:47 -0700 Subject: [PATCH 050/842] Updated to new SwingWorker. More Java 8 optimizations. --- .idea/modules/mobibot.iml | 9 +- build.gradle | 4 +- mobibot.ipr | 30 +- properties/mobibot.properties | 2 + .../net/thauvin/erik/mobibot/ReleaseInfo.java | 60 ++- .../thauvin/erik/mobibot/DeliciousPoster.java | 60 ++- .../net/thauvin/erik/mobibot/EntriesMgr.java | 48 +- .../net/thauvin/erik/mobibot/Mobibot.java | 483 +++++++++--------- .../net/thauvin/erik/mobibot/SwingWorker.java | 164 ------ .../erik/mobibot/modules/AbstractModule.java | 44 +- .../mobibot/modules/CurrencyConverter.java | 27 +- .../erik/mobibot/modules/GoogleSearch.java | 51 +- .../thauvin/erik/mobibot/modules/Joke.java | 33 +- .../erik/mobibot/modules/StockQuote.java | 6 +- .../thauvin/erik/mobibot/modules/Twitter.java | 28 +- .../thauvin/erik/mobibot/modules/Weather.java | 40 +- .../erik/mobibot/modules/WorldTime.java | 28 +- version.properties | 2 +- 18 files changed, 497 insertions(+), 622 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/SwingWorker.java diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 58ccfb9..c0dcd8b 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,27 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.6.1-beta+008" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.0-beta+006" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="false"> <output url="file://$MODULE_DIR$/../../build/classes/main" /> <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> <exclude-output /> <content url="file://$MODULE_DIR$/../.."> + <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/../../src/annotationProcessor/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> <excludeFolder url="file://$MODULE_DIR$/../../build" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.6-beta" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.5-beta" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.16" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> @@ -39,5 +38,7 @@ <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> </component> </module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9038fc7..e43615c 100644 --- a/build.gradle +++ b/build.gradle @@ -74,12 +74,12 @@ dependencies { compile 'org.twitter4j:twitter4j-core:4.0.4' compile 'net.sf.delicious-java:delicious:1.14' - compileOnly 'net.thauvin.erik:semver:0.9.5-beta' + compileOnly 'net.thauvin.erik:semver:0.9.6-beta' } annotationProcessor { project.version = getVersion(isRelease) - library 'net.thauvin.erik:semver:0.9.5-beta' + library 'net.thauvin.erik:semver:0.9.6-beta' processor 'net.thauvin.erik.semver.VersionProcessor' } diff --git a/mobibot.ipr b/mobibot.ipr index accde1d..7002b59 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -70,6 +70,9 @@ <option name="fileTypeOverride" value="3" /> <option name="addBlankAfter" value="false" /> </LanguageOptions> + <LanguageOptions name="__TEMPLATE__"> + <option name="addBlankAfter" value="false" /> + </LanguageOptions> </component> <component name="DependencyValidationManager"> <scope name="Source" pattern="file[mobibot]:src/generated/java//*||file[mobibot]:src/main/java//*" /> @@ -183,6 +186,12 @@ </MavenGeneralSettings> </option> </component> + <component name="ProjectCodeStyleSettingsManager"> + <option name="PER_PROJECT_SETTINGS"> + <value /> + </option> + <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> + </component> <component name="ProjectInspectionProfilesVisibleTreeState"> <entry key="Project Default"> <profile-state> @@ -440,13 +449,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:semver:0.9.5-beta"> + <library name="Gradle: net.thauvin.erik:semver:0.9.6-beta"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.6-beta/semver-0.9.6-beta.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.5-beta/semver-0.9.5-beta-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.6-beta/semver-0.9.6-beta-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.apache.velocity:velocity:1.7"> @@ -494,13 +503,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.slf4j:slf4j-api:1.7.16"> + <library name="Gradle: org.slf4j:slf4j-api:1.7.21"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.16/3a6274f658487d5bfff9af3862beff6da1e7fd52/slf4j-api-1.7.16.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.21/139535a69a4239db087de9bab0bee568bf8e0b70/slf4j-api-1.7.21.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.16/974c5dd98f38036bd98b3a8f7bf00ee0d91adbdd/slf4j-api-1.7.16-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.21/f285ac123f201fb4b028bac556928d7cf527ef48/slf4j-api-1.7.21-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.slf4j:slf4j-log4j12:1.7.21"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.21/7238b064d1aba20da2ac03217d700d91e02460fa/slf4j-log4j12-1.7.21.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.21/16b1333786ea93d16bff6fb0f5e3b82716d1b008/slf4j-log4j12-1.7.21-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.twitter4j:twitter4j-core:4.0.4"> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 7bf2dca..6110975 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -3,6 +3,8 @@ server=irc.freenode.net #port=6667 login=mobibot nick=mobibot + +# NickServ password ident=changepwd #ident-nick=nickserv #ident-msg=IDENTIFY changepwd diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 216dc46..1ea174d 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,15 +13,15 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "005"; - private final static Date date = new Date(1467669995645L); - private final static int major = 0; - private final static int minor = 7; - private final static int patch = 0; - private final static String prerelease = "beta"; - private final static String project = "mobibot"; + private final static String buildmeta = "006"; + private final static Date date = new Date(1468655027549L); + private final static int major = 0; + private final static int minor = 7; + private final static int patch = 0; + private final static String prerelease = "beta"; + private final static String project = "mobibot"; - /** + /** * Disables the default constructor. * * @throws UnsupportedOperationException If the constructor is called. @@ -71,7 +71,7 @@ public final class ReleaseInfo { return Integer.toString(getMajor()) + '.' + Integer.toString(getMinor()) + '.' + Integer.toString(getPatch()) - + getPreRelease() + getBuildMetadata(); + + getPreRelease(true) + getBuildMetadata(true); } /** @@ -101,14 +101,46 @@ public final class ReleaseInfo { return patch; } + /** + * Returns the pre-release version. + * + * @param isHyphen Prepend a hyphen, if <code>true</code>. + * @return The pre-release version, if any. + */ + public static String getPreRelease(final boolean isHyphen) { + if (prerelease.length() > 0) { + if (isHyphen) { + return '-' + prerelease; + } else { + return prerelease; + } + } + + return ""; + } + /** * Returns the pre-release version. * * @return The pre-release version, if any. */ public static String getPreRelease() { - if (prerelease.length() > 0) { - return '-' + prerelease; + return getPreRelease(false); + } + + /** + * Returns the build metadata. + * + * @param isPlus Prepend a plus sign, if <code>true</code>. + * @return The build metadata, if any. + */ + public static String getBuildMetadata(final boolean isPlus) { + if (buildmeta.length() > 0) { + if (isPlus) { + return '+' + buildmeta; + } else { + return buildmeta; + } } return ""; @@ -120,10 +152,6 @@ public final class ReleaseInfo { * @return The build metadata, if any. */ public static String getBuildMetadata() { - if (buildmeta.length() > 0) { - return '+' + buildmeta; - } - - return ""; + return getBuildMetadata(false); } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 2e536ee..44b2bef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -33,6 +33,8 @@ package net.thauvin.erik.mobibot; import del.icio.us.Delicious; +import javax.swing.*; + /** * The class to handle posts to del.icio.us. * @@ -49,8 +51,8 @@ class DeliciousPoster /** * Creates a new {@link DeliciousPoster} instance. * - * @param username The del.icio.us user name. - * @param password The del.icio.us password. + * @param username The del.icio.us user name. + * @param password The del.icio.us password. * @param ircServer The IRC server. */ public DeliciousPoster(final String username, final String password, final String ircServer) @@ -66,19 +68,21 @@ class DeliciousPoster */ public final void addPost(final EntryLink entry) { - final SwingWorker worker = new SwingWorker() + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { - public Object construct() + @Override + protected Boolean doInBackground() + throws Exception { return delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate()); + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate()); } }; - worker.start(); + worker.execute(); } /** @@ -102,52 +106,56 @@ class DeliciousPoster { final String link = entry.getLink(); - final SwingWorker worker = new SwingWorker() + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { - public Object construct() + @Override + protected Boolean doInBackground() + throws Exception { return delicious.deletePost(link); } }; - worker.start(); + worker.execute(); } /** * Updates a post to del.icio.us. * * @param oldUrl The old post URL. - * @param entry The entry to add. + * @param entry The entry to add. */ public final void updatePost(final String oldUrl, final EntryLink entry) { - final SwingWorker worker = new SwingWorker() + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { - public Object construct() + @Override + protected Boolean doInBackground() + throws Exception { if (!oldUrl.equals(entry.getLink())) { delicious.deletePost(oldUrl); return delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate()); + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate()); } else { return delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate(), - true, - true); + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate(), + true, + true); } } }; - worker.start(); + worker.execute(); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index f65d7e1..c16baa2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -87,21 +87,18 @@ final class EntriesMgr * @param file The file containing the backlogs. * @param history The history list. * - * @throws FileNotFoundException If the file was not found. + * @throws IOException If the file was not found or could not be read. * @throws FeedException If an error occurred while reading the feed. */ public static void loadBacklogs(final String file, final List<String> history) - throws FileNotFoundException, FeedException + throws IOException, FeedException { history.clear(); final SyndFeedInput input = new SyndFeedInput(); - InputStreamReader reader = null; - - try + try (final InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) { - reader = new InputStreamReader(new FileInputStream(new File(file))); final SyndFeed feed = input.build(reader); @@ -114,20 +111,6 @@ final class EntriesMgr history.add(item.getTitle()); } } - finally - { - if (reader != null) - { - try - { - reader.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - } } /** @@ -139,24 +122,21 @@ final class EntriesMgr * * @return The feed's last published date. * - * @throws FileNotFoundException If the file was not found. + * @throws IOException If the file was not found or could not be read. * @throws FeedException If an error occurred while reading the feed. */ @SuppressWarnings("unchecked") public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) - throws FileNotFoundException, FeedException + throws IOException, FeedException { entries.clear(); final SyndFeedInput input = new SyndFeedInput(); - String today; - InputStreamReader reader = null; + final String today; - try + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) { - reader = new InputStreamReader(new FileInputStream(new File(file))); - final SyndFeed feed = input.build(reader); today = Utils.ISO_SDF.format(feed.getPublishedDate()); @@ -196,20 +176,6 @@ final class EntriesMgr entries.add(entry); } } - finally - { - if (reader != null) - { - try - { - reader.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - } return today; } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index d872f6b..bfc594f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -122,13 +122,30 @@ public class Mobibot extends PircBot * The version strings. */ private static final String[] VERSION_STRS = { - "Version: " + ReleaseInfo.getVersion() + " (" + Utils.ISO_SDF.format(ReleaseInfo.getBuildDate()) + ')', - "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System - .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System - .getProperty("java.runtime.version") + ')', - "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " - + System.getProperty("java.vm.info") + ')' + "Version: " + + ReleaseInfo.getVersion() + + " (" + + Utils.ISO_SDF.format(ReleaseInfo.getBuildDate()) + ')', + "Platform: " + + System.getProperty("os.name") + + " (" + + System.getProperty("os.version") + + ", " + + System.getProperty("os.arch") + + ", " + + System.getProperty("user.country") + ')', + "Runtime: " + + System.getProperty("java.runtime.name") + + " (build " + + System.getProperty("java.runtime.version") + + ')', + "VM: " + + System.getProperty("java.vm.name") + + " (build " + + System.getProperty("java.vm.version") + + ", " + + System.getProperty("java.vm.info") + + ')' }; /** @@ -211,11 +228,6 @@ public class Mobibot extends PircBot */ private String feedURL = ""; - /** - * The NickServ ident password. - */ - private String ident = ""; - /** * The ident message. */ @@ -226,6 +238,11 @@ public class Mobibot extends PircBot */ private String identNick = ""; + /** + * The NickServ ident password. + */ + private String identPwd = ""; + /** * Today's date. */ @@ -239,15 +256,18 @@ public class Mobibot extends PircBot /** * Creates a new {@link Mobibot} instance. * - * @param server The server. - * @param port The port. + * @param server The server. + * @param port The port. * @param nickname The nickname. - * @param channel The channel. - * @param logsDir The logs directory. + * @param channel The channel. + * @param logsDir The logs directory. */ @SuppressWarnings("WeakerAccess") - public Mobibot(final String server, final int port, final String nickname, final String channel, - final String logsDir) + public Mobibot(final String server, + final int port, + final String nickname, + final String channel, + final String logsDir) { System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); @@ -281,7 +301,7 @@ public class Mobibot extends PircBot today = Utils.today(); } } - catch (FileNotFoundException ignore) + catch (IOException ignore) { ; // Do nothing. } @@ -295,7 +315,7 @@ public class Mobibot extends PircBot { EntriesMgr.loadBacklogs(this.logsDir + EntriesMgr.NAV_XML, history); } - catch (FileNotFoundException ignore) + catch (IOException ignore) { ; // Do nothing. } @@ -329,12 +349,11 @@ public class Mobibot extends PircBot // Setup the command line options final Options options = new Options(); options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); - options.addOption(Commands.DEBUG_ARG.substring(0, 1), - Commands.DEBUG_ARG, - false, - "print debug & logging data directly to the console"); - options.addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file") - .desc("use alternate properties file").longOpt(Commands.PROPS_ARG).build()); + options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, + "print debug & logging data directly to the console"); + options.addOption(Option.builder( + Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") + .longOpt(Commands.PROPS_ARG).build()); options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); // Parse the command line @@ -366,14 +385,11 @@ public class Mobibot extends PircBot } else { - FileInputStream fis = null; final Properties p = new Properties(); - try + try (final FileInputStream fis = new FileInputStream( + new File(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { - fis = new FileInputStream(new File(line.getOptionValue(Commands.PROPS_ARG.charAt(0), - "./mobibot.properties"))); - // Load the properties files p.load(fis); } @@ -389,20 +405,6 @@ public class Mobibot extends PircBot e.printStackTrace(System.err); System.exit(1); } - finally - { - if (fis != null) - { - try - { - fis.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - } // Get the main properties final String channel = p.getProperty("channel"); @@ -453,7 +455,7 @@ public class Mobibot extends PircBot final String ignoredNicks = p.getProperty("ignore", ""); final String identNick = p.getProperty("ident-nick", ""); final String identMsg = p.getProperty("ident-msg", ""); - final String ident = p.getProperty("ident", ""); + final String identPwd = p.getProperty("ident", ""); final String tags = p.getProperty("tags", ""); // Get the del.icio.us properties @@ -472,13 +474,7 @@ public class Mobibot extends PircBot bot.setLogin(login); bot.setVersion(weblogURL); bot.setMessageDelay(MESSAGE_DELAY); - - // Set the ident password - bot.setIdent(ident); - - // Set the ident nick and message - bot.setIdentNick(identNick); - bot.setIdentMsg(identMsg); + bot.setIdentity(identPwd, identNick, identMsg); // Set the URLs bot.setWeblogUrl(weblogURL); @@ -492,12 +488,14 @@ public class Mobibot extends PircBot } // Load the modules properties - MODULES.stream().filter(AbstractModule::hasProperties).forEach(module -> { - for (final String s : module.getPropertyKeys()) - { - module.setProperty(s, p.getProperty(s, "")); - } - }); + MODULES.stream().filter(AbstractModule::hasProperties).forEach( + module -> + { + for (final String s : module.getPropertyKeys()) + { + module.setProperty(s, p.getProperty(s, "")); + } + }); // Set the tags bot.setTags(tags); @@ -540,111 +538,12 @@ public class Mobibot extends PircBot bot.setVersion(INFO_STRS[0]); - // Identify with NickServ - if (Utils.isValidString(ident)) - { - bot.identify(ident); - } - - // Identify with a specified nick - if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) - { - bot.sendMessage(identNick, identMsg); - } + bot.identify(); bot.joinChannel(channel); } } - /** - * Sets the ident password. - * - * @param pwd The password. - */ - private void setIdent(final String pwd) - { - ident = pwd; - } - - /** - * Sets the ident nickname. - * - * @param nick The nickname. - */ - private void setIdentNick(final String nick) - { - identNick = nick; - } - - /** - * Sets the ident message. - * - * @param msg The message. - */ - private void setIdentMsg(final String msg) - { - identMsg = msg; - } - - /** - * Sets the feed URL. - * - * @param feedURL The feed URL. - */ - private void setFeedURL(final String feedURL) - { - this.feedURL = feedURL; - } - - /** - * Sets the del.icio.us authentication. - * - * @param username The del.icio.us user name. - * @param password The del.icio.us password. - */ - private void setDeliciousAuth(final String username, final String password) - { - delicious = new DeliciousPoster(username, password, ircServer); - } - - /** - * Sets the default tags/categories. - * - * @param tags The tags. - */ - private void setTags(final String tags) - { - defaultTags = tags; - } - - /** - * Sets the Ignored nicks. - * - * @param nicks The nicks to ignore - */ - private void setIgnoredNicks(final String nicks) - { - if (Utils.isValidString(nicks)) - { - final StringTokenizer st = new StringTokenizer(nicks, ","); - - while (st.hasMoreTokens()) - { - ignoredNicks.add(st.nextToken().trim().toLowerCase()); - } - } - } - - /** - * Saves the entries. - * - * @param isDayBackup Set the true if the daily backup file should also be created. - */ - private void saveEntries(final boolean isDayBackup) - { - EntriesMgr.saveEntries(this, entries, history, isDayBackup); - } - /** * Sleeps for the specified number of seconds. * @@ -676,7 +575,7 @@ public class Mobibot extends PircBot * Sends an action to the channel. * * @param channel The channel. - * @param action The action. + * @param action The action. */ private void action(final String channel, final String action) { @@ -807,8 +706,9 @@ public class Mobibot extends PircBot if (Character.isLetter(c)) { - buff.append('[').append(String.valueOf(c).toLowerCase()).append(String.valueOf(c).toUpperCase()) - .append(']'); + buff.append('[') + .append(String.valueOf(c).toLowerCase()).append(String.valueOf(c).toUpperCase()) + .append(']'); } else { @@ -864,7 +764,7 @@ public class Mobibot extends PircBot /** * Returns indented help string. * - * @param help The help string. + * @param help The help string. * @param isBold The bold flag. * * @return The indented help string. @@ -878,7 +778,7 @@ public class Mobibot extends PircBot * Responds with the bot's help. * * @param sender The nick of the person who sent the private message. - * @param topic The help topic, if any. + * @param topic The help topic, if any. */ private void helpResponse(final String sender, final String topic) { @@ -997,8 +897,8 @@ public class Mobibot extends PircBot commandsList.add(Commands.USERS_CMD); commandsList.add(Commands.VIEW_CMD); - MODULES.stream().filter(AbstractModule::isEnabled) - .forEach(module -> commandsList.addAll(module.getCommands())); + MODULES.stream().filter(AbstractModule::isEnabled).forEach( + module -> commandsList.addAll(module.getCommands())); if (tell.isEnabled()) { @@ -1032,18 +932,39 @@ public class Mobibot extends PircBot if (isOp(sender)) { send(sender, "The op commands are:"); - send(sender, - helpIndent(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); + send(sender, helpIndent( + Commands.CYCLE_CMD + " " + + Commands.ME_CMD + " " + + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + + Commands.VERSION_CMD)); } } } + /** + * Identifies the bot. + */ + private void identify() + { + // Identify with NickServ + if (Utils.isValidString(identPwd)) + { + identify(identPwd); + } + + // Identify with a specified nick + if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) + { + sendMessage(identNick, identMsg); + } + } + /** * Processes the {@link net.thauvin.erik.mobibot.Commands#IGNORE_CMD} command. * * @param sender The sender. - * @param args The command arguments. + * @param args The command arguments. */ private void ignoreResponse(final String sender, final String args) { @@ -1110,7 +1031,7 @@ public class Mobibot extends PircBot /** * Responds with the bot's information. * - * @param sender The nick of the person who sent the message. + * @param sender The nick of the person who sent the message. * @param isPrivate Set to true is the response should be send as a private message. */ private void infoResponse(final String sender, final boolean isPrivate) @@ -1213,6 +1134,19 @@ public class Mobibot extends PircBot return false; } + @Override + protected final void onAction(final String sender, + final String login, + final String hostname, + final String target, + final String action) + { + if (target.equals(channel)) + { + storeRecap(sender, action, true); + } + } + @Override protected final void onDisconnect() { @@ -1260,22 +1194,23 @@ public class Mobibot extends PircBot setVersion(INFO_STRS[0]); - if (Utils.isValidString(ident)) - { - identify(ident); - } - - if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) - { - sendMessage(identNick, identMsg); - } + identify(); joinChannel(channel); } @Override - protected final void onMessage(final String channel, final String sender, final String login, final String hostname, - final String message) + protected void onJoin(final String channel, final String sender, final String login, final String hostname) + { + tell.send(sender); + } + + @Override + protected final void onMessage(final String channel, + final String sender, + final String login, + final String hostname, + final String message) { if (logger.isDebugEnabled()) { @@ -1690,15 +1625,23 @@ public class Mobibot extends PircBot if (!isCommand) { - recap(sender, message, false); + storeRecap(sender, message, false); } tell.send(sender, true); } @Override - protected final void onPrivateMessage(final String sender, final String login, final String hostname, - final String message) + protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) + { + tell.send(newNick); + } + + @Override + protected final void onPrivateMessage(final String sender, + final String login, + final String hostname, + final String message) { if (logger.isDebugEnabled()) { @@ -1860,50 +1803,10 @@ public class Mobibot extends PircBot } } - @Override - protected final void onAction(final String sender, final String login, final String hostname, final String target, - final String action) - { - if (target.equals(channel)) - { - recap(sender, action, true); - } - } - - /** - * Stores the last 10 public messages and actions. - * - * @param sender The nick of the person who sent the private message. - * @param message The actual message sent. - * @param isAction Set to true if the message is an action. - */ - private void recap(final String sender, final String message, final boolean isAction) - { - recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") - + message); - - if (recap.size() > MAX_RECAP) - { - recap.remove(0); - } - } - - @Override - protected void onJoin(final String channel, final String sender, final String login, final String hostname) - { - tell.send(sender); - } - - @Override - protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) - { - tell.send(newNick); - } - /** * Responds with the last 10 public messages. * - * @param sender The nick of the person who sent the private message. + * @param sender The nick of the person who sent the private message. * @param isPrivate Set to true is the response should be send as a private message. */ private void recapResponse(final String sender, final boolean isPrivate) @@ -1914,11 +1817,21 @@ public class Mobibot extends PircBot } } + /** + * Saves the entries. + * + * @param isDayBackup Set the true if the daily backup file should also be created. + */ + private void saveEntries(final boolean isDayBackup) + { + EntriesMgr.saveEntries(this, entries, history, isDayBackup); + } + /** * Sends a private message or notice. * - * @param sender The nick of the person who sent the message. - * @param message The actual message. + * @param sender The nick of the person who sent the message. + * @param message The actual message. * @param isPrivate Set to true if the response should be a private message, otherwise a notice is sent. */ public final void send(final String sender, final String message, final boolean isPrivate) @@ -1949,7 +1862,7 @@ public class Mobibot extends PircBot /** * Sends a private notice. * - * @param sender The nick of the person who sent the message. + * @param sender The nick of the person who sent the message. * @param message The actual message. */ public final void send(final String sender, final String message) @@ -1957,11 +1870,92 @@ public class Mobibot extends PircBot send(sender, message, false); } + /** + * Sets the del.icio.us authentication. + * + * @param username The del.icio.us user name. + * @param password The del.icio.us password. + */ + private void setDeliciousAuth(final String username, final String password) + { + delicious = new DeliciousPoster(username, password, ircServer); + } + + /** + * Sets the feed URL. + * + * @param feedURL The feed URL. + */ + private void setFeedURL(final String feedURL) + { + this.feedURL = feedURL; + } + + /** + * Sets the bot's identification. + * + * @param identPwd The password for NickServ, if any. + * @param identNick The ident nick name. + * @param identMsg The ident message. + */ + private void setIdentity(final String identPwd, final String identNick, final String identMsg) + { + this.identPwd = identPwd; + this.identNick = identNick; + this.identMsg = identMsg; + } + + /** + * Sets the Ignored nicks. + * + * @param nicks The nicks to ignore + */ + private void setIgnoredNicks(final String nicks) + { + if (Utils.isValidString(nicks)) + { + final StringTokenizer st = new StringTokenizer(nicks, ","); + + while (st.hasMoreTokens()) + { + ignoredNicks.add(st.nextToken().trim().toLowerCase()); + } + } + } + + /** + * Sets the default tags/categories. + * + * @param tags The tags. + */ + private void setTags(final String tags) + { + defaultTags = tags; + } + + /** + * Stores the last 10 public messages and actions. + * + * @param sender The nick of the person who sent the private message. + * @param message The actual message sent. + * @param isAction Set to <code>true</code> if the message is an action. + */ + private void storeRecap(final String sender, final String message, final boolean isAction) + { + recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") + + message); + + if (recap.size() > MAX_RECAP) + { + recap.remove(0); + } + } + /** * Responds with the users on a channel. * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to true is the response should be send as a private message. + * @param sender The nick of the person who sent the message. + * @param isPrivate Set to <code>true</code> if the response should be send as a private message. */ private void usersResponse(final String sender, final boolean isPrivate) { @@ -1993,8 +1987,8 @@ public class Mobibot extends PircBot /** * Responds with the bot's version info. * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to true is the response should be send as a private message. + * @param sender The nick of the person who sent the message. + * @param isPrivate Set to <code>true</code> if the response should be send as a private message. */ private void versionResponse(final String sender, final boolean isPrivate) { @@ -2010,9 +2004,9 @@ public class Mobibot extends PircBot /** * Responds with the stored links. * - * @param sender The nick of the person who sent the message. - * @param args The view command arguments. - * @param isPrivate Set to true is the response should be send as a private message. + * @param sender The nick of the person who sent the message. + * @param args The view command arguments. + * @param isPrivate Set to <code>true</code> if the response should be send as a private message. */ private void viewResponse(final String sender, final String args, final boolean isPrivate) { @@ -2071,15 +2065,15 @@ public class Mobibot extends PircBot if (lcArgs.length() > 0) { if ((entry.getLink().toLowerCase().contains(lcArgs)) || - (entry.getTitle().toLowerCase().contains(lcArgs)) || - (entry.getNick().toLowerCase().contains(lcArgs))) + (entry.getTitle().toLowerCase().contains(lcArgs)) || + (entry.getNick().toLowerCase().contains(lcArgs))) { if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), - isPrivate); + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), + isPrivate); break; } @@ -2093,8 +2087,9 @@ public class Mobibot extends PircBot if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + Utils.bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), - isPrivate); + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), + isPrivate); break; } @@ -2109,4 +2104,4 @@ public class Mobibot extends PircBot send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate); } } -} +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java b/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java deleted file mode 100644 index e19c1d2..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/SwingWorker.java +++ /dev/null @@ -1,164 +0,0 @@ -package net.thauvin.erik.mobibot; - -import javax.swing.*; - -/** - * This is the 3rd version of SwingWorker (also known as SwingWorker 3), an abstract class that you subclass to perform - * GUI-related work in a dedicated thread. For instructions on and examples of using this class, see: - * <p/> - * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html - * <p/> - * Note that the API changed slightly in the 3rd version: - * You must now invoke start() on the SwingWorker after creating it. - */ -@SuppressWarnings("ALL") -public abstract class SwingWorker -{ - private ThreadVar threadVar; - - private Object value; // see getValue(), setValue() - - /** - * Start a thread that will call the <code>construct</code> method and then exit. - */ - public SwingWorker() - { - final Runnable doFinished = new Runnable() - { - public void run() - { - finished(); - } - }; - - Runnable doConstruct = new Runnable() - { - public void run() - { - try - { - setValue(construct()); - } - finally - { - threadVar.clear(); - } - - SwingUtilities.invokeLater(doFinished); - } - }; - - Thread t = new Thread(doConstruct); - threadVar = new ThreadVar(t); - } - - /** - * Called on the event dispatching thread (not on the worker thread) after the <code>construct</code> method has - * returned. - */ - public void finished() - { - } - - /** - * Compute the value to be returned by the <code>get</code> method. - * - * @return The computed value. - */ - public abstract Object construct(); - - /** - * Return the value created by the <code>construct</code> method. Returns null if either the constructing thread or - * the current thread was interrupted before a value was produced. - * - * @return the value created by the <code>construct</code> method - */ - public Object get() - { - while (true) - { - Thread t = threadVar.get(); - if (t == null) - { - return getValue(); - } - try - { - t.join(); - } - catch (InterruptedException e) - { - Thread.currentThread().interrupt(); // propagate - return null; - } - } - } - - /** - * Get the value produced by the worker thread, or null if it hasn't been constructed yet. - * - * @return The value. - */ - protected synchronized Object getValue() - { - return value; - } - - /** - * Set the value produced by worker thread - * - * @param x The object. - */ - private synchronized void setValue(Object x) - { - value = x; - } - - /** - * A new method that interrupts the worker thread. Call this method to force the worker to stop what it's doing. - */ - public void interrupt() - { - Thread t = threadVar.get(); - if (t != null) - { - t.interrupt(); - } - threadVar.clear(); - } - - /** - * Start the worker thread. - */ - public void start() - { - Thread t = threadVar.get(); - if (t != null) - { - t.start(); - } - } - - /** - * Class to maintain reference to current worker thread under separate synchronization control. - */ - private static class ThreadVar - { - private Thread thread; - - ThreadVar(Thread t) - { - thread = t; - } - - synchronized void clear() - { - thread = null; - } - - synchronized Thread get() - { - return thread; - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 268c72b..f43b31a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -53,13 +53,15 @@ public abstract class AbstractModule /** * Responds to a command. * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public abstract void commandResponse(final Mobibot bot, final String sender, final String args, - final boolean isPrivate); + public abstract void commandResponse(final Mobibot bot, + final String sender, + final String args, + final boolean isPrivate); /** * Returns the module's commands, if any. @@ -94,13 +96,15 @@ public abstract class AbstractModule /** * Responds with the module's help. * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The help arguments. + * @param bot The bot's instance. + * @param sender The sender. + * @param args The help arguments. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public abstract void helpResponse(final Mobibot bot, final String sender, final String args, - final boolean isPrivate); + public abstract void helpResponse(final Mobibot bot, + final String sender, + final String args, + final boolean isPrivate); /** * Returns <code>true</code> if the module is enabled. @@ -122,10 +126,28 @@ public abstract class AbstractModule return false; } + /** + * Ensures that all properties have values. + * + * @return <code>true</code> if the properties are valid, <code>false</code> otherwise. + */ + public boolean isValidProperties() + { + for (final String s : getPropertyKeys()) + { + if (!Utils.isValidString(properties.get(s))) + { + return false; + } + } + + return true; + } + /** * Sets a property key and value. * - * @param key The key. + * @param key The key. * @param value The value. */ public void setProperty(final String key, final String value) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index b0616b6..5f41439 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -100,9 +100,7 @@ final public class CurrencyConverter extends AbstractModule } } - new Thread(() -> { - run(bot, sender, args); - }).start(); + new Thread(() -> run(bot, sender, args)).start(); } @Override @@ -148,8 +146,9 @@ final public class CurrencyConverter extends AbstractModule for (final Object rawCube : cubes) { cube = (Element) rawCube; - EXCHANGE_RATES - .put(cube.getAttribute("currency").getValue(), cube.getAttribute("rate").getValue()); + EXCHANGE_RATES.put( + cube.getAttribute("currency").getValue(), + cube.getAttribute("rate").getValue()); } EXCHANGE_RATES.put("EUR", "1"); @@ -163,7 +162,7 @@ final public class CurrencyConverter extends AbstractModule { bot.getLogger().debug("Unable to fetch the exchange rates table.", e); bot.send(sender, - "An error has occurred while fetching the exchange rates table: " + e.getMessage()); + "An error has occurred while fetching the exchange rates table: " + e.getMessage()); } } @@ -195,16 +194,20 @@ final public class CurrencyConverter extends AbstractModule final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); bot.send(bot.getChannel(), - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' - + - cmds[1].toUpperCase() + " = " + - NumberFormat.getCurrencyInstance(Locale.US).format((amt * to) / from) - .substring(1) + ' ' + cmds[3].toUpperCase()); + NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + + ' ' + + cmds[1].toUpperCase() + + " = " + + NumberFormat.getCurrencyInstance(Locale.US) + .format((amt * to) / from) + .substring(1) + + ' ' + + cmds[3].toUpperCase()); } catch (NullPointerException ignored) { bot.send(sender, - "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 9becd44..973117d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -88,9 +88,7 @@ final public class GoogleSearch extends AbstractModule { if (args.length() > 0) { - new Thread(() -> { - run(bot, sender, args); - }).start(); + new Thread(() -> run(bot, sender, args)).start(); } else { @@ -113,35 +111,39 @@ final public class GoogleSearch extends AbstractModule final String q = URLEncoder.encode(query, "UTF-8"); final URL url = - new URL("https://www.googleapis.com/customsearch/v1?key=" + properties.get(GOOGLE_API_KEY_PROP) - + "&cx=" + properties.get(GOOGLE_CSE_KEY_PROP) + "&q=" + q + "&filter=1&num=5&alt=json"); + new URL("https://www.googleapis.com/customsearch/v1?key=" + + properties.get(GOOGLE_API_KEY_PROP) + + "&cx=" + + properties.get(GOOGLE_CSE_KEY_PROP) + + "&q=" + + q + + "&filter=1&num=5&alt=json"); final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); - final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); - - String line; - while ((line = reader.readLine()) != null) + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { - sb.append(line); + String line; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + + final JSONObject json = new JSONObject(sb.toString()); + final JSONArray ja = json.getJSONArray("items"); + + for (int i = 0; i < ja.length(); i++) + { + final JSONObject j = ja.getJSONObject(i); + bot.send(sender, Utils.unescapeXml(j.getString("title"))); + bot.send(sender, TAB_INDENT + Utils.green(j.getString("link"))); + } } - - final JSONObject json = new JSONObject(sb.toString()); - final JSONArray ja = json.getJSONArray("items"); - - for (int i = 0; i < ja.length(); i++) - { - final JSONObject j = ja.getJSONObject(i); - bot.send(sender, Utils.unescapeXml(j.getString("title"))); - bot.send(sender, TAB_INDENT + Utils.green(j.getString("link"))); - } - - reader.close(); } catch (Exception e) { bot.getLogger().warn("Unable to search in Google for: " + query, e); - bot.send(sender, "An error has occurred: " + e.getMessage()); + bot.send(sender, "An error has occurred searching in Google: " + e.getMessage()); } } @@ -162,7 +164,6 @@ final public class GoogleSearch extends AbstractModule @Override public boolean isEnabled() { - return Utils.isValidString(properties.get(GOOGLE_API_KEY_PROP)) && Utils - .isValidString(properties.get(GOOGLE_CSE_KEY_PROP)); + return isValidProperties(); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 8d3f19a..8796dbc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -72,9 +72,7 @@ final public class Joke extends AbstractModule @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - new Thread(() -> { - run(bot, sender); - }).start(); + new Thread(() -> run(bot, sender)).start(); } @Override @@ -95,26 +93,27 @@ final public class Joke extends AbstractModule final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); - final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); - - String line; - while ((line = reader.readLine()) != null) + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { - sb.append(line); + String line; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + + final JSONObject json = new JSONObject(sb.toString()); + + bot.send(bot.getChannel(), + Colors.CYAN + + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") + .replaceAll("\\\"", "\"") + + Colors.NORMAL); } - - final JSONObject json = new JSONObject(sb.toString()); - - bot.send(bot.getChannel(), - Colors.CYAN + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") - .replaceAll("\\\"", "\"") + Colors.NORMAL); - - reader.close(); } catch (Exception e) { bot.getLogger().warn("Unable to retrieve random joke.", e); - bot.send(sender, "An error has occurred: " + e.getMessage()); + bot.send(sender, "An error has occurred retrieving a random joke: " + e.getMessage()); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 1ec40bf..b2dbbc0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -70,9 +70,7 @@ final public class StockQuote extends AbstractModule { if (args.length() > 0) { - new Thread(() -> { - run(bot, sender, args); - }).start(); + new Thread(() -> run(bot, sender, args)).start(); } else { @@ -160,7 +158,7 @@ final public class StockQuote extends AbstractModule catch (IOException e) { bot.getLogger().debug("Unable to retrieve stock quote for: " + symbol, e); - bot.send(sender, "An error has occurred: " + e.getMessage()); + bot.send(sender, "An error has occurred retrieving a stock quote: " + e.getMessage()); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 646c89b..b65a4b0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; import twitter4j.Status; import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; @@ -76,9 +75,7 @@ final public class Twitter extends AbstractModule { if (isEnabled() && args.length() > 0) { - new Thread(() -> { - run(bot, sender, args); - }).start(); + new Thread(() -> run(bot, sender, args)).start(); } else { @@ -103,15 +100,7 @@ final public class Twitter extends AbstractModule @Override public boolean isEnabled() { - for (final String s : getPropertyKeys()) - { - if (!Utils.isValidString(properties.get(s))) - { - return false; - } - } - - return true; + return isValidProperties(); } /** @@ -122,18 +111,19 @@ final public class Twitter extends AbstractModule try { final ConfigurationBuilder cb = new ConfigurationBuilder(); - cb.setDebugEnabled(true).setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) - .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) - .setOAuthAccessToken(properties.get(TOKEN_PROP)) - .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); + cb.setDebugEnabled(true) + .setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) + .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) + .setOAuthAccessToken(properties.get(TOKEN_PROP)) + .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); final TwitterFactory tf = new TwitterFactory(cb.build()); final twitter4j.Twitter twitter = tf.getInstance(); final Status status = twitter.updateStatus(message + " (" + sender + ')'); bot.send(sender, - "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status - .getId()); + "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status + .getId()); } catch (Exception e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java index 832678b..ba0bb8d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java @@ -75,9 +75,7 @@ final public class Weather extends AbstractModule @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - new Thread(() -> { - run(bot, sender, args.toUpperCase(), isPrivate); - }).start(); + new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); } @Override @@ -110,18 +108,26 @@ final public class Weather extends AbstractModule bot.send(sender, "Station ID: " + metar.getStationID(), isPrivate); bot.send(sender, - "At: " + Utils.UTC_SDF.format(metar.getDate()) + " UTC (" + ( - ((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + " minutes ago)", - isPrivate); + "At: " + + Utils.UTC_SDF.format(metar.getDate()) + + " UTC (" + + (((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + + " minutes ago)", + isPrivate); result = metar.getWindSpeedInMPH(); if (result != null) { bot.send(sender, - "Wind Speed: " + result + " mph, " + metar.getWindSpeedInKnots() + " knots, " + metar - .getWindSpeedInMPS() + " m/s", - isPrivate); + "Wind Speed: " + + result + + " mph, " + + metar.getWindSpeedInKnots() + + " knots, " + + metar.getWindSpeedInMPS() + + " m/s", + isPrivate); } result = metar.getVisibility(); @@ -129,9 +135,11 @@ final public class Weather extends AbstractModule if (result != null) { bot.send(sender, - "Visibility: " + (metar.getVisibilityLessThan() ? "< " : "") + NUMBER_FORMAT.format(result) - + " mi, " + metar.getVisibilityInKilometers() + " km", - isPrivate); + "Visibility: " + + (metar.getVisibilityLessThan() ? "< " : "") + + NUMBER_FORMAT.format(result) + + " mi, " + metar.getVisibilityInKilometers() + " km", + isPrivate); } result = metar.getPressure(); @@ -139,8 +147,8 @@ final public class Weather extends AbstractModule if (result != null) { bot.send(sender, - "Pressure: " + result + " Hg, " + metar.getPressureInHectoPascals() + " hPa", - isPrivate); + "Pressure: " + result + " Hg, " + metar.getPressureInHectoPascals() + " hPa", + isPrivate); } result = metar.getTemperatureInCelsius(); @@ -148,8 +156,8 @@ final public class Weather extends AbstractModule if (result != null) { bot.send(sender, - "Temperature: " + result + " \u00B0C, " + metar.getTemperatureInFahrenheit() + " \u00B0F", - isPrivate); + "Temperature: " + result + " \u00B0C, " + metar.getTemperatureInFahrenheit() + " \u00B0F", + isPrivate); } if (metar.getWeatherConditions() != null) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 0ab5a10..9bc3c1a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -149,9 +149,9 @@ final public class WorldTime extends AbstractModule /** * Responds with the current time in the specified timezone/country. * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ @Override @@ -171,7 +171,7 @@ final public class WorldTime extends AbstractModule { TIME_SDF.setTimeZone(TimeZone.getTimeZone(tz)); response = TIME_SDF.format(Calendar.getInstance().getTime()) + tz.substring(tz.indexOf('/') + 1) - .replace('_', ' '); + .replace('_', ' '); } } else @@ -197,6 +197,16 @@ final public class WorldTime extends AbstractModule } } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) + { + bot.send(sender, "To display a country's current date/time:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); + + bot.send(sender, "For a listing of the supported countries:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD)); + } + /** * Returns the current Internet (beat) Time. * @@ -237,16 +247,6 @@ final public class WorldTime extends AbstractModule return ('@' + String.valueOf(beats)); } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To display a country's current date/time:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); - - bot.send(sender, "For a listing of the supported countries:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD)); - } - @Override public boolean isPrivateMsgEnabled() { diff --git a/version.properties b/version.properties index f79df4c..93eb253 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=006 +version.buildmeta=007 From e90992a7f614da6ba7cfbee059e3955869a186f5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 16 Jul 2016 11:32:11 -0700 Subject: [PATCH 051/842] Java 8 loops optimizations. --- .../net/thauvin/erik/mobibot/Mobibot.java | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index bfc594f..5cf86ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -481,11 +481,8 @@ public class Mobibot extends PircBot bot.setFeedURL(feedURL); bot.setBacklogsUrl(backlogsURL); - if (Utils.isValidString(dname) && Utils.isValidString(dpwd)) - { - // Set the del.icio.us authentication - bot.setDeliciousAuth(dname, dpwd); - } + // Set the del.icio.us authentication + bot.setDeliciousAuth(dname, dpwd); // Load the modules properties MODULES.stream().filter(AbstractModule::hasProperties).forEach( @@ -611,15 +608,11 @@ public class Mobibot extends PircBot */ private int findDupEntry(final String link) { - EntryLink entry; - synchronized (entries) { for (int i = 0; i < entries.size(); i++) { - entry = entries.get(i); - - if (link.equals(entry.getLink())) + if (link.equals(entries.get(i).getLink())) { return i; } @@ -697,17 +690,14 @@ public class Mobibot extends PircBot private String getNickPattern() { final StringBuilder buff = new StringBuilder(0); - final String nick = getNick(); - char c; - for (int i = 0; i < nick.length(); i++) + for (final char c : getNick().toCharArray()) { - c = nick.charAt(i); - if (Character.isLetter(c)) { buff.append('[') - .append(String.valueOf(c).toLowerCase()).append(String.valueOf(c).toUpperCase()) + .append(String.valueOf(c).toLowerCase()) + .append(String.valueOf(c).toUpperCase()) .append(']'); } else @@ -1006,20 +996,26 @@ public class Mobibot extends PircBot { final String[] nicks = args.toLowerCase().split(" "); - for (String nick : nicks) + for (final String nick : nicks) { + final String ignore; + if (Commands.IGNORE_ME_KEYWORD.equals(nick)) { - nick = sender.toLowerCase(); - } - - if (ignoredNicks.contains(nick)) - { - ignoredNicks.remove(nick); + ignore = sender.toLowerCase(); } else { - ignoredNicks.add(nick); + ignore = nick; + } + + if (ignoredNicks.contains(ignore)) + { + ignoredNicks.remove(ignore); + } + else + { + ignoredNicks.add(ignore); } } } @@ -1032,7 +1028,7 @@ public class Mobibot extends PircBot * Responds with the bot's information. * * @param sender The nick of the person who sent the message. - * @param isPrivate Set to true is the response should be send as a private message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void infoResponse(final String sender, final boolean isPrivate) { @@ -1113,7 +1109,7 @@ public class Mobibot extends PircBot } /** - * Returns true is the specified sender is an Op on the {@link #channel channel}. + * Returns <code>true</code> if the specified sender is an Op on the {@link #channel channel}. * * @param sender The sender. * @@ -1807,7 +1803,7 @@ public class Mobibot extends PircBot * Responds with the last 10 public messages. * * @param sender The nick of the person who sent the private message. - * @param isPrivate Set to true is the response should be send as a private message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void recapResponse(final String sender, final boolean isPrivate) { @@ -1820,7 +1816,7 @@ public class Mobibot extends PircBot /** * Saves the entries. * - * @param isDayBackup Set the true if the daily backup file should also be created. + * @param isDayBackup Set the <code>true</code> if the daily backup file should also be created. */ private void saveEntries(final boolean isDayBackup) { @@ -1832,7 +1828,8 @@ public class Mobibot extends PircBot * * @param sender The nick of the person who sent the message. * @param message The actual message. - * @param isPrivate Set to true if the response should be a private message, otherwise a notice is sent. + * @param isPrivate Set to <code>true</code> if the response should be a private message, otherwise a notice is + * sent. */ public final void send(final String sender, final String message, final boolean isPrivate) { @@ -1878,7 +1875,10 @@ public class Mobibot extends PircBot */ private void setDeliciousAuth(final String username, final String password) { - delicious = new DeliciousPoster(username, password, ircServer); + if (Utils.isValidString(username) && Utils.isValidString(password)) + { + delicious = new DeliciousPoster(username, password, ircServer); + } } /** @@ -1955,7 +1955,7 @@ public class Mobibot extends PircBot * Responds with the users on a channel. * * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be send as a private message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void usersResponse(final String sender, final boolean isPrivate) { @@ -1988,7 +1988,7 @@ public class Mobibot extends PircBot * Responds with the bot's version info. * * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be send as a private message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void versionResponse(final String sender, final boolean isPrivate) { @@ -2006,7 +2006,7 @@ public class Mobibot extends PircBot * * @param sender The nick of the person who sent the message. * @param args The view command arguments. - * @param isPrivate Set to <code>true</code> if the response should be send as a private message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void viewResponse(final String sender, final String args, final boolean isPrivate) { From ad36c1812410c24d26187bcedd72df19352c7fe2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 16 Jul 2016 18:15:14 -0700 Subject: [PATCH 052/842] Code formmating, tabs to spaces, etc. --- README.txt | 34 +- build.gradle | 150 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../net/thauvin/erik/mobibot/Commands.java | 206 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 195 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 474 +-- .../thauvin/erik/mobibot/EntryComment.java | 149 +- .../net/thauvin/erik/mobibot/EntryLink.java | 639 ++- .../net/thauvin/erik/mobibot/FeedReader.java | 119 +- .../net/thauvin/erik/mobibot/Mobibot.java | 3731 ++++++++--------- .../java/net/thauvin/erik/mobibot/Tell.java | 602 ++- .../net/thauvin/erik/mobibot/TellMessage.java | 262 +- .../thauvin/erik/mobibot/TellMessagesMgr.java | 173 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 105 +- .../java/net/thauvin/erik/mobibot/Utils.java | 467 +-- .../erik/mobibot/modules/AbstractModule.java | 195 +- .../thauvin/erik/mobibot/modules/Calc.java | 77 +- .../mobibot/modules/CurrencyConverter.java | 292 +- .../thauvin/erik/mobibot/modules/Dice.java | 110 +- .../erik/mobibot/modules/GoogleSearch.java | 189 +- .../thauvin/erik/mobibot/modules/Joke.java | 111 +- .../thauvin/erik/mobibot/modules/Lookup.java | 293 +- .../thauvin/erik/mobibot/modules/Ping.java | 78 +- .../erik/mobibot/modules/StockQuote.java | 177 +- .../thauvin/erik/mobibot/modules/Twitter.java | 142 +- .../net/thauvin/erik/mobibot/modules/War.java | 119 +- .../thauvin/erik/mobibot/modules/Weather.java | 218 +- .../erik/mobibot/modules/WorldTime.java | 348 +- version.properties | 2 +- 29 files changed, 4375 insertions(+), 5286 deletions(-) diff --git a/README.txt b/README.txt index 1d111a1..0ece869 100644 --- a/README.txt +++ b/README.txt @@ -1,23 +1,23 @@ Some very basic instructions: - { clone with git or download the ZIP } - git clone git://github.com/ethauvin/mobibot.git - - cd mobibot - - { build with gradle } - ./gradlew + { clone with git or download the ZIP } + git clone git://github.com/ethauvin/mobibot.git + + cd mobibot + + { build with gradle } + ./gradlew - cd deploy + cd deploy - { configure the properties } - vi *.properties - - { help } - java -jar mobibot.jar -h + { configure the properties } + vi *.properties + + { help } + java -jar mobibot.jar -h - { twitter oauth token request } - java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> + { twitter oauth token request } + java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> - { launch } - /usr/bin/nohup java -jar mobibot.jar & + { launch } + /usr/bin/nohup java -jar mobibot.jar & diff --git a/build.gradle b/build.gradle index e43615c..b58a31c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" - id "com.github.ben-manes.versions" version "0.12.0" + id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" + id "com.github.ben-manes.versions" version "0.12.0" } apply plugin: 'java' @@ -13,29 +13,27 @@ def packageName = 'net.thauvin.erik.mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -def getVersion(isIncrement = false) -{ - def propsFile = 'version.properties' - def majorKey = 'version.major' - def minorKey = 'version.minor' - def patchKey = 'version.patch' - def metaKey = 'version.buildmeta' - def preKey = 'version.prerelease' - if (isIncrement) - { - ant.propertyfile(file: propsFile) { - entry(key: patchKey, - type: 'int', - default: '-1', - operation: '+') - } - } - def p = new Properties() - file(propsFile).withInputStream { stream -> p.load(stream) } - def metadata = p.getProperty(metaKey, '') - def prerelease = p.getProperty(preKey, '') - return (p.getProperty(majorKey, '1') + '.' + p.getProperty(minorKey, '0') + '.' + p.getProperty(patchKey, '0') + - (prerelease.length() > 0 ? '-' + prerelease : '') + (metadata.length() > 0 ? '+' + metadata : '')) +def getVersion(isIncrement = false) { + def propsFile = 'version.properties' + def majorKey = 'version.major' + def minorKey = 'version.minor' + def patchKey = 'version.patch' + def metaKey = 'version.buildmeta' + def preKey = 'version.prerelease' + if (isIncrement) { + ant.propertyfile(file: propsFile) { + entry(key: patchKey, + type: 'int', + default: '-1', + operation: '+') + } + } + def p = new Properties() + file(propsFile).withInputStream { stream -> p.load(stream) } + def metadata = p.getProperty(metaKey, '') + def prerelease = p.getProperty(preKey, '') + return (p.getProperty(majorKey, '1') + '.' + p.getProperty(minorKey, '0') + '.' + p.getProperty(patchKey, '0') + + (prerelease.length() > 0 ? '-' + prerelease : '') + (metadata.length() > 0 ? '+' + metadata : '')) } version = getVersion() @@ -44,100 +42,100 @@ mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' repositories { - mavenLocal() - mavenCentral() - jcenter() + mavenLocal() + mavenCentral() + jcenter() } dependencies { - compile 'log4j:log4j:1.2.17@jar' + compile 'log4j:log4j:1.2.17@jar' - compile 'pircbot:pircbot:1.5.0' + compile 'pircbot:pircbot:1.5.0' - compile 'commons-codec:commons-codec:1.10' - compile 'commons-logging:commons-logging:1.2' - compile 'commons-net:commons-net:3.5' - compile 'commons-cli:commons-cli:1.3.1' - compile 'commons-httpclient:commons-httpclient:3.1' + compile 'commons-codec:commons-codec:1.10' + compile 'commons-logging:commons-logging:1.2' + compile 'commons-net:commons-net:3.5' + compile 'commons-cli:commons-cli:1.3.1' + compile 'commons-httpclient:commons-httpclient:3.1' - compile 'oro:oro:2.0.8' + compile 'oro:oro:2.0.8' - compile 'org.jsoup:jsoup:1.9.2' - compile 'com.rometools:rome:1.6.1' - compile 'org.slf4j:slf4j-log4j12:1.7.21' - compile 'org.json:json:20160212' - compile 'org.ostermiller:utils:1.07.00' + compile 'org.jsoup:jsoup:1.9.2' + compile 'com.rometools:rome:1.6.1' + compile 'org.slf4j:slf4j-log4j12:1.7.21' + compile 'org.json:json:20160212' + compile 'org.ostermiller:utils:1.07.00' - compile 'net.sourceforge.jweather:jweather:0.3.0@jar' - compile 'net.objecthunter:exp4j:0.4.7' + compile 'net.sourceforge.jweather:jweather:0.3.0@jar' + compile 'net.objecthunter:exp4j:0.4.7' - compile 'org.twitter4j:twitter4j-core:4.0.4' - compile 'net.sf.delicious-java:delicious:1.14' + compile 'org.twitter4j:twitter4j-core:4.0.4' + compile 'net.sf.delicious-java:delicious:1.14' - compileOnly 'net.thauvin.erik:semver:0.9.6-beta' + compileOnly 'net.thauvin.erik:semver:0.9.6-beta' } annotationProcessor { - project.version = getVersion(isRelease) - library 'net.thauvin.erik:semver:0.9.6-beta' - processor 'net.thauvin.erik.semver.VersionProcessor' + project.version = getVersion(isRelease) + library 'net.thauvin.erik:semver:0.9.6-beta' + processor 'net.thauvin.erik.semver.VersionProcessor' } compileJava { - options.compilerArgs << '-proc:none' << '-Xlint:unchecked' << '-Xlint:deprecation' + options.compilerArgs << '-proc:none' << '-Xlint:unchecked' << '-Xlint:deprecation' } javadoc { - options.tags = ['created'] + options.tags = ['created'] } jar { - manifest.attributes('Main-Class': mainClassName, - 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) + manifest.attributes('Main-Class': mainClassName, + 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) - version = null + version = null } clean { - delete deployDir + delete deployDir } run { - args '--v' + args '--v' } task wrapper(type: Wrapper) { - gradleVersion = gradle.gradleVersion + gradleVersion = gradle.gradleVersion } task copyToDeploy(type: Copy) { - from('properties') { - include '*.properties' - } - from jar - into deployDir + from('properties') { + include '*.properties' + } + from jar + into deployDir } task copyToDeployLib(type: Copy) { - from configurations.runtime - into deployDir + '/lib' + from configurations.runtime + into deployDir + '/lib' } task deploy(dependsOn: ['build']) { - description = 'Copies all needed files to the ${deployDir} directory.' - group = 'Publishing' - outputs.dir deployDir - inputs.files copyToDeploy - inputs.files copyToDeployLib - doLast { - file(deployDir + '/logs').mkdir() - } - mustRunAfter clean + description = 'Copies all needed files to the ${deployDir} directory.' + group = 'Publishing' + outputs.dir deployDir + inputs.files copyToDeploy + inputs.files copyToDeployLib + doLast { + file(deployDir + '/logs').mkdir() + } + mustRunAfter clean } task release(dependsOn: ['wrapper', 'clean', 'deploy']) << { - group = 'Publishing' - description = 'Releases new version.' - isRelease = true + group = 'Publishing' + description = 'Releases new version.' + isRelease = true } diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 1ea174d..65a0955 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,8 +13,8 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "006"; - private final static Date date = new Date(1468655027549L); + private final static String buildmeta = "008"; + private final static Date date = new Date(1468718052505L); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index e9dc7f3..4ec43ba 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -38,131 +38,129 @@ package net.thauvin.erik.mobibot; * @created 2014-04-26 * @since 1.0 */ -final class Commands -{ - /** - * The add (back)log command. - */ - public static final String ADDLOG_CMD = "addlog"; +final class Commands { + /** + * The add (back)log command. + */ + public static final String ADDLOG_CMD = "addlog"; - /** - * The cycle command. - */ - public static final String CYCLE_CMD = "cycle"; + /** + * The cycle command. + */ + public static final String CYCLE_CMD = "cycle"; - /** - * Debug command line argument. - */ - public static final String DEBUG_ARG = "debug"; + /** + * Debug command line argument. + */ + public static final String DEBUG_ARG = "debug"; - /** - * The debug command. - */ - public static final String DEBUG_CMD = "debug"; + /** + * The debug command. + */ + public static final String DEBUG_CMD = "debug"; - /** - * The die command. - */ - public static final String DIE_CMD = "die"; + /** + * The die command. + */ + public static final String DIE_CMD = "die"; - /** - * Help command line argument. - */ - public static final String HELP_ARG = "help"; + /** + * Help command line argument. + */ + public static final String HELP_ARG = "help"; - /** - * The help command. - */ - public static final String HELP_CMD = "help"; + /** + * The help command. + */ + public static final String HELP_CMD = "help"; - /** - * The help on posting keyword. - */ - public static final String HELP_POSTING_KEYWORD = "posting"; + /** + * The help on posting keyword. + */ + public static final String HELP_POSTING_KEYWORD = "posting"; - /** - * The help on tags keyword. - */ - public static final String HELP_TAGS_KEYWORD = "tags"; + /** + * The help on tags keyword. + */ + public static final String HELP_TAGS_KEYWORD = "tags"; - /** - * The ignore command. - */ - public static final String IGNORE_CMD = "ignore"; + /** + * The ignore command. + */ + public static final String IGNORE_CMD = "ignore"; - /** - * The ignore <code>me</code> keyword. - */ - public static final String IGNORE_ME_KEYWORD = "me"; + /** + * The ignore <code>me</code> keyword. + */ + public static final String IGNORE_ME_KEYWORD = "me"; - /** - * The info command. - */ - public static final String INFO_CMD = "info"; + /** + * The info command. + */ + public static final String INFO_CMD = "info"; - /** - * The link command. - */ - public static final String LINK_CMD = "L"; + /** + * The link command. + */ + public static final String LINK_CMD = "L"; - /** - * The me command. - */ - public static final String ME_CMD = "me"; + /** + * The me command. + */ + public static final String ME_CMD = "me"; - /** - * The msg command. - */ - public static final String MSG_CMD = "msg"; + /** + * The msg command. + */ + public static final String MSG_CMD = "msg"; - /** - * The nick command. - */ - public static final String NICK_CMD = "nick"; + /** + * The nick command. + */ + public static final String NICK_CMD = "nick"; - /** - * Properties command line argument. - */ - public static final String PROPS_ARG = "properties"; + /** + * Properties command line argument. + */ + public static final String PROPS_ARG = "properties"; - /** - * The recap command. - */ - public static final String RECAP_CMD = "recap"; + /** + * The recap command. + */ + public static final String RECAP_CMD = "recap"; - /** - * The say command. - */ - public static final String SAY_CMD = "say"; + /** + * The say command. + */ + public static final String SAY_CMD = "say"; - /** - * The users command. - */ - public static final String USERS_CMD = "users"; + /** + * The users command. + */ + public static final String USERS_CMD = "users"; - /** - * Properties version line argument. - */ - public static final String VERSION_ARG = "version"; + /** + * Properties version line argument. + */ + public static final String VERSION_ARG = "version"; - /** - * The version command. - */ - public static final String VERSION_CMD = "version"; + /** + * The version command. + */ + public static final String VERSION_CMD = "version"; - /** - * The view command. - */ - public static final String VIEW_CMD = "view"; + /** + * The view command. + */ + public static final String VIEW_CMD = "view"; - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private Commands() - throws UnsupportedOperationException - { - throw new UnsupportedOperationException("Illegal constructor call."); - } + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private Commands() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 44b2bef..181a2f4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -42,120 +42,103 @@ import javax.swing.*; * @created Mar 5, 2005 * @since 1.0 */ -class DeliciousPoster -{ - private final Delicious delicious; +class DeliciousPoster { + private final Delicious delicious; + private final String ircServer; - private final String ircServer; + /** + * Creates a new {@link DeliciousPoster} instance. + * + * @param username The del.icio.us user name. + * @param password The del.icio.us password. + * @param ircServer The IRC server. + */ + public DeliciousPoster(final String username, final String password, final String ircServer) { + delicious = new Delicious(username, password); + this.ircServer = ircServer; + } - /** - * Creates a new {@link DeliciousPoster} instance. - * - * @param username The del.icio.us user name. - * @param password The del.icio.us password. - * @param ircServer The IRC server. - */ - public DeliciousPoster(final String username, final String password, final String ircServer) - { - delicious = new Delicious(username, password); - this.ircServer = ircServer; - } + /** + * Adds a post to del.icio.us. + * + * @param entry The entry to add. + */ + public final void addPost(final EntryLink entry) { + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + @Override + protected Boolean doInBackground() + throws Exception { + return delicious.addPost(entry.getLink(), + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate()); + } + }; - /** - * Adds a post to del.icio.us. - * - * @param entry The entry to add. - */ - public final void addPost(final EntryLink entry) - { - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() - { - @Override - protected Boolean doInBackground() - throws Exception - { - return delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate()); - } - }; + worker.execute(); + } - worker.execute(); - } + /** + * Deletes a post to del.icio.us. + * + * @param entry The entry to delete. + */ + public final void deletePost(final EntryLink entry) { + final String link = entry.getLink(); - /** - * Returns he del.icio.us extended attribution line. - * - * @param entry The entry. - * - * @return The extended attribution line. - */ - private String postedBy(final EntryLink entry) - { - return "Posted by " + entry.getNick() + " on " + entry.getChannel() + " (" + ircServer + ')'; - } + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + @Override + protected Boolean doInBackground() + throws Exception { + return delicious.deletePost(link); + } + }; - /** - * Deletes a post to del.icio.us. - * - * @param entry The entry to delete. - */ - public final void deletePost(final EntryLink entry) - { - final String link = entry.getLink(); + worker.execute(); + } - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() - { - @Override - protected Boolean doInBackground() - throws Exception - { - return delicious.deletePost(link); - } - }; + /** + * Returns he del.icio.us extended attribution line. + * + * @param entry The entry. + * @return The extended attribution line. + */ + private String postedBy(final EntryLink entry) { + return "Posted by " + entry.getNick() + " on " + entry.getChannel() + " (" + ircServer + ')'; + } - worker.execute(); - } + /** + * Updates a post to del.icio.us. + * + * @param oldUrl The old post URL. + * @param entry The entry to add. + */ + public final void updatePost(final String oldUrl, final EntryLink entry) { + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + @Override + protected Boolean doInBackground() + throws Exception { + if (!oldUrl.equals(entry.getLink())) { + delicious.deletePost(oldUrl); - /** - * Updates a post to del.icio.us. - * - * @param oldUrl The old post URL. - * @param entry The entry to add. - */ - public final void updatePost(final String oldUrl, final EntryLink entry) - { - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() - { - @Override - protected Boolean doInBackground() - throws Exception - { - if (!oldUrl.equals(entry.getLink())) - { - delicious.deletePost(oldUrl); + return delicious.addPost(entry.getLink(), + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate()); + } else { + return delicious.addPost(entry.getLink(), + entry.getTitle(), + postedBy(entry), + entry.getDeliciousTags(), + entry.getDate(), + true, + true); + } + } + }; - return delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate()); - } - else - { - return delicious.addPost(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getDeliciousTags(), - entry.getDate(), - true, - true); - } - } - }; - - worker.execute(); - } + worker.execute(); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index c16baa2..daa8890 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -48,310 +48,270 @@ import java.util.List; * @created 2014-04-28 * @since 1.0 */ -final class EntriesMgr -{ - /** - * The name of the file containing the current entries. - */ - public static final String CURRENT_XML = "current.xml"; +final class EntriesMgr { + /** + * The name of the file containing the current entries. + */ + public static final String CURRENT_XML = "current.xml"; - /** - * The name of the file containing the backlog entries. - */ - public static final String NAV_XML = "nav.xml"; + /** + * The name of the file containing the backlog entries. + */ + public static final String NAV_XML = "nav.xml"; - /** - * The .xml extension - */ - public static final String XML_EXT = ".xml"; + /** + * The .xml extension + */ + public static final String XML_EXT = ".xml"; - /** - * The maximum number of backlogs to keep. - */ - private static final int MAX_BACKLOGS = 10; + /** + * The maximum number of backlogs to keep. + */ + private static final int MAX_BACKLOGS = 10; - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private EntriesMgr() - throws UnsupportedOperationException - { - throw new UnsupportedOperationException("Illegal constructor call."); - } + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private EntriesMgr() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } - /** - * Loads the backlogs. - * - * @param file The file containing the backlogs. - * @param history The history list. - * - * @throws IOException If the file was not found or could not be read. - * @throws FeedException If an error occurred while reading the feed. - */ - public static void loadBacklogs(final String file, final List<String> history) - throws IOException, FeedException - { - history.clear(); + /** + * Loads the backlogs. + * + * @param file The file containing the backlogs. + * @param history The history list. + * @throws IOException If the file was not found or could not be read. + * @throws FeedException If an error occurred while reading the feed. + */ + public static void loadBacklogs(final String file, final List<String> history) + throws IOException, FeedException { + history.clear(); - final SyndFeedInput input = new SyndFeedInput(); + final SyndFeedInput input = new SyndFeedInput(); - try (final InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) - { + try (final InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) { - final SyndFeed feed = input.build(reader); + final SyndFeed feed = input.build(reader); - final List items = feed.getEntries(); - SyndEntry item; + final List items = feed.getEntries(); + SyndEntry item; - for (int i = items.size() - 1; i >= 0; i--) - { - item = (SyndEntryImpl) items.get(i); - history.add(item.getTitle()); - } - } - } + for (int i = items.size() - 1; i >= 0; i--) { + item = (SyndEntryImpl) items.get(i); + history.add(item.getTitle()); + } + } + } - /** - * Loads the current entries. - * - * @param file The file containing the current entries. - * @param channel The channel - * @param entries The entries. - * - * @return The feed's last published date. - * - * @throws IOException If the file was not found or could not be read. - * @throws FeedException If an error occurred while reading the feed. - */ - @SuppressWarnings("unchecked") - public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) - throws IOException, FeedException - { - entries.clear(); + /** + * Loads the current entries. + * + * @param file The file containing the current entries. + * @param channel The channel + * @param entries The entries. + * @return The feed's last published date. + * @throws IOException If the file was not found or could not be read. + * @throws FeedException If an error occurred while reading the feed. + */ + @SuppressWarnings("unchecked") + public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) + throws IOException, FeedException { + entries.clear(); - final SyndFeedInput input = new SyndFeedInput(); + final SyndFeedInput input = new SyndFeedInput(); - final String today; + final String today; - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) - { - final SyndFeed feed = input.build(reader); + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) { + final SyndFeed feed = input.build(reader); - today = Utils.ISO_SDF.format(feed.getPublishedDate()); + today = Utils.ISO_SDF.format(feed.getPublishedDate()); - final List items = feed.getEntries(); - SyndEntry item; - SyndContent description; - String[] comments; - String author; - EntryLink entry; + final List items = feed.getEntries(); + SyndEntry item; + SyndContent description; + String[] comments; + String author; + EntryLink entry; - for (int i = items.size() - 1; i >= 0; i--) - { - item = (SyndEntryImpl) items.get(i); - author = item.getAuthor() - .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); - entry = new EntryLink(item.getLink(), - item.getTitle(), - author, - channel, - item.getPublishedDate(), - item.getCategories()); - description = item.getDescription(); - comments = description.getValue().split("<br/>"); + for (int i = items.size() - 1; i >= 0; i--) { + item = (SyndEntryImpl) items.get(i); + author = item.getAuthor() + .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); + entry = new EntryLink(item.getLink(), + item.getTitle(), + author, + channel, + item.getPublishedDate(), + item.getCategories()); + description = item.getDescription(); + comments = description.getValue().split("<br/>"); - int split; - for (final String comment : comments) - { - split = comment.indexOf(": "); + int split; + for (final String comment : comments) { + split = comment.indexOf(": "); - if (split != -1) - { - entry.addComment(comment.substring(split + 2).trim(), comment.substring(0, split).trim()); - } - } + if (split != -1) { + entry.addComment(comment.substring(split + 2).trim(), comment.substring(0, split).trim()); + } + } - entries.add(entry); - } - } + entries.add(entry); + } + } - return today; - } + return today; + } - /** - * Saves the entries. - * - * @param bot The bot object. - * @param entries The entries array. - * @param history The history array. - * @param isDayBackup Set the true if the daily backup file should also be created. - */ - public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, - final boolean isDayBackup) - { - if (bot.getLogger().isDebugEnabled()) - { - bot.getLogger().debug("Saving the feeds..."); - } + /** + * Saves the entries. + * + * @param bot The bot object. + * @param entries The entries array. + * @param history The history array. + * @param isDayBackup Set the true if the daily backup file should also be created. + */ + public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, + final boolean isDayBackup) { + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Saving the feeds..."); + } - if (Utils.isValidString(bot.getLogsDir()) && Utils.isValidString(bot.getWeblogUrl())) - { - FileWriter fw = null; + if (Utils.isValidString(bot.getLogsDir()) && Utils.isValidString(bot.getWeblogUrl())) { + FileWriter fw = null; - try - { - fw = new FileWriter(new File(bot.getLogsDir() + CURRENT_XML)); + try { + fw = new FileWriter(new File(bot.getLogsDir() + CURRENT_XML)); - SyndFeed rss = new SyndFeedImpl(); - rss.setFeedType("rss_2.0"); - rss.setTitle(bot.getChannel() + " IRC Links"); - rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); - rss.setLink(bot.getWeblogUrl()); - rss.setPublishedDate(Calendar.getInstance().getTime()); - rss.setLanguage("en"); + SyndFeed rss = new SyndFeedImpl(); + rss.setFeedType("rss_2.0"); + rss.setTitle(bot.getChannel() + " IRC Links"); + rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); + rss.setLink(bot.getWeblogUrl()); + rss.setPublishedDate(Calendar.getInstance().getTime()); + rss.setLanguage("en"); - EntryLink entry; - StringBuffer buff; - EntryComment comment; - final List<SyndEntry> items = new ArrayList<>(0); - SyndEntry item; - SyndContent description; + EntryLink entry; + StringBuffer buff; + EntryComment comment; + final List<SyndEntry> items = new ArrayList<>(0); + SyndEntry item; + SyndContent description; - for (int i = (entries.size() - 1); i >= 0; --i) - { - entry = entries.get(i); + for (int i = (entries.size() - 1); i >= 0; --i) { + entry = entries.get(i); - buff = new StringBuffer( - "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' - + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); + buff = new StringBuffer( + "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' + + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); - if (entry.getCommentsCount() > 0) - { - buff.append(" <br/><br/>"); + if (entry.getCommentsCount() > 0) { + buff.append(" <br/><br/>"); - final EntryComment[] comments = entry.getComments(); + final EntryComment[] comments = entry.getComments(); - for (int j = 0; j < comments.length; j++) - { - comment = comments[j]; + for (int j = 0; j < comments.length; j++) { + comment = comments[j]; - if (j > 0) - { - buff.append(" <br/>"); - } + if (j > 0) { + buff.append(" <br/>"); + } - buff.append(comment.getNick()).append(": ").append(comment.getComment()); - } - } + buff.append(comment.getNick()).append(": ").append(comment.getComment()); + } + } - item = new SyndEntryImpl(); - item.setLink(entry.getLink()); - description = new SyndContentImpl(); - description.setValue(buff.toString()); - item.setDescription(description); - item.setTitle(entry.getTitle()); - item.setPublishedDate(entry.getDate()); - item.setAuthor( - bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); - item.setCategories(entry.getTags()); + item = new SyndEntryImpl(); + item.setLink(entry.getLink()); + description = new SyndContentImpl(); + description.setValue(buff.toString()); + item.setDescription(description); + item.setTitle(entry.getTitle()); + item.setPublishedDate(entry.getDate()); + item.setAuthor( + bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); + item.setCategories(entry.getTags()); - items.add(item); - } + items.add(item); + } - rss.setEntries(items); + rss.setEntries(items); - if (bot.getLogger().isDebugEnabled()) - { - bot.getLogger().debug("Writing the entries feed."); - } + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Writing the entries feed."); + } - final SyndFeedOutput output = new SyndFeedOutput(); - output.output(rss, fw); - fw.close(); + final SyndFeedOutput output = new SyndFeedOutput(); + output.output(rss, fw); + fw.close(); - fw = new FileWriter(new File(bot.getLogsDir() + bot.getToday() + XML_EXT)); - output.output(rss, fw); + fw = new FileWriter(new File(bot.getLogsDir() + bot.getToday() + XML_EXT)); + output.output(rss, fw); - if (isDayBackup) - { - if (Utils.isValidString(bot.getBacklogsUrl())) - { - if (history.indexOf(bot.getToday()) == -1) - { - history.add(bot.getToday()); + if (isDayBackup) { + if (Utils.isValidString(bot.getBacklogsUrl())) { + if (history.indexOf(bot.getToday()) == -1) { + history.add(bot.getToday()); - while (history.size() > MAX_BACKLOGS) - { - history.remove(0); - } - } + while (history.size() > MAX_BACKLOGS) { + history.remove(0); + } + } - fw.close(); - fw = new FileWriter(new File(bot.getLogsDir() + NAV_XML)); - rss = new SyndFeedImpl(); - rss.setFeedType("rss_2.0"); - rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); - rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " + bot.getChannel()); - rss.setLink(bot.getBacklogsUrl()); - rss.setPublishedDate(Calendar.getInstance().getTime()); + fw.close(); + fw = new FileWriter(new File(bot.getLogsDir() + NAV_XML)); + rss = new SyndFeedImpl(); + rss.setFeedType("rss_2.0"); + rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); + rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " + bot.getChannel()); + rss.setLink(bot.getBacklogsUrl()); + rss.setPublishedDate(Calendar.getInstance().getTime()); - String date; - items.clear(); + String date; + items.clear(); - for (int i = (history.size() - 1); i >= 0; --i) - { - date = history.get(i); + for (int i = (history.size() - 1); i >= 0; --i) { + date = history.get(i); - item = new SyndEntryImpl(); - item.setLink(bot.getBacklogsUrl() + date + ".xml"); - item.setTitle(date); - description = new SyndContentImpl(); - description.setValue("Links for " + date); - item.setDescription(description); + item = new SyndEntryImpl(); + item.setLink(bot.getBacklogsUrl() + date + ".xml"); + item.setTitle(date); + description = new SyndContentImpl(); + description.setValue("Links for " + date); + item.setDescription(description); - items.add(item); - } + items.add(item); + } - rss.setEntries(items); + rss.setEntries(items); - if (bot.getLogger().isDebugEnabled()) - { - bot.getLogger().debug("Writing the backlog feed."); - } + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Writing the backlog feed."); + } - output.output(rss, fw); - } - else - { - bot.getLogger().warn("Unable to generate the backlogs feed. No property configured."); - } - } - } - catch (Exception e) - { - bot.getLogger().warn("Unable to generate the entries feed.", e); - } - finally - { - try - { - if (fw != null) - { - fw.close(); - } - } - catch (Exception ignore) - { - ; // Do nothing - } - } - } - else - { - bot.getLogger() - .warn("Unable to generate the entries feed. At least one of the required property is missing."); - } - } + output.output(rss, fw); + } else { + bot.getLogger().warn("Unable to generate the backlogs feed. No property configured."); + } + } + } catch (Exception e) { + bot.getLogger().warn("Unable to generate the entries feed.", e); + } finally { + try { + if (fw != null) { + fw.close(); + } + } catch (Exception ignore) { + ; // Do nothing + } + } + } else { + bot.getLogger() + .warn("Unable to generate the entries feed. At least one of the required property is missing."); + } + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index e8e09b2..826deac 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -42,92 +42,83 @@ import java.util.Date; * @created Jan 31, 2004 * @since 1.0 */ -public class EntryComment implements Serializable -{ - /** - * The serial version UID. - */ - static final long serialVersionUID = 6957415292233553224L; +public class EntryComment implements Serializable { + /** + * The serial version UID. + */ + static final long serialVersionUID = 6957415292233553224L; - /** - * The creation date. - */ - private final Date date = Calendar.getInstance().getTime(); + /** + * The creation date. + */ + private final Date date = Calendar.getInstance().getTime(); - private String comment = ""; + private String comment = ""; + private String nick = ""; - private String nick = ""; + /** + * Creates a new comment. + * + * @param comment The new comment. + * @param nick The nickname of the comment's author. + */ + public EntryComment(final String comment, final String nick) { + this.comment = comment; + this.nick = nick; + } - /** - * Creates a new comment. - * - * @param comment The new comment. - * @param nick The nickname of the comment's author. - */ - public EntryComment(final String comment, final String nick) - { - this.comment = comment; - this.nick = nick; - } + /** + * Creates a new comment. + */ + @SuppressWarnings("UnusedDeclaration") + protected EntryComment() { + ; // Required for serialization. + } - /** - * Creates a new comment. - */ - @SuppressWarnings("UnusedDeclaration") - protected EntryComment() - { - ; // Required for serialization. - } + /** + * Returns the comment. + * + * @return The comment. + */ + public final String getComment() { + return comment; + } - /** - * Returns the comment. - * - * @return The comment. - */ - public final String getComment() - { - return comment; - } + /** + * Sets the comment. + * + * @param comment The actual comment. + */ + @SuppressWarnings("UnusedDeclaration") + public final void setComment(final String comment) { + this.comment = comment; + } - /** - * Sets the comment. - * - * @param comment The actual comment. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setComment(final String comment) - { - this.comment = comment; - } + /** + * Returns the comment's creation date. + * + * @return The date. + */ + @SuppressWarnings("UnusedDeclaration") + public final Date getDate() { + return date; + } - /** - * Returns the comment's creation date. - * - * @return The date. - */ - @SuppressWarnings("UnusedDeclaration") - public final Date getDate() - { - return date; - } + /** + * Returns the nickname of the author of the comment. + * + * @return The nickname. + */ + public final String getNick() { + return nick; + } - /** - * Returns the nickname of the author of the comment. - * - * @return The nickname. - */ - public final String getNick() - { - return nick; - } - - /** - * Sets the nickname of the author of the comment. - * - * @param nick The new nickname. - */ - public final void setNick(final String nick) - { - this.nick = nick; - } + /** + * Sets the nickname of the author of the comment. + * + * @param nick The new nickname. + */ + public final void setNick(final String nick) { + this.nick = nick; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index e1fe035..2be2d11 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -47,391 +47,348 @@ import java.util.concurrent.CopyOnWriteArrayList; * @created Jan 31, 2004 * @since 1.0 */ -public class EntryLink implements Serializable -{ - /** - * The serial version UID. - */ - static final long serialVersionUID = 3676245542270899086L; +public class EntryLink implements Serializable { + /** + * The serial version UID. + */ + static final long serialVersionUID = 3676245542270899086L; - // The link's comments - private final List<EntryComment> comments = new CopyOnWriteArrayList<>(); + // The link's comments + private final List<EntryComment> comments = new CopyOnWriteArrayList<>(); - // The tags/categories - private final List<SyndCategory> tags = new CopyOnWriteArrayList<>(); + // The tags/categories + private final List<SyndCategory> tags = new CopyOnWriteArrayList<>(); - // The channel - private String channel = ""; + // The channel + private String channel = ""; - // The creation date - private Date date = Calendar.getInstance().getTime(); + // The creation date + private Date date = Calendar.getInstance().getTime(); - // The link's URL - private String link = ""; + // The link's URL + private String link = ""; - // The author's login - private String login = ""; + // The author's login + private String login = ""; - // The author's nickname - private String nick = ""; + // The author's nickname + private String nick = ""; - // The link's title - private String title = ""; + // The link's title + private String title = ""; - /** - * Creates a new entry. - * - * @param link The new entry's link. - * @param title The new entry's title. - * @param nick The nickname of the author of the link. - * @param login The login of the author of the link. - * @param channel The channel. - * @param tags The entry's tags/categories. - */ - public EntryLink(final String link, final String title, final String nick, final String login, final String channel, - final String tags) - { - this.link = link; - this.title = title; - this.nick = nick; - this.login = login; - this.channel = channel; + /** + * Creates a new entry. + * + * @param link The new entry's link. + * @param title The new entry's title. + * @param nick The nickname of the author of the link. + * @param login The login of the author of the link. + * @param channel The channel. + * @param tags The entry's tags/categories. + */ + public EntryLink(final String link, final String title, final String nick, final String login, final String channel, + final String tags) { + this.link = link; + this.title = title; + this.nick = nick; + this.login = login; + this.channel = channel; - setTags(tags); - } + setTags(tags); + } - /** - * Sets the tags. - * - * @param tags The space-delimited tags. - */ - public final void setTags(final String tags) - { - if (tags != null) - { - final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); + /** + * Creates a new entry. + * + * @param link The new entry's link. + * @param title The new entry's title. + * @param nick The nickname of the author of the link. + * @param channel The channel. + * @param date The entry date. + * @param tags The entry's tags/categories. + */ + public EntryLink(final String link, final String title, final String nick, final String channel, final Date date, + final List<SyndCategory> tags) { + this.link = link; + this.title = title; + this.nick = nick; + this.channel = channel; + this.date = date; - SyndCategoryImpl tag; - String part; - char mod; + setTags(tags); + } - for (final String rawPart : parts) - { - part = rawPart.trim(); + /** + * Adds a new comment. + * + * @param comment The actual comment. + * @param nick The nickname of the author of the comment. + * @return The total number of comments for this entry. + */ + public final int addComment(final String comment, final String nick) { + comments.add(new EntryComment(comment, nick)); - if (part.length() >= 2) - { - tag = new SyndCategoryImpl(); - tag.setName(part.substring(1).toLowerCase()); + return (comments.size() - 1); + } - mod = part.charAt(0); + /** + * Deletes a specific comment. + * + * @param index The index of the comment to delete. + */ + public final void deleteComment(final int index) { + if (index < comments.size()) { + comments.remove(index); + } + } - if (mod == '-') - { - // Don't remove the channel tag, if any. - if (!tag.getName().equals(channel.substring(1))) - { - this.tags.remove(tag); - } - } - else if (mod == '+') - { - if (!this.tags.contains(tag)) - { - this.tags.add(tag); - } - } - else - { - tag.setName(part.trim().toLowerCase()); + /** + * Returns the channel the link was posted on. + * + * @return The channel + */ + public final String getChannel() { + return channel; + } - if (!this.tags.contains(tag)) - { - this.tags.add(tag); - } - } - } - } - } - } + /** + * Sets the channel. + * + * @param channel The channel. + */ + @SuppressWarnings("UnusedDeclaration") + public final void setChannel(final String channel) { + this.channel = channel; + } - /** - * Creates a new entry. - * - * @param link The new entry's link. - * @param title The new entry's title. - * @param nick The nickname of the author of the link. - * @param channel The channel. - * @param date The entry date. - * @param tags The entry's tags/categories. - */ - public EntryLink(final String link, final String title, final String nick, final String channel, final Date date, - final List<SyndCategory> tags) - { - this.link = link; - this.title = title; - this.nick = nick; - this.channel = channel; - this.date = date; + /** + * Returns a comment. + * + * @param index The comment's index. + * @return The specific comment. + */ + public final EntryComment getComment(final int index) { + return (comments.get(index)); + } - setTags(tags); - } + /** + * Returns all the comments. + * + * @return The comments. + */ + public final EntryComment[] getComments() { + return (comments.toArray(new EntryComment[comments.size()])); + } - /** - * Adds a new comment. - * - * @param comment The actual comment. - * @param nick The nickname of the author of the comment. - * - * @return The total number of comments for this entry. - */ - public final int addComment(final String comment, final String nick) - { - comments.add(new EntryComment(comment, nick)); + /** + * Returns the total number of comments. + * + * @return The count of comments. + */ + public final int getCommentsCount() { + return comments.size(); + } - return (comments.size() - 1); - } + /** + * Returns the comment's creation date. + * + * @return The date. + */ + public final Date getDate() { + return date; + } - /** - * Deletes a specific comment. - * - * @param index The index of the comment to delete. - */ - public final void deleteComment(final int index) - { - if (index < comments.size()) - { - comments.remove(index); - } - } + /** + * Returns the tags formatted for del.icio.us. + * + * @return The tags as a comma-delimited string. + */ + public final String getDeliciousTags() { + final StringBuilder tags = new StringBuilder(nick); - /** - * Returns the channel the link was posted on. - * - * @return The channel - */ - public final String getChannel() - { - return channel; - } + for (final Object tag : this.tags) { + tags.append(','); + tags.append(((SyndCategoryImpl) tag).getName()); + } - /** - * Sets the channel. - * - * @param channel The channel. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setChannel(final String channel) - { - this.channel = channel; - } + return tags.toString(); + } - /** - * Returns a comment. - * - * @param index The comment's index. - * - * @return The specific comment. - */ - public final EntryComment getComment(final int index) - { - return (comments.get(index)); - } + /** + * Returns the comment's link. + * + * @return The link. + */ + public final String getLink() { + return link; + } - /** - * Returns all the comments. - * - * @return The comments. - */ - public final EntryComment[] getComments() - { - return (comments.toArray(new EntryComment[comments.size()])); - } + /** + * Sets the comment's link. + * + * @param link The new link. + */ + public final void setLink(final String link) { + this.link = link; + } - /** - * Returns the total number of comments. - * - * @return The count of comments. - */ - public final int getCommentsCount() - { - return comments.size(); - } + /** + * Returns the comment's author login. + * + * @return The login; + */ + public final String getLogin() { + return login; + } - /** - * Returns the comment's creation date. - * - * @return The date. - */ - public final Date getDate() - { - return date; - } + /** + * Sets the comment's author login. + * + * @param login The new login. + */ + @SuppressWarnings("UnusedDeclaration") + public final void setLogin(final String login) { + this.login = login; + } - /** - * Returns the tags formatted for del.icio.us. - * - * @return The tags as a comma-delimited string. - */ - public final String getDeliciousTags() - { - final StringBuilder tags = new StringBuilder(nick); + /** + * Returns the comment's author nickname. + * + * @return The nickname. + */ + public final String getNick() { + return nick; + } - for (final Object tag : this.tags) - { - tags.append(','); - tags.append(((SyndCategoryImpl) tag).getName()); - } + /** + * Sets the comment's author nickname. + * + * @param nick The new nickname. + */ + public final void setNick(final String nick) { + this.nick = nick; + } - return tags.toString(); - } + /** + * Returns the tags. + * + * @return The tags. + */ + public final List<SyndCategory> getTags() { + return tags; + } - /** - * Returns the comment's link. - * - * @return The link. - */ - public final String getLink() - { - return link; - } + /** + * Sets the tags. + * + * @param tags The tags. + */ + private void setTags(final List<SyndCategory> tags) { + this.tags.addAll(tags); + } - /** - * Sets the comment's link. - * - * @param link The new link. - */ - public final void setLink(final String link) - { - this.link = link; - } + /** + * Returns the comment's title. + * + * @return The title. + */ + public final String getTitle() { + return title; + } - /** - * Returns the comment's author login. - * - * @return The login; - */ - public final String getLogin() - { - return login; - } + /** + * Sets the comment's title. + * + * @param title The new title. + */ + public final void setTitle(final String title) { + this.title = title; + } - /** - * Sets the comment's author login. - * - * @param login The new login. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setLogin(final String login) - { - this.login = login; - } + /** + * Returns true if the entry has comments. + * + * @return true if there are comments, false otherwise. + */ + public final boolean hasComments() { + return (!comments.isEmpty()); + } - /** - * Returns the comment's author nickname. - * - * @return The nickname. - */ - public final String getNick() - { - return nick; - } + /** + * Returns true if the entry has tags. + * + * @return true if there are tags, false otherwise. + */ + public final boolean hasTags() { + return (!tags.isEmpty()); + } - /** - * Sets the comment's author nickname. - * - * @param nick The new nickname. - */ - public final void setNick(final String nick) - { - this.nick = nick; - } + /** + * /** Sets a comment. + * + * @param index The comment's index. + * @param comment The actual comment. + * @param nick The nickname of the author of the comment. + */ + public final void setComment(final int index, final String comment, final String nick) { + if (index < comments.size()) { + comments.set(index, new EntryComment(comment, nick)); + } + } - /** - * Returns the tags. - * - * @return The tags. - */ - public final List<SyndCategory> getTags() - { - return tags; - } + /** + * Sets the tags. + * + * @param tags The space-delimited tags. + */ + public final void setTags(final String tags) { + if (tags != null) { + final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); - /** - * Sets the tags. - * - * @param tags The tags. - */ - private void setTags(final List<SyndCategory> tags) - { - this.tags.addAll(tags); - } + SyndCategoryImpl tag; + String part; + char mod; - /** - * Returns the comment's title. - * - * @return The title. - */ - public final String getTitle() - { - return title; - } + for (final String rawPart : parts) { + part = rawPart.trim(); - /** - * Sets the comment's title. - * - * @param title The new title. - */ - public final void setTitle(final String title) - { - this.title = title; - } + if (part.length() >= 2) { + tag = new SyndCategoryImpl(); + tag.setName(part.substring(1).toLowerCase()); - /** - * Returns true if the entry has comments. - * - * @return true if there are comments, false otherwise. - */ - public final boolean hasComments() - { - return (!comments.isEmpty()); - } + mod = part.charAt(0); - /** - * Returns true if the entry has tags. - * - * @return true if there are tags, false otherwise. - */ - public final boolean hasTags() - { - return (!tags.isEmpty()); - } + if (mod == '-') { + // Don't remove the channel tag, if any. + if (!tag.getName().equals(channel.substring(1))) { + this.tags.remove(tag); + } + } else if (mod == '+') { + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } else { + tag.setName(part.trim().toLowerCase()); - /** - * /** Sets a comment. - * - * @param index The comment's index. - * @param comment The actual comment. - * @param nick The nickname of the author of the comment. - */ - public final void setComment(final int index, final String comment, final String nick) - { - if (index < comments.size()) - { - comments.set(index, new EntryComment(comment, nick)); - } - } + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } + } + } + } + } - /** - * Returns a string representation of the object. - * - * @return A string representation of the object. - */ - public final String toString() - { + /** + * Returns a string representation of the object. + * + * @return A string representation of the object. + */ + public final String toString() { - return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date - + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' - + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; - } + return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date + + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' + + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 8a7058d..ee8b4a5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -48,76 +48,67 @@ import java.util.List; * @created Feb 1, 2004 * @since 1.0 */ -class FeedReader implements Runnable -{ - /** - * The maximum number of feed items to display. - */ - private static final int MAX_ITEMS = 5; +class FeedReader implements Runnable { + /** + * The maximum number of feed items to display. + */ + private static final int MAX_ITEMS = 5; - /** - * The tab indent (4 spaces). - */ - private static final String TAB_INDENT = " "; + /** + * The tab indent (4 spaces). + */ + private static final String TAB_INDENT = " "; - /** - * The bot. - */ - private final Mobibot bot; + /** + * The bot. + */ + private final Mobibot bot; - /** - * The nick of the person who sent the message. - */ - private final String sender; + /** + * The nick of the person who sent the message. + */ + private final String sender; - /** - * The URL to fetch. - */ - private final String url; + /** + * The URL to fetch. + */ + private final String url; - /** - * Creates a new {@link FeedReader} instance. - * - * @param bot The bot's instance. - * @param sender The nick of the person who sent the message. - * @param url The URL to fetch. - */ - public FeedReader(final Mobibot bot, final String sender, final String url) - { - this.bot = bot; - this.sender = sender; - this.url = url; - } + /** + * Creates a new {@link FeedReader} instance. + * + * @param bot The bot's instance. + * @param sender The nick of the person who sent the message. + * @param url The URL to fetch. + */ + public FeedReader(final Mobibot bot, final String sender, final String url) { + this.bot = bot; + this.sender = sender; + this.url = url; + } - /** - * Fetches the Feed's items. - */ - public final void run() - { - try - { - final SyndFeedInput input = new SyndFeedInput(); - final SyndFeed feed = input.build(new XmlReader(new URL(url))); + /** + * Fetches the Feed's items. + */ + public final void run() { + try { + final SyndFeedInput input = new SyndFeedInput(); + final SyndFeed feed = input.build(new XmlReader(new URL(url))); - SyndEntry item; - final List items = feed.getEntries(); + SyndEntry item; + final List items = feed.getEntries(); - for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) - { - item = (SyndEntryImpl) items.get(i); - bot.send(sender, item.getTitle()); - bot.send(sender, TAB_INDENT + Utils.green(item.getLink())); - } - } - catch (MalformedURLException e) - { - bot.getLogger().debug("Invalid feed URL.", e); - bot.send(sender, "The feed URL is invalid."); - } - catch (Exception e) - { - bot.getLogger().debug("Unable to fetch the feed.", e); - bot.send(sender, "An error has occurred while fetching the feed: " + e.getMessage()); - } - } + for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { + item = (SyndEntryImpl) items.get(i); + bot.send(sender, item.getTitle()); + bot.send(sender, TAB_INDENT + Utils.green(item.getLink())); + } + } catch (MalformedURLException e) { + bot.getLogger().debug("Invalid feed URL.", e); + bot.send(sender, "The feed URL is invalid."); + } catch (Exception e) { + bot.getLogger().debug("Unable to fetch the feed.", e); + bot.send(sender, "An error has occurred while fetching the feed: " + e.getMessage()); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 5cf86ef..4eb851d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -53,2055 +53,1684 @@ import java.util.*; * @since 1.0 */ @Version(properties = "version.properties", className = "ReleaseInfo") -public class Mobibot extends PircBot -{ - /** - * The connect/read timeout in ms. - */ - public static final int CONNECT_TIMEOUT = 5000; - - /** - * The empty title string. - */ - static final String NO_TITLE = "No Title"; - - /** - * The default port. - */ - private static final int DEFAULT_PORT = 6667; - - /** - * The info strings. - */ - private static final String[] INFO_STRS = { - ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + " by Erik C. Thauvin (erik@thauvin.net)", - "http://www.mobitopia.org/mobibot/" - }; - - /** - * The link match string. - */ - private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - - /** - * The default maximum number of entries to display. - */ - private static final int MAX_ENTRIES = 8; - - /** - * The default maximum recap entries. - */ - private static final int MAX_RECAP = 10; - - /** - * The maximum number of times the bot will try to reconnect, if disconnected. - */ - private static final int MAX_RECONNECT = 10; - - /** - * The number of milliseconds to delay between consecutive messages. - */ - private static final long MESSAGE_DELAY = 1000L; - - /** - * The modules. - */ - private static final List<AbstractModule> MODULES = new ArrayList<>(0); - - /** - * The start time. - */ - private static final long START_TIME = System.currentTimeMillis(); - - /** - * The tags/categories marker. - */ - private static final String TAGS_MARKER = "tags:"; - - /** - * The version strings. - */ - private static final String[] VERSION_STRS = { - "Version: " - + ReleaseInfo.getVersion() - + " (" - + Utils.ISO_SDF.format(ReleaseInfo.getBuildDate()) + ')', - "Platform: " - + System.getProperty("os.name") - + " (" - + System.getProperty("os.version") - + ", " - + System.getProperty("os.arch") - + ", " - + System.getProperty("user.country") + ')', - "Runtime: " - + System.getProperty("java.runtime.name") - + " (build " - + System.getProperty("java.runtime.version") - + ')', - "VM: " - + System.getProperty("java.vm.name") - + " (build " - + System.getProperty("java.vm.version") - + ", " - + System.getProperty("java.vm.info") - + ')' - }; - - /** - * The tell object. - */ - private static Tell tell; - - /** - * The main channel. - */ - private final String channel; - - /** - * The commands list. - */ - private final List<String> commandsList = new ArrayList<>(); - - /** - * The entries array. - */ - private final List<EntryLink> entries = new ArrayList<>(0); - - /** - * The history/backlogs array. - */ - private final List<String> history = new ArrayList<>(0); - - /** - * The ignored nicks array. - */ - private final List<String> ignoredNicks = new ArrayList<>(0); - - /** - * The IRC port. - */ - private final int ircPort; - - /** - * The IRC server. - */ - private final String ircServer; - - /** - * The logger. - */ - private final Log4JLogger logger = new Log4JLogger(Mobibot.class.getPackage().getName()); - - /** - * The logger default level. - */ - private final Level loggerLevel; - - /** - * The log directory. - */ - private final String logsDir; - - /** - * The recap array. - */ - private final List<String> recap = new ArrayList<>(0); - - /** - * The backlogs URL. - */ - private String backLogsUrl = ""; - - /** - * The default tags/categories. - */ - private String defaultTags = ""; - - /** - * The del.icio.us posts handler. - */ - private DeliciousPoster delicious = null; - - /** - * The feed URL. - */ - private String feedURL = ""; - - /** - * The ident message. - */ - private String identMsg = ""; - - /** - * The ident nick. - */ - private String identNick = ""; - - /** - * The NickServ ident password. - */ - private String identPwd = ""; - - /** - * Today's date. - */ - private String today = Utils.today(); - - /** - * The weblog URL. - */ - private String weblogUrl = ""; - - /** - * Creates a new {@link Mobibot} instance. - * - * @param server The server. - * @param port The port. - * @param nickname The nickname. - * @param channel The channel. - * @param logsDir The logs directory. - */ - @SuppressWarnings("WeakerAccess") - public Mobibot(final String server, - final int port, - final String nickname, - final String channel, - final String logsDir) - { - System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); - System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); - - setName(nickname); - - ircServer = server; - ircPort = port; - this.channel = channel; - this.logsDir = logsDir; - - // Set the logger level - loggerLevel = logger.getLogger().getLevel(); - - // Initialization - Utils.UTC_SDF.setTimeZone(TimeZone.getTimeZone("UTC")); - - // Load the current entries, if any. - try - { - today = EntriesMgr.loadEntries(this.logsDir + EntriesMgr.CURRENT_XML, this.channel, entries); - - if (logger.isDebugEnabled()) - { - logger.debug("Last feed: " + today); - } - - if (!Utils.today().equals(today)) - { - entries.clear(); - today = Utils.today(); - } - } - catch (IOException ignore) - { - ; // Do nothing. - } - catch (FeedException e) - { - logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); - } - - // Load the backlogs, if any. - try - { - EntriesMgr.loadBacklogs(this.logsDir + EntriesMgr.NAV_XML, history); - } - catch (IOException ignore) - { - ; // Do nothing. - } - catch (FeedException e) - { - logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); - } - - // Load the modules - MODULES.add(new Calc()); - MODULES.add(new CurrencyConverter()); - MODULES.add(new Dice()); - MODULES.add(new GoogleSearch()); - MODULES.add(new Joke()); - MODULES.add(new Lookup()); - MODULES.add(new Ping()); - MODULES.add(new StockQuote()); - MODULES.add(new Twitter()); - MODULES.add(new War()); - MODULES.add(new Weather()); - MODULES.add(new WorldTime()); - } - - /** - * The Truth Is Out There... - * - * @param args The command line arguments. - */ - public static void main(final String[] args) - { - // Setup the command line options - final Options options = new Options(); - options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); - options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, - "print debug & logging data directly to the console"); - options.addOption(Option.builder( - Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") - .longOpt(Commands.PROPS_ARG).build()); - options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); - - // Parse the command line - final CommandLineParser parser = new DefaultParser(); - CommandLine line = null; - - try - { - line = parser.parse(options, args); - } - catch (ParseException e) - { - System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } - - if (line.hasOption(Commands.HELP_ARG.charAt(0))) - { - // Output the usage - new HelpFormatter().printHelp(Mobibot.class.getName(), options); - } - else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) - { - for (final String s : INFO_STRS) - { - System.out.println(s); - } - } - else - { - final Properties p = new Properties(); - - try (final FileInputStream fis = new FileInputStream( - new File(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) - { - // Load the properties files - p.load(fis); - } - catch (FileNotFoundException e) - { - System.err.println("Unable to find properties file."); - e.printStackTrace(System.err); - System.exit(1); - } - catch (IOException e) - { - System.err.println("Unable to open properties file."); - e.printStackTrace(System.err); - System.exit(1); - } - - // Get the main properties - final String channel = p.getProperty("channel"); - final String server = p.getProperty("server"); - final int port = Utils.getIntProperty(p.getProperty("port"), DEFAULT_PORT); - final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase()); - final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); - - if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) - { - // Redirect the stdout and stderr - PrintStream stdout = null; - - try - { - stdout = new PrintStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); - } - catch (IOException e) - { - System.err.println("Unable to open output (stdout) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - - PrintStream stderr = null; - - try - { - stderr = new PrintStream(new FileOutputStream(logsDir + nickname + ".err", true)); - } - catch (IOException e) - { - System.err.println("Unable to open error (stderr) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - - System.setOut(stdout); - System.setErr(stderr); - } - - // Get the bot's properties - final String login = p.getProperty("login", nickname); - final String weblogURL = p.getProperty("weblog", ""); - final String feedURL = p.getProperty("feed", ""); - final String backlogsURL = Utils.ensureDir(p.getProperty("backlogs", weblogURL), true); - final String ignoredNicks = p.getProperty("ignore", ""); - final String identNick = p.getProperty("ident-nick", ""); - final String identMsg = p.getProperty("ident-msg", ""); - final String identPwd = p.getProperty("ident", ""); - final String tags = p.getProperty("tags", ""); - - // Get the del.icio.us properties - final String dname = p.getProperty("delicious-user"); - final String dpwd = p.getProperty("delicious-pwd"); - - // Create the bot - final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir); - - // Get the tell command settings - tell = new Tell(bot, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); - - // Initialize the bot - bot.setVerbose(true); - bot.setAutoNickChange(true); - bot.setLogin(login); - bot.setVersion(weblogURL); - bot.setMessageDelay(MESSAGE_DELAY); - bot.setIdentity(identPwd, identNick, identMsg); - - // Set the URLs - bot.setWeblogUrl(weblogURL); - bot.setFeedURL(feedURL); - bot.setBacklogsUrl(backlogsURL); - - // Set the del.icio.us authentication - bot.setDeliciousAuth(dname, dpwd); - - // Load the modules properties - MODULES.stream().filter(AbstractModule::hasProperties).forEach( - module -> - { - for (final String s : module.getPropertyKeys()) - { - module.setProperty(s, p.getProperty(s, "")); - } - }); - - // Set the tags - bot.setTags(tags); - - // Set the ignored nicks - bot.setIgnoredNicks(ignoredNicks); - - // Save the entries - bot.saveEntries(true); - - // Connect - try - { - bot.connect(server, port); - } - catch (Exception e) - { - int retries = 0; - - while ((retries++ < MAX_RECONNECT) && !bot.isConnected()) - { - sleep(10); - - try - { - bot.connect(server, port); - } - catch (Exception ignore) - { - if (retries == MAX_RECONNECT) - { - System.err.println( - "Unable to connect to " + server + " after " + MAX_RECONNECT + " retries."); - e.printStackTrace(System.err); - System.exit(1); - } - } - } - } - - bot.setVersion(INFO_STRS[0]); - - bot.identify(); - - bot.joinChannel(channel); - } - } - - /** - * Sleeps for the specified number of seconds. - * - * @param secs The number of seconds to sleep for. - */ - private static void sleep(final int secs) - { - try - { - Thread.sleep((long) (secs * 1000)); - } - catch (InterruptedException ignore) - { - ; // Do nothing - } - } - - /** - * Sends an action to the current channel. - * - * @param action The action. - */ - final public void action(final String action) - { - action(channel, action); - } - - /** - * Sends an action to the channel. - * - * @param channel The channel. - * @param action The action. - */ - private void action(final String channel, final String action) - { - if (Utils.isValidString(channel) && Utils.isValidString(action)) - { - sendAction(channel, action); - } - } - - /** - * Responds with the title and links from the RSS feed. - * - * @param sender The nick of the person who sent the private message. - */ - private void feedResponse(final String sender) - { - if (Utils.isValidString(feedURL)) - { - new Thread(new FeedReader(this, sender, feedURL)).start(); - } - else - { - send(sender, "There is no weblog setup for this channel."); - } - } - - /** - * Returns the index of the specified duplicate entry, if any. - * - * @param link The link. - * - * @return The index or -1 if none. - */ - private int findDupEntry(final String link) - { - synchronized (entries) - { - for (int i = 0; i < entries.size(); i++) - { - if (link.equals(entries.get(i).getLink())) - { - return i; - } - } - } - - return -1; - } - - /** - * Returns the backlogs URL. - * - * @return The backlogs URL. - */ - public final String getBacklogsUrl() - { - return this.backLogsUrl; - } - - /** - * Sets the backlogs URL. - * - * @param backLogsUrl The backlogs URL. - */ - private void setBacklogsUrl(final String backLogsUrl) - { - this.backLogsUrl = backLogsUrl; - } - - /** - * Returns the current channel. - * - * @return The current channel. - */ - public final String getChannel() - { - return channel; - } - - /** - * Returns the irc server. - * - * @return The irc server. - */ - public final String getIrcServer() - { - return this.ircServer; - } - - /** - * Returns the bot's logger. - * - * @return The bot's logger. - */ - public final Log4JLogger getLogger() - { - return logger; - } - - /** - * Returns the log directory. - * - * @return the log directory. - */ - public final String getLogsDir() - { - return this.logsDir; - } - - /** - * Returns the bot's nickname regexp pattern. - * - * @return The nickname regexp pattern. - */ - private String getNickPattern() - { - final StringBuilder buff = new StringBuilder(0); - - for (final char c : getNick().toCharArray()) - { - if (Character.isLetter(c)) - { - buff.append('[') - .append(String.valueOf(c).toLowerCase()) - .append(String.valueOf(c).toUpperCase()) - .append(']'); - } - else - { - buff.append(c); - } - } - - return buff.toString(); - } - - /** - * Get today's date for the feed. - * - * @return Today's date. - */ - public String getToday() - { - return this.today; - } - - /** - * Returns the weblog URL. - * - * @return The weblog URL. - */ - public final String getWeblogUrl() - { - return this.weblogUrl; - } - - /** - * Sets the weblog URL. - * - * @param weblogUrl The weblog URL. - */ - private void setWeblogUrl(final String weblogUrl) - { - this.weblogUrl = weblogUrl; - } - - /** - * Returns indented and bold help string. - * - * @param help The help string. - * - * @return The indented help string. - */ - final public String helpIndent(final String help) - { - return helpIndent(help, true); - } - - /** - * Returns indented help string. - * - * @param help The help string. - * @param isBold The bold flag. - * - * @return The indented help string. - */ - public String helpIndent(final String help, final boolean isBold) - { - return " " + (isBold ? Utils.bold(help) : help); - } - - /** - * Responds with the bot's help. - * - * @param sender The nick of the person who sent the private message. - * @param topic The help topic, if any. - */ - private void helpResponse(final String sender, final String topic) - { - final String lcTopic = topic.toLowerCase().trim(); - - if (lcTopic.equals(Commands.HELP_POSTING_KEYWORD)) - { - send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); - send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); - send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + '1')); - send(sender, "To add a title, use a its label and a pipe:"); - send(sender, helpIndent(Commands.LINK_CMD + "1:|This is the title")); - send(sender, "To add a comment: "); - send(sender, helpIndent(Commands.LINK_CMD + "1:This is a comment")); - send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + "1.1")); - send(sender, "To edit a comment, use its label: "); - send(sender, helpIndent(Commands.LINK_CMD + "1.1:This is an edited comment")); - send(sender, "To delete a comment, use its label and a minus sign: "); - send(sender, helpIndent(Commands.LINK_CMD + "1.1:-")); - send(sender, "You can also view a posting by saying its label."); - } - else if (lcTopic.equals(Commands.HELP_TAGS_KEYWORD)) - { - send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:")); - send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); - } - else if (lcTopic.equals(Commands.VIEW_CMD)) - { - send(sender, "To list or search the current URL posts:"); - send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); - } - else if (lcTopic.equals(channel.substring(1).toLowerCase())) - { - send(sender, "To list the last 5 posts from the channel's weblog:"); - send(sender, helpIndent(getNick() + ": " + channel.substring(1))); - } - else if (lcTopic.equals(Commands.RECAP_CMD)) - { - send(sender, "To list the last 10 public channel messages:"); - send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); - } - else if (lcTopic.equals(Commands.USERS_CMD)) - { - send(sender, "To list the users present on the channel:"); - send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); - } - else if (lcTopic.equals(Commands.INFO_CMD)) - { - send(sender, "To view information about the bot:"); - send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); - } - else if (lcTopic.equals(Commands.CYCLE_CMD) && isOp(sender)) - { - send(sender, "To have the bot leave the channel and come back:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); - } - else if (lcTopic.equals(Commands.ME_CMD) && isOp(sender)) - { - send(sender, "To have the bot perform an action:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); - } - else if (lcTopic.equals(Commands.SAY_CMD) && isOp(sender)) - { - send(sender, "To have the bot say something on the channel:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); - } - else if (lcTopic.equals(Commands.VERSION_CMD) && isOp(sender)) - { - send(sender, "To view the version data (bot, java, etc.):"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); - } - else if (lcTopic.equals(Commands.MSG_CMD) && isOp(sender)) - { - send(sender, "To have the bot send a private message to someone:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); - } - else if (lcTopic.equals(Commands.IGNORE_CMD)) - { - send(sender, "To check your ignore status:"); - send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); - - send(sender, "To toggle your ignore status:"); - send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); - } - else if (lcTopic.equals(Tell.TELL_CMD) && tell.isEnabled()) - { - tell.helpResponse(sender); - } - else - { - for (final AbstractModule module : MODULES) - { - for (final String cmd : module.getCommands()) - { - if (lcTopic.equals(cmd)) - { - module.helpResponse(this, sender, topic, true); - return; - } - } - } - - send(sender, Utils.bold("Type a URL on " + channel + " to post it.")); - send(sender, "For more information on a specific command, type:"); - send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); - send(sender, "The commands are:"); - - if (commandsList.isEmpty()) - { - commandsList.add(Commands.IGNORE_CMD); - commandsList.add(Commands.INFO_CMD); - commandsList.add(channel.substring(1)); - commandsList.add(Commands.HELP_POSTING_KEYWORD); - commandsList.add(Commands.HELP_TAGS_KEYWORD); - commandsList.add(Commands.RECAP_CMD); - commandsList.add(Commands.USERS_CMD); - commandsList.add(Commands.VIEW_CMD); - - MODULES.stream().filter(AbstractModule::isEnabled).forEach( - module -> commandsList.addAll(module.getCommands())); - - if (tell.isEnabled()) - { - commandsList.add(Tell.TELL_CMD); - } - - Collections.sort(commandsList); - } - - final StringBuilder sb = new StringBuilder(0); - - for (int i = 0, cmdCount = 1; i < commandsList.size(); i++, cmdCount++) - { - if (sb.length() > 0) - { - sb.append(" "); - } - - sb.append(commandsList.get(i)); - - // 6 commands per line or last command - if (sb.length() > 0 && (cmdCount == 6 || i == (commandsList.size() - 1))) - { - send(sender, helpIndent(sb.toString())); - - sb.setLength(0); - cmdCount = 0; - } - } - - if (isOp(sender)) - { - send(sender, "The op commands are:"); - send(sender, helpIndent( - Commands.CYCLE_CMD + " " - + Commands.ME_CMD + " " - + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " - + Commands.VERSION_CMD)); - } - } - } - - /** - * Identifies the bot. - */ - private void identify() - { - // Identify with NickServ - if (Utils.isValidString(identPwd)) - { - identify(identPwd); - } - - // Identify with a specified nick - if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) - { - sendMessage(identNick, identMsg); - } - } - - /** - * Processes the {@link net.thauvin.erik.mobibot.Commands#IGNORE_CMD} command. - * - * @param sender The sender. - * @param args The command arguments. - */ - private void ignoreResponse(final String sender, final String args) - { - if (!isOp(sender)) - { - final String nick = sender.toLowerCase(); - final boolean isMe = args.toLowerCase().startsWith(Commands.IGNORE_ME_KEYWORD); - - if (ignoredNicks.contains(nick)) - { - if (isMe) - { - ignoredNicks.remove(nick); - - send(sender, "You are no longer ignored."); - } - else - { - send(sender, "You are currently ignored."); - } - } - else - { - if (isMe) - { - ignoredNicks.add(nick); - - send(sender, "You are now ignored."); - } - else - { - send(sender, "You are not currently ignored."); - } - } - } - else - { - if (args.length() > 0) - { - final String[] nicks = args.toLowerCase().split(" "); - - for (final String nick : nicks) - { - final String ignore; - - if (Commands.IGNORE_ME_KEYWORD.equals(nick)) - { - ignore = sender.toLowerCase(); - } - else - { - ignore = nick; - } - - if (ignoredNicks.contains(ignore)) - { - ignoredNicks.remove(ignore); - } - else - { - ignoredNicks.add(ignore); - } - } - } - - send(sender, "The following nicks are ignored: " + ignoredNicks.toString()); - } - } - - /** - * Responds with the bot's information. - * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void infoResponse(final String sender, final boolean isPrivate) - { - for (final String info : INFO_STRS) - { - if (info.startsWith("http://")) - { - send(sender, Utils.green(info), isPrivate); - } - else - { - send(sender, info, isPrivate); - } - } - - final StringBuilder info = new StringBuilder("Uptime: "); - - long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; - - final long years = timeInSeconds / 31540000L; - - if (years > 0) - { - info.append(years).append(Utils.plural(years, " year ", " years ")); - timeInSeconds -= (years * 31540000L); - } - - final long weeks = timeInSeconds / 604800L; - - if (weeks > 0) - { - info.append(weeks).append(Utils.plural(weeks, " week ", " weeks ")); - timeInSeconds -= (weeks * 604800L); - } - - final long days = timeInSeconds / 86400L; - - if (days > 0) - { - info.append(days).append(Utils.plural(days, " day ", " days ")); - timeInSeconds -= (days * 86400L); - } - - final long hours = timeInSeconds / 3600L; - - if (hours > 0) - { - info.append(hours).append(Utils.plural(hours, " hour ", " hours ")); - timeInSeconds -= (hours * 3600L); - } - - final long minutes = timeInSeconds / 60L; - - info.append(minutes).append(Utils.plural(minutes, " minute ", " minutes ")); - - info.append("[Entries: ").append(entries.size()); - - if (tell.isEnabled() && isOp(sender)) - { - info.append(", Messages: ").append(tell.size()); - } - - info.append(']'); - - send(sender, info.toString(), isPrivate); - } - - /** - * Determines whether the specified nick should be ignored. - * - * @param nick The nick. - * - * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. - */ - private boolean isIgnoredNick(final String nick) - { - return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); - } - - /** - * Returns <code>true</code> if the specified sender is an Op on the {@link #channel channel}. - * - * @param sender The sender. - * - * @return true, if the sender is an Op. - */ - public boolean isOp(final String sender) - { - final User[] users = getUsers(channel); - - for (final User user : users) - { - if (user.getNick().equals(sender)) - { - return user.isOp(); - } - } - - return false; - } - - @Override - protected final void onAction(final String sender, - final String login, - final String hostname, - final String target, - final String action) - { - if (target.equals(channel)) - { - storeRecap(sender, action, true); - } - } - - @Override - protected final void onDisconnect() - { - if (Utils.isValidString(weblogUrl)) - { - setVersion(weblogUrl); - } - - sleep(5); - - // Connect - try - { - connect(ircServer, ircPort); - } - catch (Exception e) - { - int retries = 0; - - while ((retries++ < MAX_RECONNECT) && !isConnected()) - { - sleep(10); - - try - { - connect(ircServer, ircPort); - } - catch (Exception ex) - { - if (retries == MAX_RECONNECT) - { - if (logger.isDebugEnabled()) - { - logger.debug( - "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", - ex); - } - - e.printStackTrace(System.err); - System.exit(1); - } - } - } - } - - setVersion(INFO_STRS[0]); - - identify(); - - joinChannel(channel); - } - - @Override - protected void onJoin(final String channel, final String sender, final String login, final String hostname) - { - tell.send(sender); - } - - @Override - protected final void onMessage(final String channel, - final String sender, - final String login, - final String hostname, - final String message) - { - if (logger.isDebugEnabled()) - { - logger.debug(">>> " + sender + ": " + message); - } - - boolean isCommand = false; - - // Capture URLs posted on the channel - if (message.matches(LINK_MATCH) && !isIgnoredNick(sender)) - { - isCommand = true; - - final String[] cmds = message.split(" ", 2); - - if (cmds.length == 1 || (!cmds[1].contains(getNick()))) - { - final String link = cmds[0].trim(); - boolean isBackup = false; - - final int dupIndex = findDupEntry(link); - - if (dupIndex == -1) - { - if (!Utils.today().equals(today)) - { - isBackup = true; - saveEntries(true); - - entries.clear(); - today = Utils.today(); - } - - final StringBuilder tags = new StringBuilder(defaultTags); - String title = NO_TITLE; - - if (cmds.length == 2) - { - final String[] data = cmds[1].trim().split(TAGS_MARKER, 2); - - if (data.length == 1) - { - title = data[0].trim(); - } - else - { - if (Utils.isValidString(data[0])) - { - title = data[0].trim(); - } - - tags.append(' ').append(data[1].trim()); - } - } - - if (NO_TITLE.equals(title)) - { - try - { - final Document html = Jsoup.connect(link).userAgent("Mozilla").get(); - final String htmlTitle = html.title(); - - if (Utils.isValidString(htmlTitle)) - { - title = htmlTitle; - } - } - catch (IOException ignore) - { - // Do nothing - } - } - - entries.add(new EntryLink(link, title, sender, login, channel, tags.toString())); - - final int index = entries.size() - 1; - final EntryLink entry = entries.get(index); - send(channel, Utils.buildLink(index, entry)); - - if (delicious != null) - { - delicious.addPost(entry); - } - - saveEntries(isBackup); - - if (NO_TITLE.equals(entry.getTitle())) - { - send(sender, "Please specify a title, by typing:", true); - send(sender, helpIndent(Commands.LINK_CMD + (index + 1) + ":|This is the title"), true); - } - } - else - { - final EntryLink entry = entries.get(dupIndex); - send(sender, "Duplicate >> " + Utils.buildLink(dupIndex, entry)); - } - } - } - // mobibot: <command> - else if (message.matches(getNickPattern() + ":.*")) - { - isCommand = true; - - final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); - final String cmd = cmds[0].toLowerCase(); - - String args = ""; - - if (cmds.length > 1) - { - args = cmds[1].trim(); - } - - // mobibot: help - if (cmd.startsWith(Commands.HELP_CMD)) - { - helpResponse(sender, args); - } - // mobibot: recap - else if (cmd.equals(Commands.RECAP_CMD)) - { - recapResponse(sender, false); - } - // mobibot: users - else if (cmd.equals(Commands.USERS_CMD)) - { - usersResponse(sender, false); - } - // mobibot: info - else if (cmd.equals(Commands.INFO_CMD)) - { - infoResponse(sender, false); - } - // mobbiot: version - else if (cmd.equals(Commands.VERSION_CMD)) - { - versionResponse(sender, false); - } - // mobibot: <channel> - else if (cmd.equalsIgnoreCase(channel.substring(1))) - { - feedResponse(sender); - } - // mobibot: view - else if (cmd.startsWith(Commands.VIEW_CMD)) - { - viewResponse(sender, args, false); - } - // mobibot: tell - else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) - { - tell.response(sender, args); - } - // mobibot: ignore - else if (cmd.startsWith(Commands.IGNORE_CMD)) - { - ignoreResponse(sender, args); - } - // modules - else - { - for (final AbstractModule module : MODULES) - { - for (final String c : module.getCommands()) - { - if (cmd.startsWith(c)) - { - module.commandResponse(this, sender, args, false); - } - } - } - } - } - // L1:<comment>, L1:-, L1:|<title>, etc. - else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) - { - isCommand = true; - - final String[] cmds = message.substring(1).split(":", 2); - final int index = Integer.parseInt(cmds[0]) - 1; - - // L1:<comment> - if (index < entries.size()) - { - final String cmd = cmds[1].trim(); - - if (cmd.length() == 0) - { - final EntryLink entry = entries.get(index); - send(channel, Utils.buildLink(index, entry)); - - if (entry.hasTags()) - { - send(channel, Utils.buildTags(index, entry)); - } - - if (entry.hasComments()) - { - final EntryComment[] comments = entry.getComments(); - - for (int i = 0; i < comments.length; i++) - { - send(channel, Utils.buildComment(index, i, comments[i])); - } - } - } - else - { - // L1:- - if ("-".equals(cmd)) - { - final EntryLink entry = entries.get(index); - - if (entry.getLogin().equals(login) || isOp(sender)) - { - if (delicious != null) - { - delicious.deletePost(entry); - } - - entries.remove(index); - send(channel, "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); - saveEntries(false); - } - else - { - send(sender, "Please ask a channel op to remove this entry for you."); - } - } - // L1:|<title> - else if (cmd.charAt(0) == '|') - { - if (cmd.length() > 1) - { - final EntryLink entry = entries.get(index); - entry.setTitle(cmd.substring(1).trim()); - - if (delicious != null) - { - delicious.updatePost(entry.getLink(), entry); - } - - send(channel, Utils.buildLink(index, entry)); - saveEntries(false); - } - } - // L1:=<url> - else if (cmd.charAt(0) == '=') - { - final EntryLink entry = entries.get(index); - - if (entry.getLogin().equals(login) || isOp(sender)) - { - final String link = cmd.substring(1); - - if (link.matches(LINK_MATCH)) - { - final String oldLink = entry.getLink(); - - entry.setLink(link); - - if (delicious != null) - { - delicious.updatePost(oldLink, entry); - } - - send(channel, Utils.buildLink(index, entry)); - saveEntries(false); - } - } - else - { - send(sender, "Please ask a channel op to change this link for you."); - } - } - // L1:?<author> - else if (cmd.charAt(0) == '?') - { - if (isOp(sender)) - { - if (cmd.length() > 1) - { - final EntryLink entry = entries.get(index); - entry.setNick(cmd.substring(1)); - send(channel, Utils.buildLink(index, entry)); - saveEntries(false); - } - } - else - { - send(sender, "Please ask a channel op to change the author of this link for you."); - } - } - else - { - final EntryLink entry = entries.get(index); - final int cindex = entry.addComment(cmd, sender); - - final EntryComment comment = entry.getComment(cindex); - send(sender, Utils.buildComment(index, cindex, comment)); - saveEntries(false); - } - } - } - } - // L1T:<+-tag> - else if (message.matches(Commands.LINK_CMD + "[0-9]+T:.*")) - { - isCommand = true; - - final String[] cmds = message.substring(1).split("T:", 2); - final int index = Integer.parseInt(cmds[0]) - 1; - - if (index < entries.size()) - { - final String cmd = cmds[1].trim(); - - final EntryLink entry = entries.get(index); - - if (cmd.length() != 0) - { - if (entry.getLogin().equals(login) || isOp(sender)) - { - entry.setTags(cmd); - - if (delicious != null) - { - delicious.updatePost(entry.getLink(), entry); - } - - send(channel, Utils.buildTags(index, entry)); - saveEntries(false); - } - else - { - send(sender, "Please ask a channel op to change the tags for you."); - } - } - else - { - if (entry.hasTags()) - { - send(channel, Utils.buildTags(index, entry)); - } - else - { - send(sender, "The entry has no tags. Why don't add some?"); - } - } - } - } - // L1.1:<command> - else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) - { - isCommand = true; - - final String[] cmds = message.substring(1).split("[.:]", 3); - final int index = Integer.parseInt(cmds[0]) - 1; - - if (index < entries.size()) - { - final EntryLink entry = entries.get(index); - final int cindex = Integer.parseInt(cmds[1]) - 1; - - if (cindex < entry.getCommentsCount()) - { - final String cmd = cmds[2].trim(); - - // L1.1: - if (cmd.length() == 0) - { - final EntryComment comment = entry.getComment(cindex); - send(channel, Utils.buildComment(index, cindex, comment)); - } - // L1.1:- - else if ("-".equals(cmd)) - { - entry.deleteComment(cindex); - send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); - saveEntries(false); - } - // L1.1:?<author> - else if (cmd.charAt(0) == '?') - { - if (isOp(sender)) - { - if (cmd.length() > 1) - { - final EntryComment comment = entry.getComment(cindex); - comment.setNick(cmd.substring(1)); - send(channel, Utils.buildComment(index, cindex, comment)); - saveEntries(false); - } - } - else - { - send(sender, "Please ask a channel op to change the author of this comment for you."); - } - } - else - { - entry.setComment(cindex, cmd, sender); - - final EntryComment comment = entry.getComment(cindex); - send(sender, Utils.buildComment(index, cindex, comment)); - saveEntries(false); - } - } - } - } - - if (!isCommand) - { - storeRecap(sender, message, false); - } - - tell.send(sender, true); - } - - @Override - protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) - { - tell.send(newNick); - } - - @Override - protected final void onPrivateMessage(final String sender, - final String login, - final String hostname, - final String message) - { - if (logger.isDebugEnabled()) - { - logger.debug(">>> " + sender + ": " + message); - } - - final String[] cmds = message.split(" ", 2); - final String cmd = cmds[0].toLowerCase(); - String args = ""; - - if (cmds.length > 1) - { - args = cmds[1].trim(); - } - - if (cmd.startsWith(Commands.HELP_CMD)) - { - helpResponse(sender, args); - } - else if ("kill".equals(cmd) && isOp(sender)) - { - sendRawLine("QUIT : Poof!"); - System.exit(0); - } - else if (cmd.equals(Commands.DIE_CMD) && isOp(sender)) - { - send(channel, sender + " has just signed my death sentence."); - saveEntries(true); - sleep(3); - quitServer("The Bot Is Out There!"); - System.exit(0); - } - else if (cmd.equals(Commands.CYCLE_CMD)) - { - send(channel, sender + " has just asked me to leave. I'll be back!"); - sleep(0); - partChannel(channel); - sleep(10); - joinChannel(channel); - } - else if (cmd.equals(Commands.RECAP_CMD)) - { - recapResponse(sender, true); - } - else if (cmd.equals(Commands.USERS_CMD)) - { - usersResponse(sender, true); - } - else if (cmd.equals(Commands.ADDLOG_CMD) && (cmds.length > 1) && isOp(sender)) - { - // e.g. 2014-04-01 - final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); - if (backlog.exists()) - { - history.add(0, args); - send(sender, history.toString(), true); - } - else - { - send(sender, "The specified log could not be found."); - } - } - else if (cmd.equals(Commands.ME_CMD) && isOp(sender)) - { - if (args.length() > 1) - { - action(args); - } - else - { - helpResponse(sender, Commands.ME_CMD); - } - } - else if (cmd.equals(Commands.NICK_CMD) && (cmds.length > 1)) - { - if (isOp(sender)) - { - changeNick(args); - } - } - else if (cmd.equals(Commands.SAY_CMD) && isOp(sender)) - { - if (cmds.length > 1) - { - send(channel, args, true); - } - else - { - helpResponse(sender, Commands.SAY_CMD); - } - } - else if (cmd.equals(Commands.MSG_CMD) && isOp(sender)) - { - if (cmds.length > 1) - { - final String[] msg = args.split(" ", 2); - - if (args.length() > 2) - { - send(msg[0], msg[1], true); - } - else - { - helpResponse(sender, Commands.MSG_CMD); - } - } - else - { - helpResponse(sender, Commands.MSG_CMD); - } - } - else if (cmd.equals(Commands.VIEW_CMD)) - { - viewResponse(sender, args, true); - } - else if (cmd.equals(Tell.TELL_CMD) && tell.isEnabled()) - { - tell.response(sender, args); - } - else if (cmd.equals(Commands.INFO_CMD)) - { - infoResponse(sender, true); - } - else if (cmd.equals(Commands.VERSION_CMD)) - { - versionResponse(sender, true); - } - else if (cmd.equals(Commands.DEBUG_CMD) && isOp(sender)) - { - if (logger.isDebugEnabled()) - { - logger.getLogger().setLevel(loggerLevel); - } - else - { - logger.getLogger().setLevel(Level.DEBUG); - } - - send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); - } - else - { - for (final AbstractModule module : MODULES) - { - if (module.isPrivateMsgEnabled()) - { - for (final String c : module.getCommands()) - { - if (cmd.equals(c)) - { - module.commandResponse(this, sender, args, true); - return; - } - } - } - } - - helpResponse(sender, ""); - } - } - - /** - * Responds with the last 10 public messages. - * - * @param sender The nick of the person who sent the private message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void recapResponse(final String sender, final boolean isPrivate) - { - for (final String recap : this.recap) - { - send(sender, recap, isPrivate); - } - } - - /** - * Saves the entries. - * - * @param isDayBackup Set the <code>true</code> if the daily backup file should also be created. - */ - private void saveEntries(final boolean isDayBackup) - { - EntriesMgr.saveEntries(this, entries, history, isDayBackup); - } - - /** - * Sends a private message or notice. - * - * @param sender The nick of the person who sent the message. - * @param message The actual message. - * @param isPrivate Set to <code>true</code> if the response should be a private message, otherwise a notice is - * sent. - */ - public final void send(final String sender, final String message, final boolean isPrivate) - { - if (Utils.isValidString(message) && Utils.isValidString(sender)) - { - if (isPrivate) - { - if (logger.isDebugEnabled()) - { - logger.debug("Sending message to " + sender + ": " + message); - } - - sendMessage(sender, message); - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("Sending notice to " + sender + ": " + message); - } - - sendNotice(sender, message); - } - } - } - - /** - * Sends a private notice. - * - * @param sender The nick of the person who sent the message. - * @param message The actual message. - */ - public final void send(final String sender, final String message) - { - send(sender, message, false); - } - - /** - * Sets the del.icio.us authentication. - * - * @param username The del.icio.us user name. - * @param password The del.icio.us password. - */ - private void setDeliciousAuth(final String username, final String password) - { - if (Utils.isValidString(username) && Utils.isValidString(password)) - { - delicious = new DeliciousPoster(username, password, ircServer); - } - } - - /** - * Sets the feed URL. - * - * @param feedURL The feed URL. - */ - private void setFeedURL(final String feedURL) - { - this.feedURL = feedURL; - } - - /** - * Sets the bot's identification. - * - * @param identPwd The password for NickServ, if any. - * @param identNick The ident nick name. - * @param identMsg The ident message. - */ - private void setIdentity(final String identPwd, final String identNick, final String identMsg) - { - this.identPwd = identPwd; - this.identNick = identNick; - this.identMsg = identMsg; - } - - /** - * Sets the Ignored nicks. - * - * @param nicks The nicks to ignore - */ - private void setIgnoredNicks(final String nicks) - { - if (Utils.isValidString(nicks)) - { - final StringTokenizer st = new StringTokenizer(nicks, ","); - - while (st.hasMoreTokens()) - { - ignoredNicks.add(st.nextToken().trim().toLowerCase()); - } - } - } - - /** - * Sets the default tags/categories. - * - * @param tags The tags. - */ - private void setTags(final String tags) - { - defaultTags = tags; - } - - /** - * Stores the last 10 public messages and actions. - * - * @param sender The nick of the person who sent the private message. - * @param message The actual message sent. - * @param isAction Set to <code>true</code> if the message is an action. - */ - private void storeRecap(final String sender, final String message, final boolean isAction) - { - recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") - + message); - - if (recap.size() > MAX_RECAP) - { - recap.remove(0); - } - } - - /** - * Responds with the users on a channel. - * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void usersResponse(final String sender, final boolean isPrivate) - { - final User[] users = getUsers(channel); - final String[] nicks = new String[users.length]; - - for (int i = 0; i < users.length; i++) - { - nicks[i] = users[i].getNick(); - } - - Arrays.sort(nicks, String.CASE_INSENSITIVE_ORDER); - - final StringBuilder buff = new StringBuilder(0); - - for (final String nick : nicks) - { - if (isOp(nick)) - { - buff.append('@'); - } - - buff.append(nick).append(' '); - } - - send(sender, buff.toString(), isPrivate); - } - - /** - * Responds with the bot's version info. - * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void versionResponse(final String sender, final boolean isPrivate) - { - if (isOp(sender)) - { - for (final String version : VERSION_STRS) - { - send(sender, version, isPrivate); - } - } - } - - /** - * Responds with the stored links. - * - * @param sender The nick of the person who sent the message. - * @param args The view command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void viewResponse(final String sender, final String args, final boolean isPrivate) - { - String lcArgs = args.toLowerCase(); - - if (!entries.isEmpty()) - { - final int max = entries.size(); - int i = 0; - - if (!(lcArgs.length() > 0) && (max > MAX_ENTRIES)) - { - i = max - MAX_ENTRIES; - } - - if (lcArgs.matches("^\\d+(| .*)")) - { - final String[] split = lcArgs.split(" ", 2); - - try - { - i = Integer.parseInt(split[0]); - - if (i > 0) - { - i--; - } - - if (split.length == 2) - { - lcArgs = split[1].trim(); - } - else - { - lcArgs = ""; - } - - if (i > max) - { - i = 0; - } - } - catch (NumberFormatException ignore) - { - ; // Do nothing - } - } - - EntryLink entry; - int sent = 0; - - for (; i < max; i++) - { - entry = entries.get(i); - - if (lcArgs.length() > 0) - { - if ((entry.getLink().toLowerCase().contains(lcArgs)) || - (entry.getTitle().toLowerCase().contains(lcArgs)) || - (entry.getNick().toLowerCase().contains(lcArgs))) - { - if (sent > MAX_ENTRIES) - { - send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), - isPrivate); - - break; - } - - send(sender, Utils.buildLink(i, entry, true), isPrivate); - sent++; - } - } - else - { - if (sent > MAX_ENTRIES) - { - send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), - isPrivate); - - break; - } - - send(sender, Utils.buildLink(i, entry, true), isPrivate); - sent++; - } - } - } - else - { - send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate); - } - } +public class Mobibot extends PircBot { + /** + * The connect/read timeout in ms. + */ + public static final int CONNECT_TIMEOUT = 5000; + + /** + * The empty title string. + */ + static final String NO_TITLE = "No Title"; + + /** + * The default port. + */ + private static final int DEFAULT_PORT = 6667; + + /** + * The info strings. + */ + private static final String[] INFO_STRS = { + ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + " by Erik C. Thauvin (erik@thauvin.net)", + "http://www.mobitopia.org/mobibot/" + }; + + /** + * The link match string. + */ + private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; + + /** + * The default maximum number of entries to display. + */ + private static final int MAX_ENTRIES = 8; + + /** + * The default maximum recap entries. + */ + private static final int MAX_RECAP = 10; + + /** + * The maximum number of times the bot will try to reconnect, if disconnected. + */ + private static final int MAX_RECONNECT = 10; + + /** + * The number of milliseconds to delay between consecutive messages. + */ + private static final long MESSAGE_DELAY = 1000L; + + /** + * The modules. + */ + private static final List<AbstractModule> MODULES = new ArrayList<>(0); + + /** + * The start time. + */ + private static final long START_TIME = System.currentTimeMillis(); + + /** + * The tags/categories marker. + */ + private static final String TAGS_MARKER = "tags:"; + + /** + * The version strings. + */ + private static final String[] VERSION_STRS = { + "Version: " + + ReleaseInfo.getVersion() + + " (" + + Utils.ISO_SDF.format(ReleaseInfo.getBuildDate()) + ')', + "Platform: " + + System.getProperty("os.name") + + " (" + + System.getProperty("os.version") + + ", " + + System.getProperty("os.arch") + + ", " + + System.getProperty("user.country") + ')', + "Runtime: " + + System.getProperty("java.runtime.name") + + " (build " + + System.getProperty("java.runtime.version") + + ')', + "VM: " + + System.getProperty("java.vm.name") + + " (build " + + System.getProperty("java.vm.version") + + ", " + + System.getProperty("java.vm.info") + + ')' + }; + + /** + * The tell object. + */ + private static Tell tell; + + /** + * The main channel. + */ + private final String channel; + + /** + * The commands list. + */ + private final List<String> commandsList = new ArrayList<>(); + + /** + * The entries array. + */ + private final List<EntryLink> entries = new ArrayList<>(0); + + /** + * The history/backlogs array. + */ + private final List<String> history = new ArrayList<>(0); + + /** + * The ignored nicks array. + */ + private final List<String> ignoredNicks = new ArrayList<>(0); + + /** + * The IRC port. + */ + private final int ircPort; + + /** + * The IRC server. + */ + private final String ircServer; + + /** + * The logger. + */ + private final Log4JLogger logger = new Log4JLogger(Mobibot.class.getPackage().getName()); + + /** + * The logger default level. + */ + private final Level loggerLevel; + + /** + * The log directory. + */ + private final String logsDir; + + /** + * The recap array. + */ + private final List<String> recap = new ArrayList<>(0); + + /** + * The backlogs URL. + */ + private String backLogsUrl = ""; + + /** + * The default tags/categories. + */ + private String defaultTags = ""; + + /** + * The del.icio.us posts handler. + */ + private DeliciousPoster delicious = null; + + /** + * The feed URL. + */ + private String feedURL = ""; + + /** + * The ident message. + */ + private String identMsg = ""; + + /** + * The ident nick. + */ + private String identNick = ""; + + /** + * The NickServ ident password. + */ + private String identPwd = ""; + + /** + * Today's date. + */ + private String today = Utils.today(); + + /** + * The weblog URL. + */ + private String weblogUrl = ""; + + /** + * Creates a new {@link Mobibot} instance. + * + * @param server The server. + * @param port The port. + * @param nickname The nickname. + * @param channel The channel. + * @param logsDir The logs directory. + */ + @SuppressWarnings("WeakerAccess") + public Mobibot(final String server, + final int port, + final String nickname, + final String channel, + final String logsDir) { + System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); + System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); + + setName(nickname); + + ircServer = server; + ircPort = port; + this.channel = channel; + this.logsDir = logsDir; + + // Set the logger level + loggerLevel = logger.getLogger().getLevel(); + + // Initialization + Utils.UTC_SDF.setTimeZone(TimeZone.getTimeZone("UTC")); + + // Load the current entries, if any. + try { + today = EntriesMgr.loadEntries(this.logsDir + EntriesMgr.CURRENT_XML, this.channel, entries); + + if (logger.isDebugEnabled()) { + logger.debug("Last feed: " + today); + } + + if (!Utils.today().equals(today)) { + entries.clear(); + today = Utils.today(); + } + } catch (IOException ignore) { + ; // Do nothing. + } catch (FeedException e) { + logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); + } + + // Load the backlogs, if any. + try { + EntriesMgr.loadBacklogs(this.logsDir + EntriesMgr.NAV_XML, history); + } catch (IOException ignore) { + ; // Do nothing. + } catch (FeedException e) { + logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); + } + + // Load the modules + MODULES.add(new Calc()); + MODULES.add(new CurrencyConverter()); + MODULES.add(new Dice()); + MODULES.add(new GoogleSearch()); + MODULES.add(new Joke()); + MODULES.add(new Lookup()); + MODULES.add(new Ping()); + MODULES.add(new StockQuote()); + MODULES.add(new Twitter()); + MODULES.add(new War()); + MODULES.add(new Weather()); + MODULES.add(new WorldTime()); + } + + /** + * The Truth Is Out There... + * + * @param args The command line arguments. + */ + public static void main(final String[] args) { + // Setup the command line options + final Options options = new Options(); + options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); + options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, + "print debug & logging data directly to the console"); + options.addOption(Option.builder( + Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") + .longOpt(Commands.PROPS_ARG).build()); + options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); + + // Parse the command line + final CommandLineParser parser = new DefaultParser(); + CommandLine line = null; + + try { + line = parser.parse(options, args); + } catch (ParseException e) { + System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); + e.printStackTrace(System.err); + System.exit(1); + } + + if (line.hasOption(Commands.HELP_ARG.charAt(0))) { + // Output the usage + new HelpFormatter().printHelp(Mobibot.class.getName(), options); + } else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) { + for (final String s : INFO_STRS) { + System.out.println(s); + } + } else { + final Properties p = new Properties(); + + try (final FileInputStream fis = new FileInputStream( + new File(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + // Load the properties files + p.load(fis); + } catch (FileNotFoundException e) { + System.err.println("Unable to find properties file."); + e.printStackTrace(System.err); + System.exit(1); + } catch (IOException e) { + System.err.println("Unable to open properties file."); + e.printStackTrace(System.err); + System.exit(1); + } + + // Get the main properties + final String channel = p.getProperty("channel"); + final String server = p.getProperty("server"); + final int port = Utils.getIntProperty(p.getProperty("port"), DEFAULT_PORT); + final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase()); + final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); + + if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { + // Redirect the stdout and stderr + PrintStream stdout = null; + + try { + stdout = new PrintStream(new FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + } catch (IOException e) { + System.err.println("Unable to open output (stdout) log file."); + e.printStackTrace(System.err); + System.exit(1); + } + + PrintStream stderr = null; + + try { + stderr = new PrintStream(new FileOutputStream(logsDir + nickname + ".err", true)); + } catch (IOException e) { + System.err.println("Unable to open error (stderr) log file."); + e.printStackTrace(System.err); + System.exit(1); + } + + System.setOut(stdout); + System.setErr(stderr); + } + + // Get the bot's properties + final String login = p.getProperty("login", nickname); + final String weblogURL = p.getProperty("weblog", ""); + final String feedURL = p.getProperty("feed", ""); + final String backlogsURL = Utils.ensureDir(p.getProperty("backlogs", weblogURL), true); + final String ignoredNicks = p.getProperty("ignore", ""); + final String identNick = p.getProperty("ident-nick", ""); + final String identMsg = p.getProperty("ident-msg", ""); + final String identPwd = p.getProperty("ident", ""); + final String tags = p.getProperty("tags", ""); + + // Get the del.icio.us properties + final String dname = p.getProperty("delicious-user"); + final String dpwd = p.getProperty("delicious-pwd"); + + // Create the bot + final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir); + + // Get the tell command settings + tell = new Tell(bot, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); + + // Initialize the bot + bot.setVerbose(true); + bot.setAutoNickChange(true); + bot.setLogin(login); + bot.setVersion(weblogURL); + bot.setMessageDelay(MESSAGE_DELAY); + bot.setIdentity(identPwd, identNick, identMsg); + + // Set the URLs + bot.setWeblogUrl(weblogURL); + bot.setFeedURL(feedURL); + bot.setBacklogsUrl(backlogsURL); + + // Set the del.icio.us authentication + bot.setDeliciousAuth(dname, dpwd); + + // Load the modules properties + MODULES.stream().filter(AbstractModule::hasProperties).forEach( + module -> { + for (final String s : module.getPropertyKeys()) { + module.setProperty(s, p.getProperty(s, "")); + } + }); + + // Set the tags + bot.setTags(tags); + + // Set the ignored nicks + bot.setIgnoredNicks(ignoredNicks); + + // Save the entries + bot.saveEntries(true); + + // Connect + try { + bot.connect(server, port); + } catch (Exception e) { + int retries = 0; + + while ((retries++ < MAX_RECONNECT) && !bot.isConnected()) { + sleep(10); + + try { + bot.connect(server, port); + } catch (Exception ignore) { + if (retries == MAX_RECONNECT) { + System.err.println( + "Unable to connect to " + server + " after " + MAX_RECONNECT + " retries."); + e.printStackTrace(System.err); + System.exit(1); + } + } + } + } + + bot.setVersion(INFO_STRS[0]); + + bot.identify(); + + bot.joinChannel(channel); + } + } + + /** + * Sleeps for the specified number of seconds. + * + * @param secs The number of seconds to sleep for. + */ + private static void sleep(final int secs) { + try { + Thread.sleep((long) (secs * 1000)); + } catch (InterruptedException ignore) { + ; // Do nothing + } + } + + /** + * Sends an action to the current channel. + * + * @param action The action. + */ + final public void action(final String action) { + action(channel, action); + } + + /** + * Sends an action to the channel. + * + * @param channel The channel. + * @param action The action. + */ + private void action(final String channel, final String action) { + if (Utils.isValidString(channel) && Utils.isValidString(action)) { + sendAction(channel, action); + } + } + + /** + * Responds with the title and links from the RSS feed. + * + * @param sender The nick of the person who sent the private message. + */ + private void feedResponse(final String sender) { + if (Utils.isValidString(feedURL)) { + new Thread(new FeedReader(this, sender, feedURL)).start(); + } else { + send(sender, "There is no weblog setup for this channel."); + } + } + + /** + * Returns the index of the specified duplicate entry, if any. + * + * @param link The link. + * @return The index or -1 if none. + */ + private int findDupEntry(final String link) { + synchronized (entries) { + for (int i = 0; i < entries.size(); i++) { + if (link.equals(entries.get(i).getLink())) { + return i; + } + } + } + + return -1; + } + + /** + * Returns the backlogs URL. + * + * @return The backlogs URL. + */ + public final String getBacklogsUrl() { + return this.backLogsUrl; + } + + /** + * Sets the backlogs URL. + * + * @param backLogsUrl The backlogs URL. + */ + private void setBacklogsUrl(final String backLogsUrl) { + this.backLogsUrl = backLogsUrl; + } + + /** + * Returns the current channel. + * + * @return The current channel. + */ + public final String getChannel() { + return channel; + } + + /** + * Returns the irc server. + * + * @return The irc server. + */ + public final String getIrcServer() { + return this.ircServer; + } + + /** + * Returns the bot's logger. + * + * @return The bot's logger. + */ + public final Log4JLogger getLogger() { + return logger; + } + + /** + * Returns the log directory. + * + * @return the log directory. + */ + public final String getLogsDir() { + return this.logsDir; + } + + /** + * Returns the bot's nickname regexp pattern. + * + * @return The nickname regexp pattern. + */ + private String getNickPattern() { + final StringBuilder buff = new StringBuilder(0); + + for (final char c : getNick().toCharArray()) { + if (Character.isLetter(c)) { + buff.append('[') + .append(String.valueOf(c).toLowerCase()) + .append(String.valueOf(c).toUpperCase()) + .append(']'); + } else { + buff.append(c); + } + } + + return buff.toString(); + } + + /** + * Get today's date for the feed. + * + * @return Today's date. + */ + public String getToday() { + return this.today; + } + + /** + * Returns the weblog URL. + * + * @return The weblog URL. + */ + public final String getWeblogUrl() { + return this.weblogUrl; + } + + /** + * Sets the weblog URL. + * + * @param weblogUrl The weblog URL. + */ + private void setWeblogUrl(final String weblogUrl) { + this.weblogUrl = weblogUrl; + } + + /** + * Returns indented and bold help string. + * + * @param help The help string. + * @return The indented help string. + */ + final public String helpIndent(final String help) { + return helpIndent(help, true); + } + + /** + * Returns indented help string. + * + * @param help The help string. + * @param isBold The bold flag. + * @return The indented help string. + */ + public String helpIndent(final String help, final boolean isBold) { + return " " + (isBold ? Utils.bold(help) : help); + } + + /** + * Responds with the bot's help. + * + * @param sender The nick of the person who sent the private message. + * @param topic The help topic, if any. + */ + private void helpResponse(final String sender, final String topic) { + final String lcTopic = topic.toLowerCase().trim(); + + if (lcTopic.equals(Commands.HELP_POSTING_KEYWORD)) { + send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); + send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); + send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + '1')); + send(sender, "To add a title, use a its label and a pipe:"); + send(sender, helpIndent(Commands.LINK_CMD + "1:|This is the title")); + send(sender, "To add a comment: "); + send(sender, helpIndent(Commands.LINK_CMD + "1:This is a comment")); + send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + "1.1")); + send(sender, "To edit a comment, use its label: "); + send(sender, helpIndent(Commands.LINK_CMD + "1.1:This is an edited comment")); + send(sender, "To delete a comment, use its label and a minus sign: "); + send(sender, helpIndent(Commands.LINK_CMD + "1.1:-")); + send(sender, "You can also view a posting by saying its label."); + } else if (lcTopic.equals(Commands.HELP_TAGS_KEYWORD)) { + send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:")); + send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); + } else if (lcTopic.equals(Commands.VIEW_CMD)) { + send(sender, "To list or search the current URL posts:"); + send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); + } else if (lcTopic.equals(channel.substring(1).toLowerCase())) { + send(sender, "To list the last 5 posts from the channel's weblog:"); + send(sender, helpIndent(getNick() + ": " + channel.substring(1))); + } else if (lcTopic.equals(Commands.RECAP_CMD)) { + send(sender, "To list the last 10 public channel messages:"); + send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); + } else if (lcTopic.equals(Commands.USERS_CMD)) { + send(sender, "To list the users present on the channel:"); + send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); + } else if (lcTopic.equals(Commands.INFO_CMD)) { + send(sender, "To view information about the bot:"); + send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); + } else if (lcTopic.equals(Commands.CYCLE_CMD) && isOp(sender)) { + send(sender, "To have the bot leave the channel and come back:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); + } else if (lcTopic.equals(Commands.ME_CMD) && isOp(sender)) { + send(sender, "To have the bot perform an action:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); + } else if (lcTopic.equals(Commands.SAY_CMD) && isOp(sender)) { + send(sender, "To have the bot say something on the channel:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); + } else if (lcTopic.equals(Commands.VERSION_CMD) && isOp(sender)) { + send(sender, "To view the version data (bot, java, etc.):"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); + } else if (lcTopic.equals(Commands.MSG_CMD) && isOp(sender)) { + send(sender, "To have the bot send a private message to someone:"); + send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); + } else if (lcTopic.equals(Commands.IGNORE_CMD)) { + send(sender, "To check your ignore status:"); + send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); + + send(sender, "To toggle your ignore status:"); + send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); + } else if (lcTopic.equals(Tell.TELL_CMD) && tell.isEnabled()) { + tell.helpResponse(sender); + } else { + for (final AbstractModule module : MODULES) { + for (final String cmd : module.getCommands()) { + if (lcTopic.equals(cmd)) { + module.helpResponse(this, sender, topic, true); + return; + } + } + } + + send(sender, Utils.bold("Type a URL on " + channel + " to post it.")); + send(sender, "For more information on a specific command, type:"); + send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); + send(sender, "The commands are:"); + + if (commandsList.isEmpty()) { + commandsList.add(Commands.IGNORE_CMD); + commandsList.add(Commands.INFO_CMD); + commandsList.add(channel.substring(1)); + commandsList.add(Commands.HELP_POSTING_KEYWORD); + commandsList.add(Commands.HELP_TAGS_KEYWORD); + commandsList.add(Commands.RECAP_CMD); + commandsList.add(Commands.USERS_CMD); + commandsList.add(Commands.VIEW_CMD); + + MODULES.stream().filter(AbstractModule::isEnabled).forEach( + module -> commandsList.addAll(module.getCommands())); + + if (tell.isEnabled()) { + commandsList.add(Tell.TELL_CMD); + } + + Collections.sort(commandsList); + } + + final StringBuilder sb = new StringBuilder(0); + + for (int i = 0, cmdCount = 1; i < commandsList.size(); i++, cmdCount++) { + if (sb.length() > 0) { + sb.append(" "); + } + + sb.append(commandsList.get(i)); + + // 6 commands per line or last command + if (sb.length() > 0 && (cmdCount == 6 || i == (commandsList.size() - 1))) { + send(sender, helpIndent(sb.toString())); + + sb.setLength(0); + cmdCount = 0; + } + } + + if (isOp(sender)) { + send(sender, "The op commands are:"); + send(sender, helpIndent( + Commands.CYCLE_CMD + " " + + Commands.ME_CMD + " " + + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + + Commands.VERSION_CMD)); + } + } + } + + /** + * Identifies the bot. + */ + private void identify() { + // Identify with NickServ + if (Utils.isValidString(identPwd)) { + identify(identPwd); + } + + // Identify with a specified nick + if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) { + sendMessage(identNick, identMsg); + } + } + + /** + * Processes the {@link net.thauvin.erik.mobibot.Commands#IGNORE_CMD} command. + * + * @param sender The sender. + * @param args The command arguments. + */ + private void ignoreResponse(final String sender, final String args) { + if (!isOp(sender)) { + final String nick = sender.toLowerCase(); + final boolean isMe = args.toLowerCase().startsWith(Commands.IGNORE_ME_KEYWORD); + + if (ignoredNicks.contains(nick)) { + if (isMe) { + ignoredNicks.remove(nick); + + send(sender, "You are no longer ignored."); + } else { + send(sender, "You are currently ignored."); + } + } else { + if (isMe) { + ignoredNicks.add(nick); + + send(sender, "You are now ignored."); + } else { + send(sender, "You are not currently ignored."); + } + } + } else { + if (args.length() > 0) { + final String[] nicks = args.toLowerCase().split(" "); + + for (final String nick : nicks) { + final String ignore; + + if (Commands.IGNORE_ME_KEYWORD.equals(nick)) { + ignore = sender.toLowerCase(); + } else { + ignore = nick; + } + + if (ignoredNicks.contains(ignore)) { + ignoredNicks.remove(ignore); + } else { + ignoredNicks.add(ignore); + } + } + } + + send(sender, "The following nicks are ignored: " + ignoredNicks.toString()); + } + } + + /** + * Responds with the bot's information. + * + * @param sender The nick of the person who sent the message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + private void infoResponse(final String sender, final boolean isPrivate) { + for (final String info : INFO_STRS) { + if (info.startsWith("http://")) { + send(sender, Utils.green(info), isPrivate); + } else { + send(sender, info, isPrivate); + } + } + + final StringBuilder info = new StringBuilder("Uptime: "); + + long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; + + final long years = timeInSeconds / 31540000L; + + if (years > 0) { + info.append(years).append(Utils.plural(years, " year ", " years ")); + timeInSeconds -= (years * 31540000L); + } + + final long weeks = timeInSeconds / 604800L; + + if (weeks > 0) { + info.append(weeks).append(Utils.plural(weeks, " week ", " weeks ")); + timeInSeconds -= (weeks * 604800L); + } + + final long days = timeInSeconds / 86400L; + + if (days > 0) { + info.append(days).append(Utils.plural(days, " day ", " days ")); + timeInSeconds -= (days * 86400L); + } + + final long hours = timeInSeconds / 3600L; + + if (hours > 0) { + info.append(hours).append(Utils.plural(hours, " hour ", " hours ")); + timeInSeconds -= (hours * 3600L); + } + + final long minutes = timeInSeconds / 60L; + + info.append(minutes).append(Utils.plural(minutes, " minute ", " minutes ")); + + info.append("[Entries: ").append(entries.size()); + + if (tell.isEnabled() && isOp(sender)) { + info.append(", Messages: ").append(tell.size()); + } + + info.append(']'); + + send(sender, info.toString(), isPrivate); + } + + /** + * Determines whether the specified nick should be ignored. + * + * @param nick The nick. + * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. + */ + private boolean isIgnoredNick(final String nick) { + return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); + } + + /** + * Returns <code>true</code> if the specified sender is an Op on the {@link #channel channel}. + * + * @param sender The sender. + * @return true, if the sender is an Op. + */ + public boolean isOp(final String sender) { + final User[] users = getUsers(channel); + + for (final User user : users) { + if (user.getNick().equals(sender)) { + return user.isOp(); + } + } + + return false; + } + + @Override + protected final void onAction(final String sender, + final String login, + final String hostname, + final String target, + final String action) { + if (target.equals(channel)) { + storeRecap(sender, action, true); + } + } + + @Override + protected final void onDisconnect() { + if (Utils.isValidString(weblogUrl)) { + setVersion(weblogUrl); + } + + sleep(5); + + // Connect + try { + connect(ircServer, ircPort); + } catch (Exception e) { + int retries = 0; + + while ((retries++ < MAX_RECONNECT) && !isConnected()) { + sleep(10); + + try { + connect(ircServer, ircPort); + } catch (Exception ex) { + if (retries == MAX_RECONNECT) { + if (logger.isDebugEnabled()) { + logger.debug( + "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", + ex); + } + + e.printStackTrace(System.err); + System.exit(1); + } + } + } + } + + setVersion(INFO_STRS[0]); + + identify(); + + joinChannel(channel); + } + + @Override + protected void onJoin(final String channel, final String sender, final String login, final String hostname) { + tell.send(sender); + } + + @Override + protected final void onMessage(final String channel, + final String sender, + final String login, + final String hostname, + final String message) { + if (logger.isDebugEnabled()) { + logger.debug(">>> " + sender + ": " + message); + } + + boolean isCommand = false; + + // Capture URLs posted on the channel + if (message.matches(LINK_MATCH) && !isIgnoredNick(sender)) { + isCommand = true; + + final String[] cmds = message.split(" ", 2); + + if (cmds.length == 1 || (!cmds[1].contains(getNick()))) { + final String link = cmds[0].trim(); + boolean isBackup = false; + + final int dupIndex = findDupEntry(link); + + if (dupIndex == -1) { + if (!Utils.today().equals(today)) { + isBackup = true; + saveEntries(true); + + entries.clear(); + today = Utils.today(); + } + + final StringBuilder tags = new StringBuilder(defaultTags); + String title = NO_TITLE; + + if (cmds.length == 2) { + final String[] data = cmds[1].trim().split(TAGS_MARKER, 2); + + if (data.length == 1) { + title = data[0].trim(); + } else { + if (Utils.isValidString(data[0])) { + title = data[0].trim(); + } + + tags.append(' ').append(data[1].trim()); + } + } + + if (NO_TITLE.equals(title)) { + try { + final Document html = Jsoup.connect(link).userAgent("Mozilla").get(); + final String htmlTitle = html.title(); + + if (Utils.isValidString(htmlTitle)) { + title = htmlTitle; + } + } catch (IOException ignore) { + // Do nothing + } + } + + entries.add(new EntryLink(link, title, sender, login, channel, tags.toString())); + + final int index = entries.size() - 1; + final EntryLink entry = entries.get(index); + send(channel, Utils.buildLink(index, entry)); + + if (delicious != null) { + delicious.addPost(entry); + } + + saveEntries(isBackup); + + if (NO_TITLE.equals(entry.getTitle())) { + send(sender, "Please specify a title, by typing:", true); + send(sender, helpIndent(Commands.LINK_CMD + (index + 1) + ":|This is the title"), true); + } + } else { + final EntryLink entry = entries.get(dupIndex); + send(sender, "Duplicate >> " + Utils.buildLink(dupIndex, entry)); + } + } + } + // mobibot: <command> + else if (message.matches(getNickPattern() + ":.*")) { + isCommand = true; + + final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); + final String cmd = cmds[0].toLowerCase(); + + String args = ""; + + if (cmds.length > 1) { + args = cmds[1].trim(); + } + + // mobibot: help + if (cmd.startsWith(Commands.HELP_CMD)) { + helpResponse(sender, args); + } + // mobibot: recap + else if (cmd.equals(Commands.RECAP_CMD)) { + recapResponse(sender, false); + } + // mobibot: users + else if (cmd.equals(Commands.USERS_CMD)) { + usersResponse(sender, false); + } + // mobibot: info + else if (cmd.equals(Commands.INFO_CMD)) { + infoResponse(sender, false); + } + // mobbiot: version + else if (cmd.equals(Commands.VERSION_CMD)) { + versionResponse(sender, false); + } + // mobibot: <channel> + else if (cmd.equalsIgnoreCase(channel.substring(1))) { + feedResponse(sender); + } + // mobibot: view + else if (cmd.startsWith(Commands.VIEW_CMD)) { + viewResponse(sender, args, false); + } + // mobibot: tell + else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) { + tell.response(sender, args); + } + // mobibot: ignore + else if (cmd.startsWith(Commands.IGNORE_CMD)) { + ignoreResponse(sender, args); + } + // modules + else { + for (final AbstractModule module : MODULES) { + for (final String c : module.getCommands()) { + if (cmd.startsWith(c)) { + module.commandResponse(this, sender, args, false); + } + } + } + } + } + // L1:<comment>, L1:-, L1:|<title>, etc. + else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { + isCommand = true; + + final String[] cmds = message.substring(1).split(":", 2); + final int index = Integer.parseInt(cmds[0]) - 1; + + // L1:<comment> + if (index < entries.size()) { + final String cmd = cmds[1].trim(); + + if (cmd.length() == 0) { + final EntryLink entry = entries.get(index); + send(channel, Utils.buildLink(index, entry)); + + if (entry.hasTags()) { + send(channel, Utils.buildTags(index, entry)); + } + + if (entry.hasComments()) { + final EntryComment[] comments = entry.getComments(); + + for (int i = 0; i < comments.length; i++) { + send(channel, Utils.buildComment(index, i, comments[i])); + } + } + } else { + // L1:- + if ("-".equals(cmd)) { + final EntryLink entry = entries.get(index); + + if (entry.getLogin().equals(login) || isOp(sender)) { + if (delicious != null) { + delicious.deletePost(entry); + } + + entries.remove(index); + send(channel, "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); + saveEntries(false); + } else { + send(sender, "Please ask a channel op to remove this entry for you."); + } + } + // L1:|<title> + else if (cmd.charAt(0) == '|') { + if (cmd.length() > 1) { + final EntryLink entry = entries.get(index); + entry.setTitle(cmd.substring(1).trim()); + + if (delicious != null) { + delicious.updatePost(entry.getLink(), entry); + } + + send(channel, Utils.buildLink(index, entry)); + saveEntries(false); + } + } + // L1:=<url> + else if (cmd.charAt(0) == '=') { + final EntryLink entry = entries.get(index); + + if (entry.getLogin().equals(login) || isOp(sender)) { + final String link = cmd.substring(1); + + if (link.matches(LINK_MATCH)) { + final String oldLink = entry.getLink(); + + entry.setLink(link); + + if (delicious != null) { + delicious.updatePost(oldLink, entry); + } + + send(channel, Utils.buildLink(index, entry)); + saveEntries(false); + } + } else { + send(sender, "Please ask a channel op to change this link for you."); + } + } + // L1:?<author> + else if (cmd.charAt(0) == '?') { + if (isOp(sender)) { + if (cmd.length() > 1) { + final EntryLink entry = entries.get(index); + entry.setNick(cmd.substring(1)); + send(channel, Utils.buildLink(index, entry)); + saveEntries(false); + } + } else { + send(sender, "Please ask a channel op to change the author of this link for you."); + } + } else { + final EntryLink entry = entries.get(index); + final int cindex = entry.addComment(cmd, sender); + + final EntryComment comment = entry.getComment(cindex); + send(sender, Utils.buildComment(index, cindex, comment)); + saveEntries(false); + } + } + } + } + // L1T:<+-tag> + else if (message.matches(Commands.LINK_CMD + "[0-9]+T:.*")) { + isCommand = true; + + final String[] cmds = message.substring(1).split("T:", 2); + final int index = Integer.parseInt(cmds[0]) - 1; + + if (index < entries.size()) { + final String cmd = cmds[1].trim(); + + final EntryLink entry = entries.get(index); + + if (cmd.length() != 0) { + if (entry.getLogin().equals(login) || isOp(sender)) { + entry.setTags(cmd); + + if (delicious != null) { + delicious.updatePost(entry.getLink(), entry); + } + + send(channel, Utils.buildTags(index, entry)); + saveEntries(false); + } else { + send(sender, "Please ask a channel op to change the tags for you."); + } + } else { + if (entry.hasTags()) { + send(channel, Utils.buildTags(index, entry)); + } else { + send(sender, "The entry has no tags. Why don't add some?"); + } + } + } + } + // L1.1:<command> + else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { + isCommand = true; + + final String[] cmds = message.substring(1).split("[.:]", 3); + final int index = Integer.parseInt(cmds[0]) - 1; + + if (index < entries.size()) { + final EntryLink entry = entries.get(index); + final int cindex = Integer.parseInt(cmds[1]) - 1; + + if (cindex < entry.getCommentsCount()) { + final String cmd = cmds[2].trim(); + + // L1.1: + if (cmd.length() == 0) { + final EntryComment comment = entry.getComment(cindex); + send(channel, Utils.buildComment(index, cindex, comment)); + } + // L1.1:- + else if ("-".equals(cmd)) { + entry.deleteComment(cindex); + send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); + saveEntries(false); + } + // L1.1:?<author> + else if (cmd.charAt(0) == '?') { + if (isOp(sender)) { + if (cmd.length() > 1) { + final EntryComment comment = entry.getComment(cindex); + comment.setNick(cmd.substring(1)); + send(channel, Utils.buildComment(index, cindex, comment)); + saveEntries(false); + } + } else { + send(sender, "Please ask a channel op to change the author of this comment for you."); + } + } else { + entry.setComment(cindex, cmd, sender); + + final EntryComment comment = entry.getComment(cindex); + send(sender, Utils.buildComment(index, cindex, comment)); + saveEntries(false); + } + } + } + } + + if (!isCommand) { + storeRecap(sender, message, false); + } + + tell.send(sender, true); + } + + @Override + protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { + tell.send(newNick); + } + + @Override + protected final void onPrivateMessage(final String sender, + final String login, + final String hostname, + final String message) { + if (logger.isDebugEnabled()) { + logger.debug(">>> " + sender + ": " + message); + } + + final String[] cmds = message.split(" ", 2); + final String cmd = cmds[0].toLowerCase(); + String args = ""; + + if (cmds.length > 1) { + args = cmds[1].trim(); + } + + if (cmd.startsWith(Commands.HELP_CMD)) { + helpResponse(sender, args); + } else if ("kill".equals(cmd) && isOp(sender)) { + sendRawLine("QUIT : Poof!"); + System.exit(0); + } else if (cmd.equals(Commands.DIE_CMD) && isOp(sender)) { + send(channel, sender + " has just signed my death sentence."); + saveEntries(true); + sleep(3); + quitServer("The Bot Is Out There!"); + System.exit(0); + } else if (cmd.equals(Commands.CYCLE_CMD)) { + send(channel, sender + " has just asked me to leave. I'll be back!"); + sleep(0); + partChannel(channel); + sleep(10); + joinChannel(channel); + } else if (cmd.equals(Commands.RECAP_CMD)) { + recapResponse(sender, true); + } else if (cmd.equals(Commands.USERS_CMD)) { + usersResponse(sender, true); + } else if (cmd.equals(Commands.ADDLOG_CMD) && (cmds.length > 1) && isOp(sender)) { + // e.g. 2014-04-01 + final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); + if (backlog.exists()) { + history.add(0, args); + send(sender, history.toString(), true); + } else { + send(sender, "The specified log could not be found."); + } + } else if (cmd.equals(Commands.ME_CMD) && isOp(sender)) { + if (args.length() > 1) { + action(args); + } else { + helpResponse(sender, Commands.ME_CMD); + } + } else if (cmd.equals(Commands.NICK_CMD) && (cmds.length > 1)) { + if (isOp(sender)) { + changeNick(args); + } + } else if (cmd.equals(Commands.SAY_CMD) && isOp(sender)) { + if (cmds.length > 1) { + send(channel, args, true); + } else { + helpResponse(sender, Commands.SAY_CMD); + } + } else if (cmd.equals(Commands.MSG_CMD) && isOp(sender)) { + if (cmds.length > 1) { + final String[] msg = args.split(" ", 2); + + if (args.length() > 2) { + send(msg[0], msg[1], true); + } else { + helpResponse(sender, Commands.MSG_CMD); + } + } else { + helpResponse(sender, Commands.MSG_CMD); + } + } else if (cmd.equals(Commands.VIEW_CMD)) { + viewResponse(sender, args, true); + } else if (cmd.equals(Tell.TELL_CMD) && tell.isEnabled()) { + tell.response(sender, args); + } else if (cmd.equals(Commands.INFO_CMD)) { + infoResponse(sender, true); + } else if (cmd.equals(Commands.VERSION_CMD)) { + versionResponse(sender, true); + } else if (cmd.equals(Commands.DEBUG_CMD) && isOp(sender)) { + if (logger.isDebugEnabled()) { + logger.getLogger().setLevel(loggerLevel); + } else { + logger.getLogger().setLevel(Level.DEBUG); + } + + send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); + } else { + for (final AbstractModule module : MODULES) { + if (module.isPrivateMsgEnabled()) { + for (final String c : module.getCommands()) { + if (cmd.equals(c)) { + module.commandResponse(this, sender, args, true); + return; + } + } + } + } + + helpResponse(sender, ""); + } + } + + /** + * Responds with the last 10 public messages. + * + * @param sender The nick of the person who sent the private message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + private void recapResponse(final String sender, final boolean isPrivate) { + for (final String recap : this.recap) { + send(sender, recap, isPrivate); + } + } + + /** + * Saves the entries. + * + * @param isDayBackup Set the <code>true</code> if the daily backup file should also be created. + */ + private void saveEntries(final boolean isDayBackup) { + EntriesMgr.saveEntries(this, entries, history, isDayBackup); + } + + /** + * Sends a private message or notice. + * + * @param sender The nick of the person who sent the message. + * @param message The actual message. + * @param isPrivate Set to <code>true</code> if the response should be a private message, otherwise a notice is + * sent. + */ + public final void send(final String sender, final String message, final boolean isPrivate) { + if (Utils.isValidString(message) && Utils.isValidString(sender)) { + if (isPrivate) { + if (logger.isDebugEnabled()) { + logger.debug("Sending message to " + sender + ": " + message); + } + + sendMessage(sender, message); + } else { + if (logger.isDebugEnabled()) { + logger.debug("Sending notice to " + sender + ": " + message); + } + + sendNotice(sender, message); + } + } + } + + /** + * Sends a private notice. + * + * @param sender The nick of the person who sent the message. + * @param message The actual message. + */ + public final void send(final String sender, final String message) { + send(sender, message, false); + } + + /** + * Sets the del.icio.us authentication. + * + * @param username The del.icio.us user name. + * @param password The del.icio.us password. + */ + private void setDeliciousAuth(final String username, final String password) { + if (Utils.isValidString(username) && Utils.isValidString(password)) { + delicious = new DeliciousPoster(username, password, ircServer); + } + } + + /** + * Sets the feed URL. + * + * @param feedURL The feed URL. + */ + private void setFeedURL(final String feedURL) { + this.feedURL = feedURL; + } + + /** + * Sets the bot's identification. + * + * @param identPwd The password for NickServ, if any. + * @param identNick The ident nick name. + * @param identMsg The ident message. + */ + private void setIdentity(final String identPwd, final String identNick, final String identMsg) { + this.identPwd = identPwd; + this.identNick = identNick; + this.identMsg = identMsg; + } + + /** + * Sets the Ignored nicks. + * + * @param nicks The nicks to ignore + */ + private void setIgnoredNicks(final String nicks) { + if (Utils.isValidString(nicks)) { + final StringTokenizer st = new StringTokenizer(nicks, ","); + + while (st.hasMoreTokens()) { + ignoredNicks.add(st.nextToken().trim().toLowerCase()); + } + } + } + + /** + * Sets the default tags/categories. + * + * @param tags The tags. + */ + private void setTags(final String tags) { + defaultTags = tags; + } + + /** + * Stores the last 10 public messages and actions. + * + * @param sender The nick of the person who sent the private message. + * @param message The actual message sent. + * @param isAction Set to <code>true</code> if the message is an action. + */ + private void storeRecap(final String sender, final String message, final boolean isAction) { + recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") + + message); + + if (recap.size() > MAX_RECAP) { + recap.remove(0); + } + } + + /** + * Responds with the users on a channel. + * + * @param sender The nick of the person who sent the message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + private void usersResponse(final String sender, final boolean isPrivate) { + final User[] users = getUsers(channel); + final String[] nicks = new String[users.length]; + + for (int i = 0; i < users.length; i++) { + nicks[i] = users[i].getNick(); + } + + Arrays.sort(nicks, String.CASE_INSENSITIVE_ORDER); + + final StringBuilder buff = new StringBuilder(0); + + for (final String nick : nicks) { + if (isOp(nick)) { + buff.append('@'); + } + + buff.append(nick).append(' '); + } + + send(sender, buff.toString(), isPrivate); + } + + /** + * Responds with the bot's version info. + * + * @param sender The nick of the person who sent the message. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + private void versionResponse(final String sender, final boolean isPrivate) { + if (isOp(sender)) { + for (final String version : VERSION_STRS) { + send(sender, version, isPrivate); + } + } + } + + /** + * Responds with the stored links. + * + * @param sender The nick of the person who sent the message. + * @param args The view command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + private void viewResponse(final String sender, final String args, final boolean isPrivate) { + String lcArgs = args.toLowerCase(); + + if (!entries.isEmpty()) { + final int max = entries.size(); + int i = 0; + + if (!(lcArgs.length() > 0) && (max > MAX_ENTRIES)) { + i = max - MAX_ENTRIES; + } + + if (lcArgs.matches("^\\d+(| .*)")) { + final String[] split = lcArgs.split(" ", 2); + + try { + i = Integer.parseInt(split[0]); + + if (i > 0) { + i--; + } + + if (split.length == 2) { + lcArgs = split[1].trim(); + } else { + lcArgs = ""; + } + + if (i > max) { + i = 0; + } + } catch (NumberFormatException ignore) { + ; // Do nothing + } + } + + EntryLink entry; + int sent = 0; + + for (; i < max; i++) { + entry = entries.get(i); + + if (lcArgs.length() > 0) { + if ((entry.getLink().toLowerCase().contains(lcArgs)) || + (entry.getTitle().toLowerCase().contains(lcArgs)) || + (entry.getNick().toLowerCase().contains(lcArgs))) { + if (sent > MAX_ENTRIES) { + send(sender, + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), + isPrivate); + + break; + } + + send(sender, Utils.buildLink(i, entry, true), isPrivate); + sent++; + } + } else { + if (sent > MAX_ENTRIES) { + send(sender, + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), + isPrivate); + + break; + } + + send(sender, Utils.buildLink(i, entry, true), isPrivate); + sent++; + } + } + } else { + send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate); + } + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index dff6317..fd8858c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -41,389 +41,317 @@ import java.util.concurrent.CopyOnWriteArrayList; * @created 2016-07-02 * @since 1.0 */ -public class Tell -{ - /** - * The all keyword. - */ - public static final String TELL_ALL_KEYWORD = "all"; +public class Tell { + /** + * The all keyword. + */ + public static final String TELL_ALL_KEYWORD = "all"; - /** - * The tell command. - */ - public static final String TELL_CMD = "tell"; + /** + * The tell command. + */ + public static final String TELL_CMD = "tell"; - /** - * The delete command. - */ - public static final String TELL_DEL_KEYWORD = "del"; + /** + * The delete command. + */ + public static final String TELL_DEL_KEYWORD = "del"; - /** - * The default maximum number of days to keep messages. - */ + /** + * The default maximum number of days to keep messages. + */ + private static final int DEFAULT_TELL_MAX_DAYS = 7; - private static final int DEFAULT_TELL_MAX_DAYS = 7; + /** + * The default message max queue size. + */ + private static final int DEFAULT_TELL_MAX_SIZE = 50; - /** - * The default message max queue size. - */ - private static final int DEFAULT_TELL_MAX_SIZE = 50; + /** + * The serialized object file extension. + */ + private static final String SER_EXT = ".ser"; - /** - * The serialized object file extension. - */ - private static final String SER_EXT = ".ser"; + /** + * The bot instance. + */ + final private Mobibot bot; - /** - * The bot instance. - */ - final private Mobibot bot; + /** + * The maximum number of days to keep messages. + */ + final private int maxDays; - /** - * The maximum number of days to keep messages. - */ - final private int maxDays; + /** + * The message maximum queue size. + */ + final private int maxSize; - /** - * The message maximum queue size. - */ - final private int maxSize; + /** + * The messages queue. + */ + private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); - /** - * The messages queue. - */ - private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); + /** + * The serialized object file. + */ + private final String serializedObject; - /** - * The serialized object file. - */ - private final String serializedObject; + public Tell(final Mobibot bot, final String maxDays, final String maxSize) { + this.bot = bot; + this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS); + this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE); - public Tell(final Mobibot bot, final String maxDays, final String maxSize) - { - this.bot = bot; - this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS); - this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE); + // Load the message queue. + serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT; + messages.addAll(TellMessagesMgr.load(serializedObject, bot.getLogger())); - // Load the message queue. - serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT; - messages.addAll(TellMessagesMgr.load(serializedObject, bot.getLogger())); + if (clean()) { + save(); + } + } - if (clean()) - { - save(); - } - } + /** + * Cleans the messages queue. + * + * @return <code>True</code> if the queue was cleaned. + */ + private boolean clean() { + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Cleaning the messages."); + } - /** - * Cleans the messages queue. - * - * @return <code>True</code> if the queue was cleaned. - */ - private boolean clean() - { - if (bot.getLogger().isDebugEnabled()) - { - bot.getLogger().debug("Cleaning the messages."); - } + return TellMessagesMgr.clean(messages, maxDays); + } - return TellMessagesMgr.clean(messages, maxDays); - } + /** + * Responds with help. + * + * @param sender The sender. + */ + public void helpResponse(final String sender) { + bot.send(sender, "To send a message to someone when they join the channel:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>")); - /** - * Saves the messages queue. - */ - private void save() - { - TellMessagesMgr.save(serializedObject, messages, bot.getLogger()); - } + bot.send(sender, "To view queued and sent messages:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD)); - /** - * Processes the commands. - * - * @param sender The sender's nick. - * @param cmds The commands string. - */ - public void response(final String sender, final String cmds) - { - if (!Utils.isValidString(cmds)) - { - helpResponse(sender); - } - else if (cmds.startsWith(Commands.VIEW_CMD)) - { - if (bot.isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD)) - { - if (messages.size() > 0) - { - for (final TellMessage message : messages) - { - bot.send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") - + ']', - true); - } - } - else - { - bot.send(sender, "There are no messages in the queue.", true); - } - } - else - { - boolean hasMessage = false; + bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); + } - for (final TellMessage message : messages) - { - if (message.isMatch(sender)) - { - if (!hasMessage) - { - hasMessage = true; - bot.send(sender, "Here are your messages: ", true); - } + /** + * Returns <code>true</code> if enabled. + * + * @return <code>true</code> or <code>false</code> + */ + public boolean isEnabled() { + return maxSize > 0 && maxDays > 0; + } - if (message.isReceived()) - { - bot.send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId() - + ", DELIVERED]", - true); + /** + * Processes the commands. + * + * @param sender The sender's nick. + * @param cmds The commands string. + */ + public void response(final String sender, final String cmds) { + if (!Utils.isValidString(cmds)) { + helpResponse(sender); + } else if (cmds.startsWith(Commands.VIEW_CMD)) { + if (bot.isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD)) { + if (messages.size() > 0) { + for (final TellMessage message : messages) { + bot.send(sender, Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [ID: " + message.getId() + ", " + + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', + true); + } + } else { + bot.send(sender, "There are no messages in the queue.", true); + } + } else { + boolean hasMessage = false; - } - else - { - bot.send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() - + ", QUEUED]", - true); - } + for (final TellMessage message : messages) { + if (message.isMatch(sender)) { + if (!hasMessage) { + hasMessage = true; + bot.send(sender, "Here are your messages: ", true); + } - bot.send(sender, bot.helpIndent(message.getMessage(), false), true); - } - } + if (message.isReceived()) { + bot.send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + + message.getId() + ", DELIVERED]", + true); - if (!hasMessage) - { - bot.send(sender, "You have no messages in the queue.", true); - } - else - { - bot.send(sender, "To delete one or all delivered messages:"); - bot.send(sender, - bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" - + TELL_ALL_KEYWORD + '>')); - bot.send(sender, - "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); - } - } - } - else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) - { - final String[] split = cmds.split(" "); + } else { + bot.send(sender, + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + + message.getId() + ", QUEUED]", + true); + } - if (split.length == 2) - { - final String id = split[1]; - boolean deleted = false; + bot.send(sender, bot.helpIndent(message.getMessage(), false), true); + } + } - if (id.equalsIgnoreCase(TELL_ALL_KEYWORD)) - { - for (final TellMessage message : messages) - { - if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) - { - messages.remove(message); - deleted = true; - } - } + if (!hasMessage) { + bot.send(sender, "You have no messages in the queue.", true); + } else { + bot.send(sender, "To delete one or all delivered messages:"); + bot.send(sender, + bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + + TELL_ALL_KEYWORD + '>')); + bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + + Utils.plural(maxDays, " day.", " days.")); + } + } + } else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) { + final String[] split = cmds.split(" "); - if (deleted) - { - save(); - bot.send(sender, "Delivered messages have been deleted.", true); - } - else - { - bot.send(sender, "No delivered messages were found.", true); - } + if (split.length == 2) { + final String id = split[1]; + boolean deleted = false; - } - else - { - boolean found = false; + if (id.equalsIgnoreCase(TELL_ALL_KEYWORD)) { + for (final TellMessage message : messages) { + if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) { + messages.remove(message); + deleted = true; + } + } - for (final TellMessage message : messages) - { - found = message.isMatchId(id); + if (deleted) { + save(); + bot.send(sender, "Delivered messages have been deleted.", true); + } else { + bot.send(sender, "No delivered messages were found.", true); + } - if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender))) - { - messages.remove(message); + } else { + boolean found = false; - save(); - bot.send(sender, "Your message was deleted from the queue.", true); - deleted = true; - break; - } - } + for (final TellMessage message : messages) { + found = message.isMatchId(id); - if (!deleted) - { - if (found) - { - bot.send(sender, "Only messages that you sent can be deleted.", true); - } - else - { - bot.send(sender, "The specified message [ID " + id + "] could not be found.", true); - } - } - } - } - else - { - helpResponse(sender); - } - } - else - { - final String[] split = cmds.split(" ", 2); + if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender))) { + messages.remove(message); - if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" "))) - { - if (messages.size() < maxSize) - { - final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); + save(); + bot.send(sender, "Your message was deleted from the queue.", true); + deleted = true; + break; + } + } - messages.add(message); + if (!deleted) { + if (found) { + bot.send(sender, "Only messages that you sent can be deleted.", true); + } else { + bot.send(sender, "The specified message [ID " + id + "] could not be found.", true); + } + } + } + } else { + helpResponse(sender); + } + } else { + final String[] split = cmds.split(" ", 2); - save(); + if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" "))) { + if (messages.size() < maxSize) { + final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); - bot.send(sender, - "Message [ID " + message.getId() + "] was queued for " + Utils - .bold(message.getRecipient()), true); - } - else - { - bot.send(sender, "Sorry, the messages queue is currently full.", true); - } - } - else - { - helpResponse(sender); - } - } + messages.add(message); - if (clean()) - { - save(); - } - } + save(); - /** - * Responds with help. - * - * @param sender The sender. - */ - public void helpResponse(final String sender) - { - bot.send(sender, "To send a message to someone when they join the channel:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>")); + bot.send(sender, "Message [ID " + message.getId() + "] was queued for " + + Utils.bold(message.getRecipient()), true); + } else { + bot.send(sender, "Sorry, the messages queue is currently full.", true); + } + } else { + helpResponse(sender); + } + } - bot.send(sender, "To view queued and sent messages:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD)); + if (clean()) save(); + } - bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); - } + /** + * Saves the messages queue. + */ + private void save() { + TellMessagesMgr.save(serializedObject, messages, bot.getLogger()); + } - /** - * Checks and sends messages. - * - * @param nickname The user's nickname. - */ - public void send(final String nickname) - { - send(nickname, false); - } + /** + * Checks and sends messages. + * + * @param nickname The user's nickname. + * @param isMessage The message flag. + */ + public void send(final String nickname, final boolean isMessage) { + if (!nickname.equals(bot.getNick()) && isEnabled()) { + messages.stream().filter(message -> message.isMatch(nickname)).forEach( + message -> { + if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { + if (message.getSender().equals(nickname)) { + if (!isMessage) { + bot.send(nickname, Utils.bold("You") + " wanted me to remind you: " + + Utils.reverseColor(message.getMessage()), + true); - /** - * Checks and sends messages. - * - * @param nickname The user's nickname. - * @param isMessage The message flag. - */ - public void send(final String nickname, final boolean isMessage) - { - if (!nickname.equals(bot.getNick()) && isEnabled()) - { - messages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> { - if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) - { - if (message.getSender().equals(nickname)) - { - if (!isMessage) - { - bot.send(nickname, - Utils.bold("You") + " wanted me to remind you: " + Utils - .reverseColor(message.getMessage()), - true); + message.setIsReceived(); + message.setIsNotified(); - message.setIsReceived(); - message.setIsNotified(); + save(); + } + } else { + bot.send(nickname, message.getSender() + " wanted me to tell you: " + + Utils.reverseColor(message.getMessage()), + true); - save(); - } - } - else - { - bot.send(nickname, - message.getSender() + " wanted me to tell you: " + Utils - .reverseColor(message.getMessage()), - true); + message.setIsReceived(); - message.setIsReceived(); + save(); + } + } else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() + && !message.isNotified()) { + bot.send(nickname, + "Your message " + + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + + Utils.bold(message.getRecipient()) + " on " + + Utils.UTC_SDF.format(message.getReceived()), + true); - save(); - } - } - else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message - .isNotified()) - { - bot.send(nickname, - "Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " - + Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF - .format(message.getReceived()), - true); + message.setIsNotified(); - message.setIsNotified(); + save(); + } + }); + } + } - save(); - } - }); - } - } + /** + * Checks and sends messages. + * + * @param nickname The user's nickname. + */ + public void send(final String nickname) { + send(nickname, false); + } - /** - * Returns <code>true</code> if enabled. - * - * @return <code>true</code> or <code>false</code> - */ - public boolean isEnabled() - { - return maxSize > 0 && maxDays > 0; - } - - /** - * Returns the messages queue size. - * - * @return The size. - */ - public int size() - { - return messages.size(); - } + /** + * Returns the messages queue size. + * + * @return The size. + */ + public int size() { + return messages.size(); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 1d20b77..dc64bc0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -42,162 +42,138 @@ import java.util.Date; * @created 2014-04-24 * @since 1.0 */ -public class TellMessage implements Serializable -{ - private static final long serialVersionUID = 1L; +public class TellMessage implements Serializable { + private static final long serialVersionUID = 1L; + private final String id; + private final String message; + final private Date queued; + private final String recipient; + private final String sender; + private boolean isNotified; + private boolean isReceived; + private Date received; - private final String id; + /** + * Create a new message. + * + * @param sender The sender's nick. + * @param recipient The recipient's nick. + * @param message The message. + */ + public TellMessage(final String sender, final String recipient, final String message) { + this.sender = sender; + this.recipient = recipient; + this.message = message; - private final String message; + this.queued = Calendar.getInstance().getTime(); + this.id = Utils.TIMESTAMP_SDF.format(this.queued); - final private Date queued; + } - private final String recipient; + /** + * Returns the message id. + * + * @return The message id. + */ + public String getId() { + return id; + } - private final String sender; + /** + * Returns the message text. + * + * @return The text of the message. + */ + public String getMessage() { + return message; + } - private boolean isNotified; + /** + * Returns the state of the queue flag. + * + * @return <code>true</code> if the message is queued. + */ + public Date getQueued() { + return queued; + } - private boolean isReceived; + /** + * Returns the state of the received flag. + * + * @return <code>true</code> if the message has been received. + */ + public Date getReceived() { + return received; + } - private Date received; + /** + * Returns the message's recipient. + * + * @return The recipient of the message. + */ + public String getRecipient() { + return recipient; + } - /** - * Create a new message. - * - * @param sender The sender's nick. - * @param recipient The recipient's nick. - * @param message The message. - */ - public TellMessage(final String sender, final String recipient, final String message) - { - this.sender = sender; - this.recipient = recipient; - this.message = message; + /** + * Returns the message's sender. + * + * @return The sender of the message. + */ + public String getSender() { + return sender; + } - this.queued = Calendar.getInstance().getTime(); - this.id = Utils.TIMESTAMP_SDF.format(this.queued); + /** + * Matches the message sender or recipient. + * + * @param nick The nickname to match with. + * @return <code>true</code> if the nickname matches. + */ + public boolean isMatch(final String nick) { + return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); + } - } + /** + * Match the message ID. + * + * @param id The ID to match with. + * @return <code>true</code> if the id matches. + */ + public boolean isMatchId(final String id) { + return this.id.equals(id); + } - /** - * Returns the message id. - * - * @return The message id. - */ - public String getId() - { - return id; - } + /** + * Returns the notification flag state. + * + * @return <code>true</code> if the sender has been notified. + */ + public boolean isNotified() { + return isNotified; + } - /** - * Returns the message text. - * - * @return The text of the message. - */ - public String getMessage() - { - return message; - } + /** + * Returns the received flag state. + * + * @return <code>true</code> if the message was received. + */ + public boolean isReceived() { + return isReceived; + } - /** - * Returns the state of the queue flag. - * - * @return <code>true</code> if the message is queued. - */ - public Date getQueued() - { - return queued; - } + /** + * Sets the notified flag. + */ + public void setIsNotified() { + isNotified = true; + } - /** - * Returns the state of the received flag. - * - * @return <code>true</code> if the message has been received. - */ - public Date getReceived() - { - return received; - } - - /** - * Returns the message's recipient. - * - * @return The recipient of the message. - */ - public String getRecipient() - { - return recipient; - } - - /** - * Returns the message's sender. - * - * @return The sender of the message. - */ - public String getSender() - { - return sender; - } - - /** - * Matches the message sender or recipient. - * - * @param nick The nickname to match with. - * - * @return <code>true</code> if the nickname matches. - */ - public boolean isMatch(final String nick) - { - return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); - } - - /** - * Match the message ID. - * - * @param id The ID to match with. - * - * @return <code>true</code> if the id matches. - */ - public boolean isMatchId(final String id) - { - return this.id.equals(id); - } - - /** - * Returns the notification flag state. - * - * @return <code>true</code> if the sender has been notified. - */ - public boolean isNotified() - { - return isNotified; - } - - /** - * Returns the received flag state. - * - * @return <code>true</code> if the message was received. - */ - public boolean isReceived() - { - return isReceived; - } - - /** - * Sets the notified flag. - */ - public void setIsNotified() - { - isNotified = true; - } - - /** - * Sets the received flag. - */ - public void setIsReceived() - { - received = Calendar.getInstance().getTime(); - isReceived = true; - } + /** + * Sets the received flag. + */ + public void setIsReceived() { + received = Calendar.getInstance().getTime(); + isReceived = true; + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 1137f0b..37e0346 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -46,113 +46,90 @@ import java.util.List; * @created 2014-04-26 * @since 1.0 */ -final class TellMessagesMgr -{ - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private TellMessagesMgr() - throws UnsupportedOperationException - { - throw new UnsupportedOperationException("Illegal constructor call."); - } +final class TellMessagesMgr { + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private TellMessagesMgr() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } - /** - * Cleans the messages queue - * - * @param tellMessages The messages list. - * @param tellMaxDays The maximum number of days to keep messages for. - * - * @return <code>True</code> if the queue was cleaned. - */ - public static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) - { - final Calendar maxDate = Calendar.getInstance(); - final Date today = new Date(); - boolean cleaned = false; + /** + * Cleans the messages queue + * + * @param tellMessages The messages list. + * @param tellMaxDays The maximum number of days to keep messages for. + * @return <code>True</code> if the queue was cleaned. + */ + public static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) { + final Calendar maxDate = Calendar.getInstance(); + final Date today = new Date(); + boolean cleaned = false; - for (final TellMessage message : tellMessages) - { - maxDate.setTime(message.getQueued()); - maxDate.add(Calendar.DATE, tellMaxDays); + for (final TellMessage message : tellMessages) { + maxDate.setTime(message.getQueued()); + maxDate.add(Calendar.DATE, tellMaxDays); - if (maxDate.getTime().before(today)) - { - tellMessages.remove(message); - cleaned = true; - } - } + if (maxDate.getTime().before(today)) { + tellMessages.remove(message); + cleaned = true; + } + } - return cleaned; - } + return cleaned; + } - /** - * Loads the messages. - * - * @param file The serialized objects file. - * @param logger The logger. - * - * @return The {@link net.thauvin.erik.mobibot.TellMessage} array. - */ - @SuppressWarnings("unchecked") - public static List<TellMessage> load(final String file, final Log4JLogger logger) - { - try - { + /** + * Loads the messages. + * + * @param file The serialized objects file. + * @param logger The logger. + * @return The {@link net.thauvin.erik.mobibot.TellMessage} array. + */ + @SuppressWarnings("unchecked") + public static List<TellMessage> load(final String file, final Log4JLogger logger) { + try { - try (ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) - { - if (logger.isDebugEnabled()) - { - logger.debug("Loading the messages."); - } + try (ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { + if (logger.isDebugEnabled()) { + logger.debug("Loading the messages."); + } - return ((List<TellMessage>) input.readObject()); - } - } - catch (FileNotFoundException ignore) - { - ; // Do nothing. - } - catch (IOException e) - { - logger.error("An IO error occurred loading the messages queue.", e); - } - catch (Exception e) - { - logger.getLogger().error("An error occurred loading the messages queue.", e); - } + return ((List<TellMessage>) input.readObject()); + } + } catch (FileNotFoundException ignore) { + ; // Do nothing. + } catch (IOException e) { + logger.error("An IO error occurred loading the messages queue.", e); + } catch (Exception e) { + logger.getLogger().error("An error occurred loading the messages queue.", e); + } - return new ArrayList<>(); - } + return new ArrayList<>(); + } - /** - * Saves the messages. - * - * @param file The serialized objects file. - * @param messages The {@link net.thauvin.erik.mobibot.TellMessage} array. - * @param logger The logger. - */ - public static void save(final String file, final List<TellMessage> messages, final Log4JLogger logger) - { - try - { + /** + * Saves the messages. + * + * @param file The serialized objects file. + * @param messages The {@link net.thauvin.erik.mobibot.TellMessage} array. + * @param logger The logger. + */ + public static void save(final String file, final List<TellMessage> messages, final Log4JLogger logger) { + try { - try (ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) - { - if (logger.isDebugEnabled()) - { - logger.debug("Saving the messages."); - } + try (ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { + if (logger.isDebugEnabled()) { + logger.debug("Saving the messages."); + } - output.writeObject(messages); - } - } - catch (IOException e) - { - logger.error("Unable to save messages queue.", e); - } - } + output.writeObject(messages); + } + } catch (IOException e) { + logger.error("Unable to save messages queue.", e); + } + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 45efe3b..5f3b492 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -9,68 +9,61 @@ import java.io.BufferedReader; import java.io.InputStreamReader; /** - * The <code>TwitterOAuth</code> class. <p> Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> - * to register your bot. </p> Then execute: <p> <code>java -cp "mobibot.jar:lib/*" - * net.thauvin.erik.mobibot.TwitterOAuth - * <consumerKey> <consumerSecret></code> </p> and follow the prompts/instructions. + * The <code>TwitterOAuth</code> class. + * <p> + * Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> to register your bot. + * </p> + * Then execute: + * <p> + * <code> + * java -cp "mobibot.jar:lib/*"net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> + * </code> + * </p> + * and follow the prompts/instructions. * * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> * @author <a href="http://twitter4j.org/en/code-examples.html#oauth">http://twitter4j.org/en/code-examples.html#oauth</a> * @created Sep 13, 2010 * @since 1.0 */ -public final class TwitterOAuth -{ - public static void main(final String[] args) - throws Exception - { - if (args.length == 2) - { - final twitter4j.Twitter twitter = new TwitterFactory().getInstance(); - twitter.setOAuthConsumer(args[0], args[1]); - final RequestToken requestToken = twitter.getOAuthRequestToken(); - AccessToken accessToken = null; - final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - while (null == accessToken) - { - System.out.println("Open the following URL and grant access to your account:"); - System.out.println(requestToken.getAuthorizationURL()); - System.out.print("Enter the PIN (if available) or just hit enter.[PIN]:"); - final String pin = br.readLine(); - try - { - if (pin.length() > 0) - { - accessToken = twitter.getOAuthAccessToken(requestToken, pin); - } - else - { - accessToken = twitter.getOAuthAccessToken(); - } +public final class TwitterOAuth { + public static void main(final String[] args) + throws Exception { + if (args.length == 2) { + final twitter4j.Twitter twitter = new TwitterFactory().getInstance(); + twitter.setOAuthConsumer(args[0], args[1]); + final RequestToken requestToken = twitter.getOAuthRequestToken(); + AccessToken accessToken = null; + final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + while (null == accessToken) { + System.out.println("Open the following URL and grant access to your account:"); + System.out.println(requestToken.getAuthorizationURL()); + System.out.print("Enter the PIN (if available) or just hit enter.[PIN]:"); + final String pin = br.readLine(); + try { + if (pin.length() > 0) { + accessToken = twitter.getOAuthAccessToken(requestToken, pin); + } else { + accessToken = twitter.getOAuthAccessToken(); + } - System.out.println( - "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" - + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" - + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken.getTokenSecret()); - } - catch (TwitterException te) - { - if (401 == te.getStatusCode()) - { - System.out.println("Unable to get the access token."); - } - else - { - te.printStackTrace(); - } - } - } - } - else - { - System.out.println("Usage: " + TwitterOAuth.class.getName() + " <consumerKey> <consumerSecret>"); - } + System.out.println( + "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" + + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" + + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken + .getTokenSecret()); + } catch (TwitterException te) { + if (401 == te.getStatusCode()) { + System.out.println("Unable to get the access token."); + } else { + te.printStackTrace(); + } + } + } + } else { + System.out.println("Usage: " + TwitterOAuth.class.getName() + " <consumerKey> <consumerSecret>"); + } - System.exit(0); - } + System.exit(0); + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 174d373..99586fc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -44,288 +44,235 @@ import java.util.Calendar; * @created 2014-04-26 * @since 1.0 */ -final public class Utils -{ - /** - * The ISO (YYYY-MM-DD) simple date format. - */ - public static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); +final public class Utils { + /** + * The ISO (YYYY-MM-DD) simple date format. + */ + public static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); + /** + * The timestamp simple date format. + */ + public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); + /** + * The UTC (yyyy-MM-dd HH:mm) simple date format. + */ + public static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - /** - * The timestamp simple date format. - */ - public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private Utils() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } - /** - * The UTC (yyyy-MM-dd HH:mm) simple date format. - */ - public static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + /** + * Makes the given int bold. + * + * @param i The int. + * @return The bold string. + */ + public static String bold(final int i) { + return bold(Integer.toString(i)); + } - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private Utils() - throws UnsupportedOperationException - { - throw new UnsupportedOperationException("Illegal constructor call."); - } + /** + * Makes the given string bold. + * + * @param s The string. + * @return The bold string. + */ + public static String bold(final String s) { + return Colors.BOLD + s + Colors.BOLD; + } - /** - * Makes the given int bold. - * - * @param i The int. - * - * @return The bold string. - */ - public static String bold(final int i) - { - return bold(Integer.toString(i)); - } + /** + * Builds an entry's comment for display on the channel. + * + * @param entryIndex The entry's index. + * @param commentIndex The comment's index. + * @param comment The {@link net.thauvin.erik.mobibot.EntryComment comment} object. + * @return The entry's comment. + */ + static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) { + return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " + + comment.getComment()); + } - /** - * Makes the given string bold. - * - * @param s The string. - * - * @return The bold string. - */ - public static String bold(final String s) - { - return Colors.BOLD + s + Colors.BOLD; - } + /** + * Builds an entry's link for display on the channel. + * + * @param index The entry's index. + * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. + * @return The entry's link. + * @see #buildLink(int, net.thauvin.erik.mobibot.EntryLink, boolean) + */ + static String buildLink(final int index, final EntryLink entry) { + return buildLink(index, entry, false); + } - /** - * Builds an entry's comment for display on the channel. - * - * @param entryIndex The entry's index. - * @param commentIndex The comment's index. - * @param comment The {@link net.thauvin.erik.mobibot.EntryComment comment} object. - * - * @return The entry's comment. - */ - static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) - { - return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " - + comment.getComment()); - } + /** + * Builds an entry's link for display on the channel. + * + * @param index The entry's index. + * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. + * @param isView Set to true to display the number of comments. + * @return The entry's link. + */ + static String buildLink(final int index, final EntryLink entry, final boolean isView) { + final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. - * - * @return The entry's link. - * - * @see #buildLink(int, net.thauvin.erik.mobibot.EntryLink, boolean) - */ - static String buildLink(final int index, final EntryLink entry) - { - return buildLink(index, entry, false); - } + buff.append('[').append(entry.getNick()).append(']'); - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. - * @param isView Set to true to display the number of comments. - * - * @return The entry's link. - */ - static String buildLink(final int index, final EntryLink entry, final boolean isView) - { - final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); + if (isView && entry.hasComments()) { + buff.append("[+").append(entry.getCommentsCount()).append(']'); + } - buff.append('[').append(entry.getNick()).append(']'); + buff.append(' '); - if (isView && entry.hasComments()) - { - buff.append("[+").append(entry.getCommentsCount()).append(']'); - } + if (Mobibot.NO_TITLE.equals(entry.getTitle())) { + buff.append(entry.getTitle()); + } else { + buff.append(bold(entry.getTitle())); + } - buff.append(' '); + buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )"); - if (Mobibot.NO_TITLE.equals(entry.getTitle())) - { - buff.append(entry.getTitle()); - } - else - { - buff.append(bold(entry.getTitle())); - } + return buff.toString(); + } - buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )"); + /** + * Build an entry's tags/categories for display on the channel. + * + * @param entryIndex The entry's index. + * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. + * @return The entry's tags. + */ + static String buildTags(final int entryIndex, final EntryLink entry) { + return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); + } - return buff.toString(); - } + /** + * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. + * + * @param location The File or URL location. + * @param isUrl Set to true if the location is a URL + * @return The location ending with a slash. + */ + static String ensureDir(final String location, final boolean isUrl) { + if (isUrl) { + if (location.charAt(location.length() - 1) == '/') { + return location; + } else { + return location + '/'; + } + } else { + if (location.charAt(location.length() - 1) == File.separatorChar) { + return location; + } else { + return location + File.separatorChar; + } + } + } - /** - * Makes the given string green. - * - * @param s The string. - * - * @return The bold string. - */ - public static String green(final String s) - { - return Colors.DARK_GREEN + s + Colors.NORMAL; - } + /** + * Returns a property as an int. + * + * @param property The port property value. + * @param def The default property value. + * @return The port or default value if invalid. + */ + public static int getIntProperty(final String property, final int def) { + int prop; - /** - * Build an entry's tags/categories for display on the channel. - * - * @param entryIndex The entry's index. - * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. - * - * @return The entry's tags. - */ - static String buildTags(final int entryIndex, final EntryLink entry) - { - return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); - } + try { + prop = Integer.parseInt(property); + } catch (NumberFormatException ignore) { + prop = def; + } - /** - * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. - * - * @param location The File or URL location. - * @param isUrl Set to true if the location is a URL - * - * @return The location ending with a slash. - */ - static String ensureDir(final String location, final boolean isUrl) - { - if (isUrl) - { - if (location.charAt(location.length() - 1) == '/') - { - return location; - } - else - { - return location + '/'; - } - } - else - { - if (location.charAt(location.length() - 1) == File.separatorChar) - { - return location; - } - else - { - return location + File.separatorChar; - } - } - } + return prop; + } - /** - * Returns a property as an int. - * - * @param property The port property value. - * @param def The default property value. - * - * @return The port or default value if invalid. - */ - public static int getIntProperty(final String property, final int def) - { - int prop; + /** + * Makes the given string green. + * + * @param s The string. + * @return The bold string. + */ + public static String green(final String s) { + return Colors.DARK_GREEN + s + Colors.NORMAL; + } - try - { - prop = Integer.parseInt(property); - } - catch (NumberFormatException ignore) - { - prop = def; - } + /** + * Returns <code>true</code> if the given string is <em>not</em> blank or null. + * + * @param s The string to check. + * @return <code>true</code> if the string is valid, <code>false</code> otherwise. + */ + public static boolean isValidString(final CharSequence s) { + final int len; + if (s == null || (len = s.length()) == 0) { + return false; + } + for (int i = 0; i < len; i++) { + if (!Character.isWhitespace(s.charAt(i))) { + return true; + } + } + return false; + } - return prop; - } + /** + * Returns the plural form of a word, if count > 1. + * + * @param count The count. + * @param word The word. + * @param plural The plural word. + */ + public static String plural(final long count, final String word, final String plural) { + if (count > 1) { + return plural; + } else { + return word; + } + } - /** - * Returns <code>true</code> if the given string is <em>not</em> blank or null. - * - * @param s The string to check. - * - * @return <code>true</code> if the string is valid, <code>false</code> otherwise. - */ - public static boolean isValidString(final CharSequence s) - { - final int len; - if (s == null || (len = s.length()) == 0) - { - return false; - } - for (int i = 0; i < len; i++) - { - if (!Character.isWhitespace(s.charAt(i))) - { - return true; - } - } - return false; - } + /** + * Makes the given string reverse color. + * + * @param s The string. + * @return The reverse color string. + */ + public static String reverseColor(final String s) { + return Colors.REVERSE + s + Colors.REVERSE; + } - /** - * Returns the plural form of a word, if count > 1. - * - * @param count The count. - * @param word The word. - * @param plural The plural word. - */ - public static String plural(final long count, final String word, final String plural) - { - if (count > 1) - { - return plural; - } - else - { - return word; - } - } + /** + * Returns today's date. + * + * @return Today's date in {@link #ISO_SDF ISO} format. + */ + public static String today() { + return ISO_SDF.format(Calendar.getInstance().getTime()); + } - /** - * Makes the given string reverse color. - * - * @param s The string. - * - * @return The reverse color string. - */ - public static String reverseColor(final String s) - { - return Colors.REVERSE + s + Colors.REVERSE; - } + /** + * Converts XML/XHTML entities to plain text. + * + * @param str The string to unescape. + * @return The unescaped string. + */ + public static String unescapeXml(final String str) { + String s = str.replaceAll("&", "&"); + s = s.replaceAll("<", "<"); + s = s.replaceAll(">", ">"); + s = s.replaceAll(""", "\""); + s = s.replaceAll("'", "'"); + s = s.replaceAll("'", "'"); - /** - * Returns today's date. - * - * @return Today's date in {@link #ISO_SDF ISO} format. - */ - public static String today() - { - return ISO_SDF.format(Calendar.getInstance().getTime()); - } - - /** - * Converts XML/XHTML entities to plain text. - * - * @param str The string to unescape. - * - * @return The unescaped string. - */ - public static String unescapeXml(final String str) - { - String s = str.replaceAll("&", "&"); - s = s.replaceAll("<", "<"); - s = s.replaceAll(">", ">"); - s = s.replaceAll(""", "\""); - s = s.replaceAll("'", "'"); - s = s.replaceAll("'", "'"); - - return s; - } + return s; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index f43b31a..678ac99 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -43,118 +43,105 @@ import java.util.*; * @created 2016-07-01 * @since 1.0 */ -public abstract class AbstractModule -{ +public abstract class AbstractModule { + final List<String> commands = new ArrayList<>(); + final Map<String, String> properties = new HashMap<>(); - final List<String> commands = new ArrayList<>(); + /** + * Responds to a command. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + public abstract void commandResponse(final Mobibot bot, + final String sender, + final String args, + final boolean isPrivate); - final Map<String, String> properties = new HashMap<>(); + /** + * Returns the module's commands, if any. + * + * @return The commands. + */ + public List<String> getCommands() { + return commands; + } - /** - * Responds to a command. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - public abstract void commandResponse(final Mobibot bot, - final String sender, - final String args, - final boolean isPrivate); + /** + * Returns the module's property keys. + * + * @return The keys. + */ + public Set<String> getPropertyKeys() { + return properties.keySet(); + } - /** - * Returns the module's commands, if any. - * - * @return The commands. - */ - public List<String> getCommands() - { - return commands; - } + /** + * Returns <code>true</code> if the module has properties. + * + * @return <code>true</code> or <code>false</code> . + */ + public boolean hasProperties() { + return !properties.isEmpty(); + } - /** - * Returns the module's property keys. - * - * @return The keys. - */ - public Set<String> getPropertyKeys() - { - return properties.keySet(); - } + /** + * Responds with the module's help. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The help arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + public abstract void helpResponse(final Mobibot bot, + final String sender, + final String args, + final boolean isPrivate); - /** - * Returns <code>true</code> if the module has properties. - * - * @return <code>true</code> or <code>false</code> . - */ - public boolean hasProperties() - { - return !properties.isEmpty(); - } + /** + * Returns <code>true</code> if the module is enabled. + * + * @return <code>true</code> or <code>false</code> + */ + public boolean isEnabled() { + return true; + } - /** - * Responds with the module's help. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The help arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - public abstract void helpResponse(final Mobibot bot, - final String sender, - final String args, - final boolean isPrivate); + /** + * Returns <codde>true</codde> if the module responds to private messages. + * + * @return <code>true</code> or <code>false</code> + */ + public boolean isPrivateMsgEnabled() { + return false; + } - /** - * Returns <code>true</code> if the module is enabled. - * - * @return <code>true</code> or <code>false</code> - */ - public boolean isEnabled() - { - return true; - } + /** + * Ensures that all properties have values. + * + * @return <code>true</code> if the properties are valid, <code>false</code> otherwise. + */ + public boolean isValidProperties() { + for (final String s : getPropertyKeys()) { + if (!Utils.isValidString(properties.get(s))) { + return false; + } + } - /** - * Returns <codde>true</codde> if the module responds to private messages. - * - * @return <code>true</code> or <code>false</code> - */ - public boolean isPrivateMsgEnabled() - { - return false; - } + return true; + } - /** - * Ensures that all properties have values. - * - * @return <code>true</code> if the properties are valid, <code>false</code> otherwise. - */ - public boolean isValidProperties() - { - for (final String s : getPropertyKeys()) - { - if (!Utils.isValidString(properties.get(s))) - { - return false; - } - } - - return true; - } - - /** - * Sets a property key and value. - * - * @param key The key. - * @param value The value. - */ - public void setProperty(final String key, final String value) - { - if (Utils.isValidString(key)) - { - properties.put(key, value); - } - } + /** + * Sets a property key and value. + * + * @param key The key. + * @param value The value. + */ + public void setProperty(final String key, final String value) { + if (Utils.isValidString(key)) { + properties.put(key, value); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index e5dbe92..233ae79 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -45,54 +45,43 @@ import java.text.DecimalFormat; * @created 2016-07-01 * @since 1.0 */ -public class Calc extends AbstractModule -{ - /** - * The Calc command. - */ - private static final String CALC_CMD = "calc"; +public class Calc extends AbstractModule { + /** + * The Calc command. + */ + private static final String CALC_CMD = "calc"; - /** - * The default constructor. - */ - public Calc() - { - commands.add(CALC_CMD); - } + /** + * The default constructor. + */ + public Calc() { + commands.add(CALC_CMD); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - if (Utils.isValidString(args)) - { - final DecimalFormat decimalFormat = new DecimalFormat("#.##"); + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (Utils.isValidString(args)) { + final DecimalFormat decimalFormat = new DecimalFormat("#.##"); - try - { - final Expression calc = new ExpressionBuilder(args).build(); - bot.send(bot.getChannel(), args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate())); - } - catch (Exception e) - { - if (bot.getLogger().isDebugEnabled()) - { - bot.getLogger().debug("Unable to calculate: " + args, e); - } + try { + final Expression calc = new ExpressionBuilder(args).build(); + bot.send(bot.getChannel(), args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate())); + } catch (Exception e) { + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Unable to calculate: " + args, e); + } - bot.send(bot.getChannel(), "No idea. This is the kind of math I don't get."); - } - } - else - { - helpResponse(bot, sender, args, isPrivate); - } + bot.send(bot.getChannel(), "No idea. This is the kind of math I don't get."); + } + } else { + helpResponse(bot, sender, args, isPrivate); + } - } + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To solve a mathematical calculation:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>")); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To solve a mathematical calculation:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>")); + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 5f41439..e66b343 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -54,188 +54,154 @@ import java.util.TreeMap; * @created Feb 11, 2004 * @since 1.0 */ -final public class CurrencyConverter extends AbstractModule -{ - /** - * The currency command. - */ - private static final String CURRENCY_CMD = "currency"; +final public class CurrencyConverter extends AbstractModule { + /** + * The currency command. + */ + private static final String CURRENCY_CMD = "currency"; - /** - * The rates keyword. - */ - private static final String CURRENCY_RATES_KEYWORD = "rates"; + /** + * The rates keyword. + */ + private static final String CURRENCY_RATES_KEYWORD = "rates"; - /** - * The exchange rates. - */ - private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); + /** + * The exchange rates. + */ + private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); - /** - * The exchange rates table URL. - */ - private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; + /** + * The exchange rates table URL. + */ + private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; - /** - * The last exchange rates table publication date. - */ - private String pubDate = ""; + /** + * The last exchange rates table publication date. + */ + private static String pubDate = ""; - /** - * Creates a new {@link CurrencyConverter} instance. - */ - public CurrencyConverter() - { - commands.add(CURRENCY_CMD); - } + /** + * Creates a new {@link CurrencyConverter} instance. + */ + public CurrencyConverter() { + commands.add(CURRENCY_CMD); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - synchronized (this) - { - if (!pubDate.equals(Utils.today())) - { - EXCHANGE_RATES.clear(); - } - } + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + synchronized (this) { + if (!pubDate.equals(Utils.today())) { + EXCHANGE_RATES.clear(); + } + } - new Thread(() -> run(bot, sender, args)).start(); - } + new Thread(() -> run(bot, sender, args)).start(); + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To convert from one currency to another:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To convert from one currency to another:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); - if (args.endsWith(CURRENCY_CMD)) - { - bot.send(sender, "For a listing of currency rates:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); - bot.send(sender, "For a listing of supported currencies:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); - } - } + if (args.endsWith(CURRENCY_CMD)) { + bot.send(sender, "For a listing of currency rates:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); + bot.send(sender, "For a listing of supported currencies:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); + } + } - /** - * Converts the specified currencies. - */ - private void run(final Mobibot bot, final String sender, final String query) - { - if (Utils.isValidString(sender)) - { - if (EXCHANGE_RATES.isEmpty()) - { - try - { - final SAXBuilder builder = new SAXBuilder(); - builder.setIgnoringElementContentWhitespace(true); + /** + * Converts the specified currencies. + */ + private void run(final Mobibot bot, final String sender, final String query) { + if (Utils.isValidString(sender)) { + if (EXCHANGE_RATES.isEmpty()) { + try { + final SAXBuilder builder = new SAXBuilder(); + builder.setIgnoringElementContentWhitespace(true); - final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); - final Element root = doc.getRootElement(); - final Namespace ns = root.getNamespace(""); - final Element cubeRoot = root.getChild("Cube", ns); - final Element cubeTime = cubeRoot.getChild("Cube", ns); + final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); + final Element root = doc.getRootElement(); + final Namespace ns = root.getNamespace(""); + final Element cubeRoot = root.getChild("Cube", ns); + final Element cubeTime = cubeRoot.getChild("Cube", ns); - pubDate = cubeTime.getAttribute("time").getValue(); + pubDate = cubeTime.getAttribute("time").getValue(); - final List cubes = cubeTime.getChildren(); - Element cube; + final List cubes = cubeTime.getChildren(); + Element cube; - for (final Object rawCube : cubes) - { - cube = (Element) rawCube; - EXCHANGE_RATES.put( - cube.getAttribute("currency").getValue(), - cube.getAttribute("rate").getValue()); - } + for (final Object rawCube : cubes) { + cube = (Element) rawCube; + EXCHANGE_RATES.put( + cube.getAttribute("currency").getValue(), + cube.getAttribute("rate").getValue()); + } - EXCHANGE_RATES.put("EUR", "1"); - } - catch (JDOMException e) - { - bot.getLogger().debug("Unable to parse the exchange rates table.", e); - bot.send(sender, "An error has occurred while parsing the exchange rates table."); - } - catch (IOException e) - { - bot.getLogger().debug("Unable to fetch the exchange rates table.", e); - bot.send(sender, - "An error has occurred while fetching the exchange rates table: " + e.getMessage()); - } - } + EXCHANGE_RATES.put("EUR", "1"); + } catch (JDOMException e) { + bot.getLogger().debug("Unable to parse the exchange rates table.", e); + bot.send(sender, "An error has occurred while parsing the exchange rates table."); + } catch (IOException e) { + bot.getLogger().debug("Unable to fetch the exchange rates table.", e); + bot.send(sender, + "An error has occurred while fetching the exchange rates table: " + e.getMessage()); + } + } - if (EXCHANGE_RATES.isEmpty()) - { - bot.getLogger().debug("The exchange rate table is empty."); - bot.send(sender, "Sorry, but the exchange rate table is empty."); - } - else if (Utils.isValidString(query)) - { - if (!EXCHANGE_RATES.isEmpty()) - { - if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) - { - final String[] cmds = query.split(" "); + if (EXCHANGE_RATES.isEmpty()) { + bot.getLogger().debug("The exchange rate table is empty."); + bot.send(sender, "Sorry, but the exchange rate table is empty."); + } else if (Utils.isValidString(query)) { + if (!EXCHANGE_RATES.isEmpty()) { + if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { + final String[] cmds = query.split(" "); - if (cmds.length == 4) - { - if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) - { - bot.send(sender, "You're kidding, right?"); - } - else - { - try - { - final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); - final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); - final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); + if (cmds.length == 4) { + if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) { + bot.send(sender, "You're kidding, right?"); + } else { + try { + final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); + final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); + final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); - bot.send(bot.getChannel(), - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) - + ' ' - + cmds[1].toUpperCase() - + " = " - + NumberFormat.getCurrencyInstance(Locale.US) - .format((amt * to) / from) - .substring(1) - + ' ' - + cmds[3].toUpperCase()); - } - catch (NullPointerException ignored) - { - bot.send(sender, - "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); - } - } - } - } - else if (query.equals(CURRENCY_RATES_KEYWORD)) - { - bot.send(sender, "Last Update: " + pubDate); + bot.send(bot.getChannel(), + NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + + ' ' + + cmds[1].toUpperCase() + + " = " + + NumberFormat.getCurrencyInstance(Locale.US) + .format((amt * to) / from) + .substring(1) + + ' ' + + cmds[3].toUpperCase()); + } catch (NullPointerException ignored) { + bot.send(sender, + "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + } + } + } + } else if (query.equals(CURRENCY_RATES_KEYWORD)) { + bot.send(sender, "Last Update: " + pubDate); - final StringBuilder buff = new StringBuilder(0); + final StringBuilder buff = new StringBuilder(0); - for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) - { - if (buff.length() > 0) - { - buff.append(", "); - } - buff.append(rate.getKey()).append(": ").append(rate.getValue()); - } + for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { + if (buff.length() > 0) { + buff.append(", "); + } + buff.append(rate.getKey()).append(": ").append(rate.getValue()); + } - bot.send(sender, buff.toString()); - } - } - } - else - { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); - bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); - } - } - } + bot.send(sender, buff.toString()); + } + } + } else { + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); + bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + } + } + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index e9d251f..b13c989 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -43,73 +43,63 @@ import java.util.Random; * @created 2014-04-28 * @since 1.0 */ -final public class Dice extends AbstractModule -{ - /** - * The dice command. - */ - private final String DICE_CMD = "dice"; +final public class Dice extends AbstractModule { + /** + * The dice command. + */ + private final String DICE_CMD = "dice"; - /** - * The default constructor. - */ - public Dice() - { - commands.add(DICE_CMD); - } + /** + * The default constructor. + */ + public Dice() { + commands.add(DICE_CMD); + } - /** - * Rolls the dice. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - final Random r = new Random(); + /** + * Rolls the dice. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + final Random r = new Random(); - int i = r.nextInt(6) + 1; - int y = r.nextInt(6) + 1; - final int playerTotal = i + y; + int i = r.nextInt(6) + 1; + int y = r.nextInt(6) + 1; + final int playerTotal = i + y; - bot.send(bot.getChannel(), - sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils - .bold(playerTotal)); + bot.send(bot.getChannel(), + sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils + .bold(playerTotal)); - i = r.nextInt(6) + 1; - y = r.nextInt(6) + 1; - final int total = i + y; + i = r.nextInt(6) + 1; + y = r.nextInt(6) + 1; + final int total = i + y; - bot.action( - "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); + bot.action( + "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); - if (playerTotal < total) - { - bot.action("wins."); - } - else if (playerTotal > total) - { - bot.action("lost."); - } - else - { - bot.action("tied."); - } - } + if (playerTotal < total) { + bot.action("wins."); + } else if (playerTotal > total) { + bot.action("lost."); + } else { + bot.action("tied."); + } + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To roll the dice:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD)); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To roll the dice:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD)); + } - @Override - public boolean isEnabled() - { - return true; - } + @Override + public boolean isEnabled() { + return true; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 973117d..85f2bec 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -49,121 +49,100 @@ import java.net.URLEncoder; * @created Feb 7, 2004 * @since 1.0 */ -final public class GoogleSearch extends AbstractModule -{ - /** - * The Google API Key property. - */ - private static final String GOOGLE_API_KEY_PROP = "google-api-key"; +final public class GoogleSearch extends AbstractModule { + /** + * The Google API Key property. + */ + private static final String GOOGLE_API_KEY_PROP = "google-api-key"; - /** - * The google command. - */ - private static final String GOOGLE_CMD = "google"; + /** + * The google command. + */ + private static final String GOOGLE_CMD = "google"; - /** - * The Google Custom Search Engine ID property. - */ - private static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; + /** + * The Google Custom Search Engine ID property. + */ + private static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; - /** - * The tab indent (4 spaces). - */ - private static final String TAB_INDENT = " "; + /** + * The tab indent (4 spaces). + */ + private static final String TAB_INDENT = " "; - /** - * Creates a new {@link GoogleSearch} instance. - */ - public GoogleSearch() - { - commands.add(GOOGLE_CMD); - properties.put(GOOGLE_API_KEY_PROP, ""); - properties.put(GOOGLE_CSE_KEY_PROP, ""); - } + /** + * Creates a new {@link GoogleSearch} instance. + */ + public GoogleSearch() { + commands.add(GOOGLE_CMD); + properties.put(GOOGLE_API_KEY_PROP, ""); + properties.put(GOOGLE_CSE_KEY_PROP, ""); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - if (isEnabled() && args.length() > 0) - { - if (args.length() > 0) - { - new Thread(() -> run(bot, sender, args)).start(); - } - else - { - helpResponse(bot, sender, args, isPrivate); - } - } - else - { - helpResponse(bot, sender, args, isPrivate); - } - } + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (isEnabled() && args.length() > 0) { + if (args.length() > 0) { + new Thread(() -> run(bot, sender, args)).start(); + } else { + helpResponse(bot, sender, args, isPrivate); + } + } else { + helpResponse(bot, sender, args, isPrivate); + } + } - /** - * Searches Google. - */ - private void run(final Mobibot bot, final String sender, final String query) - { - try - { - final String q = URLEncoder.encode(query, "UTF-8"); + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (isEnabled()) { + bot.send(sender, "To search Google:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); + } else { + bot.send(sender, "The Google searching facility is disabled."); + } + } - final URL url = - new URL("https://www.googleapis.com/customsearch/v1?key=" - + properties.get(GOOGLE_API_KEY_PROP) - + "&cx=" - + properties.get(GOOGLE_CSE_KEY_PROP) - + "&q=" - + q - + "&filter=1&num=5&alt=json"); - final URLConnection conn = url.openConnection(); + @Override + public boolean isEnabled() { + return isValidProperties(); + } - final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) - { - String line; - while ((line = reader.readLine()) != null) - { - sb.append(line); - } + /** + * Searches Google. + */ + private void run(final Mobibot bot, final String sender, final String query) { + try { + final String q = URLEncoder.encode(query, "UTF-8"); - final JSONObject json = new JSONObject(sb.toString()); - final JSONArray ja = json.getJSONArray("items"); + final URL url = + new URL("https://www.googleapis.com/customsearch/v1?key=" + + properties.get(GOOGLE_API_KEY_PROP) + + "&cx=" + + properties.get(GOOGLE_CSE_KEY_PROP) + + "&q=" + + q + + "&filter=1&num=5&alt=json"); + final URLConnection conn = url.openConnection(); - for (int i = 0; i < ja.length(); i++) - { - final JSONObject j = ja.getJSONObject(i); - bot.send(sender, Utils.unescapeXml(j.getString("title"))); - bot.send(sender, TAB_INDENT + Utils.green(j.getString("link"))); - } - } - } - catch (Exception e) - { - bot.getLogger().warn("Unable to search in Google for: " + query, e); - bot.send(sender, "An error has occurred searching in Google: " + e.getMessage()); - } - } + final StringBuilder sb = new StringBuilder(); + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - if (isEnabled()) - { - bot.send(sender, "To search Google:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); - } - else - { - bot.send(sender, "The Google searching facility is disabled."); - } - } + final JSONObject json = new JSONObject(sb.toString()); + final JSONArray ja = json.getJSONArray("items"); - @Override - public boolean isEnabled() - { - return isValidProperties(); - } + for (int i = 0; i < ja.length(); i++) { + final JSONObject j = ja.getJSONObject(i); + bot.send(sender, Utils.unescapeXml(j.getString("title"))); + bot.send(sender, TAB_INDENT + Utils.green(j.getString("link"))); + } + } + } catch (Exception e) { + bot.getLogger().warn("Unable to search in Google for: " + query, e); + bot.send(sender, "An error has occurred searching in Google: " + e.getMessage()); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 8796dbc..d945f85 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -47,73 +47,62 @@ import java.net.URLConnection; * @created 2014-04-20 * @since 1.0 */ -final public class Joke extends AbstractModule -{ +final public class Joke extends AbstractModule { + /** + * The joke command. + */ + private static final String JOKE_CMD = "joke"; - /** - * The joke command. - */ - private static final String JOKE_CMD = "joke"; + /** + * The ICNDB URL. + */ + private static final String JOKE_URL = + "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; - /** - * The ICNDB URL. - */ - private static final String JOKE_URL = - "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; + /** + * Creates a new {@link Joke} instance. + */ + public Joke() { + commands.add(JOKE_CMD); + } - /** - * Creates a new {@link Joke} instance. - */ - public Joke() - { - commands.add(JOKE_CMD); - } + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + new Thread(() -> run(bot, sender)).start(); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - new Thread(() -> run(bot, sender)).start(); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To retrieve a random joke:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD)); + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To retrieve a random joke:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD)); - } + /** + * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> + */ + private void run(final Mobibot bot, final String sender) { + try { + final URL url = new URL(JOKE_URL); + final URLConnection conn = url.openConnection(); - /** - * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> - */ - private void run(final Mobibot bot, final String sender) - { - try - { - final URL url = new URL(JOKE_URL); - final URLConnection conn = url.openConnection(); + final StringBuilder sb = new StringBuilder(); + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } - final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) - { - String line; - while ((line = reader.readLine()) != null) - { - sb.append(line); - } + final JSONObject json = new JSONObject(sb.toString()); - final JSONObject json = new JSONObject(sb.toString()); - - bot.send(bot.getChannel(), - Colors.CYAN - + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") - .replaceAll("\\\"", "\"") - + Colors.NORMAL); - } - } - catch (Exception e) - { - bot.getLogger().warn("Unable to retrieve random joke.", e); - bot.send(sender, "An error has occurred retrieving a random joke: " + e.getMessage()); - } - } + bot.send(bot.getChannel(), + Colors.CYAN + + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") + .replaceAll("\\\"", "\"") + + Colors.NORMAL); + } + } catch (Exception e) { + bot.getLogger().warn("Unable to retrieve random joke.", e); + bot.send(sender, "An error has occurred retrieving a random joke: " + e.getMessage()); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index b42c686..cf67caa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -45,190 +45,151 @@ import java.net.UnknownHostException; * @created 2014-04-26 * @since 1.0 */ -final public class Lookup extends AbstractModule -{ - /** - * THe lookup command. - */ - private static final String LOOKUP_CMD = "lookup"; +final public class Lookup extends AbstractModule { + /** + * THe lookup command. + */ + private static final String LOOKUP_CMD = "lookup"; - /** - * The whois host. - */ - private static final String WHOIS_HOST = "whois.arin.net"; + /** + * The whois host. + */ + private static final String WHOIS_HOST = "whois.arin.net"; - /** - * The default constructor - */ - public Lookup() - { - commands.add(LOOKUP_CMD); - } + /** + * The default constructor + */ + public Lookup() { + commands.add(LOOKUP_CMD); + } - /** - * Process a command. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - if (args.matches("(\\S.)+(\\S)+")) - { - try - { - bot.send(bot.getChannel(), Lookup.lookup(args)); - } - catch (UnknownHostException ignore) - { - if (args.matches( - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) - { - try - { - final String[] lines = Lookup.whois(args); + /** + * Performs a DNS lookup on the specified query. + * + * @param query The IP address or hostname. + * @return The lookup query result string. + * @throws java.net.UnknownHostException If the host is unknown. + */ + private static String lookup(final String query) + throws UnknownHostException { + final StringBuilder buffer = new StringBuilder(""); - if ((lines != null) && (lines.length > 0)) - { - String line; + final InetAddress[] results = InetAddress.getAllByName(query); + String hostInfo; - for (final String rawLine : lines) - { - line = rawLine.trim(); + for (final InetAddress result : results) { + if (result.getHostAddress().equals(query)) { + hostInfo = result.getHostName(); - if ((line.length() > 0) && (line.charAt(0) != '#')) - { - bot.send(bot.getChannel(), line); - } - } - } - else - { - bot.send(bot.getChannel(), "Unknown host."); - } - } - catch (IOException ioe) - { - if (bot.getLogger().isDebugEnabled()) - { - bot.getLogger().debug("Unable to perform whois IP lookup: " + args, ioe); - } + if (hostInfo.equals(query)) { + throw new UnknownHostException(); + } + } else { + hostInfo = result.getHostAddress(); + } - bot.send(bot.getChannel(), "Unable to perform whois IP lookup: " + ioe.getMessage()); - } - } - else - { - bot.send(bot.getChannel(), "Unknown host."); - } - } - } - else - { - helpResponse(bot, sender, args, true); - } - } + if (buffer.length() > 0) { + buffer.append(", "); + } - /** - * Performs a DNS lookup on the specified query. - * - * @param query The IP address or hostname. - * - * @return The lookup query result string. - * - * @throws java.net.UnknownHostException If the host is unknown. - */ - private static String lookup(final String query) - throws UnknownHostException - { - final StringBuilder buffer = new StringBuilder(""); + buffer.append(hostInfo); + } - final InetAddress[] results = InetAddress.getAllByName(query); - String hostInfo; + return buffer.toString(); + } - for (final InetAddress result : results) - { - if (result.getHostAddress().equals(query)) - { - hostInfo = result.getHostName(); + /** + * Performs a whois IP query. + * + * @param query The IP address. + * @return The IP whois data, if any. + * @throws java.io.IOException If a connection error occurs. + */ + private static String[] whois(final String query) + throws IOException { + return whois(query, WHOIS_HOST); + } - if (hostInfo.equals(query)) - { - throw new UnknownHostException(); - } - } - else - { - hostInfo = result.getHostAddress(); - } + /** + * Performs a whois IP query. + * + * @param query The IP address. + * @param host The whois host. + * @return The IP whois data, if any. + * @throws java.io.IOException If a connection error occurs. + */ + @SuppressWarnings("WeakerAccess, SameParameterValue") + public static String[] whois(final String query, final String host) + throws IOException { + final WhoisClient whois = new WhoisClient(); + String[] lines; - if (buffer.length() > 0) - { - buffer.append(", "); - } + try { + whois.setDefaultTimeout(Mobibot.CONNECT_TIMEOUT); + whois.connect(host); + whois.setSoTimeout(Mobibot.CONNECT_TIMEOUT); + whois.setSoLinger(false, 0); - buffer.append(hostInfo); - } + lines = whois.query('-' + query).split("\n"); + } finally { + whois.disconnect(); + } - return buffer.toString(); - } + return lines; + } - /** - * Performs a whois IP query. - * - * @param query The IP address. - * - * @return The IP whois data, if any. - * - * @throws java.io.IOException If a connection error occurs. - */ - private static String[] whois(final String query) - throws IOException - { - return whois(query, WHOIS_HOST); - } + /** + * Process a command. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (args.matches("(\\S.)+(\\S)+")) { + try { + bot.send(bot.getChannel(), Lookup.lookup(args)); + } catch (UnknownHostException ignore) { + if (args.matches( + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) { + try { + final String[] lines = Lookup.whois(args); - /** - * Performs a whois IP query. - * - * @param query The IP address. - * @param host The whois host. - * - * @return The IP whois data, if any. - * - * @throws java.io.IOException If a connection error occurs. - */ - @SuppressWarnings("WeakerAccess, SameParameterValue") - public static String[] whois(final String query, final String host) - throws IOException - { - final WhoisClient whois = new WhoisClient(); - String[] lines; + if ((lines != null) && (lines.length > 0)) { + String line; - try - { - whois.setDefaultTimeout(Mobibot.CONNECT_TIMEOUT); - whois.connect(host); - whois.setSoTimeout(Mobibot.CONNECT_TIMEOUT); - whois.setSoLinger(false, 0); + for (final String rawLine : lines) { + line = rawLine.trim(); - lines = whois.query('-' + query).split("\n"); - } - finally - { - whois.disconnect(); - } + if ((line.length() > 0) && (line.charAt(0) != '#')) { + bot.send(bot.getChannel(), line); + } + } + } else { + bot.send(bot.getChannel(), "Unknown host."); + } + } catch (IOException ioe) { + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Unable to perform whois IP lookup: " + args, ioe); + } - return lines; - } + bot.send(bot.getChannel(), "Unable to perform whois IP lookup: " + ioe.getMessage()); + } + } else { + bot.send(bot.getChannel(), "Unknown host."); + } + } + } else { + helpResponse(bot, sender, args, true); + } + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To perform a DNS lookup query:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>")); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To perform a DNS lookup query:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>")); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 19feda8..dad186e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -44,49 +44,47 @@ import java.util.Random; * @created 2016-07-02 * @since 1.0 */ -public class Ping extends AbstractModule -{ - /** - * The ping responses. - */ - private static final List<String> PINGS = Arrays.asList("is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!"); +public class Ping extends AbstractModule { + /** + * The ping responses. + */ + private static final List<String> PINGS = + Arrays.asList( + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!"); - /** - * The ping command. - */ - private static final String PING_CMD = "ping"; + /** + * The ping command. + */ + private static final String PING_CMD = "ping"; - /** - * The default constructor. - */ - public Ping() - { - commands.add(PING_CMD); - } + /** + * The default constructor. + */ + public Ping() { + commands.add(PING_CMD); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - final Random r = new Random(); + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + final Random r = new Random(); - bot.action(PINGS.get(r.nextInt(PINGS.size()))); - } + bot.action(PINGS.get(r.nextInt(PINGS.size()))); + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To ping the bot:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + PING_CMD)); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To ping the bot:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + PING_CMD)); + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index b2dbbc0..6d4096a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -45,120 +45,93 @@ import java.io.IOException; * @created Feb 7, 2004 * @since 1.0 */ -final public class StockQuote extends AbstractModule -{ - /** - * The quote command. - */ - private static final String STOCK_CMD = "stock"; +final public class StockQuote extends AbstractModule { + /** + * The quote command. + */ + private static final String STOCK_CMD = "stock"; - /** - * The Yahoo! stock quote URL. - */ - private static final String YAHOO_URL = "http://finance.yahoo.com/d/quotes.csv?&f=snl1d1t1c1oghv&e=.csv&s="; + /** + * The Yahoo! stock quote URL. + */ + private static final String YAHOO_URL = "http://finance.yahoo.com/d/quotes.csv?&f=snl1d1t1c1oghv&e=.csv&s="; - /** - * Creates a new {@link StockQuote} instance. - */ - public StockQuote() - { - commands.add(STOCK_CMD); - } + /** + * Creates a new {@link StockQuote} instance. + */ + public StockQuote() { + commands.add(STOCK_CMD); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - if (args.length() > 0) - { - new Thread(() -> run(bot, sender, args)).start(); - } - else - { - helpResponse(bot, sender, args, isPrivate); - } - } + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (args.length() > 0) { + new Thread(() -> run(bot, sender, args)).start(); + } else { + helpResponse(bot, sender, args, isPrivate); + } + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To retrieve a stock quote:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol[.country code]>")); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To retrieve a stock quote:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol[.country code]>")); + } - /** - * Returns the specified stock quote from Yahoo! - */ - private void run(final Mobibot bot, final String sender, final String symbol) - { - try - { - final HttpClient client = new HttpClient(); - client.getHttpConnectionManager().getParams().setConnectionTimeout(Mobibot.CONNECT_TIMEOUT); - client.getHttpConnectionManager().getParams().setSoTimeout(Mobibot.CONNECT_TIMEOUT); + /** + * Returns the specified stock quote from Yahoo! + */ + private void run(final Mobibot bot, final String sender, final String symbol) { + try { + final HttpClient client = new HttpClient(); + client.getHttpConnectionManager().getParams().setConnectionTimeout(Mobibot.CONNECT_TIMEOUT); + client.getHttpConnectionManager().getParams().setSoTimeout(Mobibot.CONNECT_TIMEOUT); - final GetMethod getMethod = new GetMethod(YAHOO_URL + symbol.toUpperCase()); - client.executeMethod(getMethod); + final GetMethod getMethod = new GetMethod(YAHOO_URL + symbol.toUpperCase()); + client.executeMethod(getMethod); - final String[][] lines = CSVParser.parse(getMethod.getResponseBodyAsString()); + final String[][] lines = CSVParser.parse(getMethod.getResponseBodyAsString()); - if (lines.length > 0) - { - final String[] quote = lines[0]; + if (lines.length > 0) { + final String[] quote = lines[0]; - if (quote.length > 0) - { - if ((quote.length > 3) && (!"N/A".equalsIgnoreCase(quote[3]))) - { - bot.send(bot.getChannel(), "Symbol: " + quote[0] + " [" + quote[1] + ']'); + if (quote.length > 0) { + if ((quote.length > 3) && (!"N/A".equalsIgnoreCase(quote[3]))) { + bot.send(bot.getChannel(), "Symbol: " + quote[0] + " [" + quote[1] + ']'); - if (quote.length > 5) - { - bot.send(bot.getChannel(), "Last Trade: " + quote[2] + " (" + quote[5] + ')'); - } - else - { - bot.send(bot.getChannel(), "Last Trade: " + quote[2]); - } + if (quote.length > 5) { + bot.send(bot.getChannel(), "Last Trade: " + quote[2] + " (" + quote[5] + ')'); + } else { + bot.send(bot.getChannel(), "Last Trade: " + quote[2]); + } - if (quote.length > 4) - { - bot.send(sender, "Time: " + quote[3] + ' ' + quote[4]); - } + if (quote.length > 4) { + bot.send(sender, "Time: " + quote[3] + ' ' + quote[4]); + } - if (quote.length > 6 && !"N/A".equalsIgnoreCase(quote[6])) - { - bot.send(sender, "Open: " + quote[6]); - } + if (quote.length > 6 && !"N/A".equalsIgnoreCase(quote[6])) { + bot.send(sender, "Open: " + quote[6]); + } - if (quote.length > 7 && !"N/A".equalsIgnoreCase(quote[7]) && !"N/A".equalsIgnoreCase(quote[8])) - { - bot.send(sender, "Day's Range: " + quote[7] + " - " + quote[8]); - } + if (quote.length > 7 && !"N/A".equalsIgnoreCase(quote[7]) && !"N/A".equalsIgnoreCase(quote[8])) { + bot.send(sender, "Day's Range: " + quote[7] + " - " + quote[8]); + } - if (quote.length > 9 && !"0".equalsIgnoreCase(quote[9])) - { - bot.send(sender, "Volume: " + quote[9]); - } - } - else - { - bot.send(sender, "Invalid ticker symbol."); - } - } - else - { - bot.send(sender, "No values returned."); - } - } - else - { - bot.send(sender, "No data returned."); - } - } - catch (IOException e) - { - bot.getLogger().debug("Unable to retrieve stock quote for: " + symbol, e); - bot.send(sender, "An error has occurred retrieving a stock quote: " + e.getMessage()); - } - } + if (quote.length > 9 && !"0".equalsIgnoreCase(quote[9])) { + bot.send(sender, "Volume: " + quote[9]); + } + } else { + bot.send(sender, "Invalid ticker symbol."); + } + } else { + bot.send(sender, "No values returned."); + } + } else { + bot.send(sender, "No data returned."); + } + } catch (IOException e) { + bot.getLogger().debug("Unable to retrieve stock quote for: " + symbol, e); + bot.send(sender, "An error has occurred retrieving a stock quote: " + e.getMessage()); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index b65a4b0..05e41d7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -43,92 +43,74 @@ import twitter4j.conf.ConfigurationBuilder; * @created Sept 10, 2008 * @since 1.0 */ -final public class Twitter extends AbstractModule -{ - private final static String CONSUMER_KEY_PROP = "twitter-consumerKey"; +final public class Twitter extends AbstractModule { + private final static String CONSUMER_KEY_PROP = "twitter-consumerKey"; + private final static String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; + private final static String TOKEN_PROP = "twitter-token"; + private final static String TOKEN_SECRET_PROP = "twitter-tokenSecret"; - private final static String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; + /** + * The twitter command. + */ + private final static String TWITTER_CMD = "twitter"; - private final static String TOKEN_PROP = "twitter-token"; + /** + * Creates a new {@link Twitter} instance. + */ + public Twitter() { + commands.add(TWITTER_CMD); + properties.put(CONSUMER_SECRET_PROP, ""); + properties.put(CONSUMER_KEY_PROP, ""); + properties.put(TOKEN_PROP, ""); + properties.put(TOKEN_SECRET_PROP, ""); + } - private final static String TOKEN_SECRET_PROP = "twitter-tokenSecret"; + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (isEnabled() && args.length() > 0) { + new Thread(() -> run(bot, sender, args)).start(); + } else { + helpResponse(bot, sender, args, isPrivate); + } + } - /** - * The twitter command. - */ - private final static String TWITTER_CMD = "twitter"; + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (isEnabled()) { + bot.send(sender, "To post to Twitter:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>")); + } else { + bot.send(sender, "The Twitter posting facility is disabled."); + } + } - /** - * Creates a new {@link Twitter} instance. - */ - public Twitter() - { - commands.add(TWITTER_CMD); - properties.put(CONSUMER_SECRET_PROP, ""); - properties.put(CONSUMER_KEY_PROP, ""); - properties.put(TOKEN_PROP, ""); - properties.put(TOKEN_SECRET_PROP, ""); - } + @Override + public boolean isEnabled() { + return isValidProperties(); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - if (isEnabled() && args.length() > 0) - { - new Thread(() -> run(bot, sender, args)).start(); - } - else - { - helpResponse(bot, sender, args, isPrivate); - } - } + /** + * Posts to twitter. + */ + private void run(final Mobibot bot, final String sender, final String message) { + try { + final ConfigurationBuilder cb = new ConfigurationBuilder(); + cb.setDebugEnabled(true) + .setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) + .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) + .setOAuthAccessToken(properties.get(TOKEN_PROP)) + .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); + final TwitterFactory tf = new TwitterFactory(cb.build()); + final twitter4j.Twitter twitter = tf.getInstance(); - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - if (isEnabled()) - { - bot.send(sender, "To post to Twitter:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>")); - } - else - { - bot.send(sender, "The Twitter posting facility is disabled."); - } - } + final Status status = twitter.updateStatus(message + " (" + sender + ')'); - @Override - public boolean isEnabled() - { - return isValidProperties(); - } - - /** - * Posts to twitter. - */ - private void run(final Mobibot bot, final String sender, final String message) - { - try - { - final ConfigurationBuilder cb = new ConfigurationBuilder(); - cb.setDebugEnabled(true) - .setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) - .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) - .setOAuthAccessToken(properties.get(TOKEN_PROP)) - .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); - final TwitterFactory tf = new TwitterFactory(cb.build()); - final twitter4j.Twitter twitter = tf.getInstance(); - - final Status status = twitter.updateStatus(message + " (" + sender + ')'); - - bot.send(sender, - "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status - .getId()); - } - catch (Exception e) - { - bot.getLogger().warn("Unable to post to Twitter: " + message, e); - bot.send(sender, "An error has occurred: " + e.getMessage()); - } - } + bot.send(sender, + "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status + .getId()); + } catch (Exception e) { + bot.getLogger().warn("Unable to post to Twitter: " + message, e); + bot.send(sender, "An error has occurred: " + e.getMessage()); + } + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 182d5ee..ab07d74 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -43,79 +43,70 @@ import java.util.Random; * @created 2014-04-28 * @since 1.0 */ -final public class War extends AbstractModule -{ - /** - * The war command. - */ - private static final String WAR_CMD = "war"; +final public class War extends AbstractModule { + /** + * The war command. + */ + private static final String WAR_CMD = "war"; - /** - * The deck of card. - */ - private static final String[] WAR_DECK = - new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; + /** + * The deck of card. + */ + private static final String[] WAR_DECK = + new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; - /** - * The suits for the deck of card. - */ - private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; + /** + * The suits for the deck of card. + */ + private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; - /** - * The default constructor. - */ - public War() - { - commands.add(WAR_CMD); - } + /** + * The default constructor. + */ + public War() { + commands.add(WAR_CMD); + } - /** - * Plays war. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - final Random r = new Random(); + /** + * Plays war. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + final Random r = new Random(); - int i; - int y; + int i; + int y; - while (true) - { - i = r.nextInt(WAR_DECK.length); - y = r.nextInt(WAR_DECK.length); + while (true) { + i = r.nextInt(WAR_DECK.length); + y = r.nextInt(WAR_DECK.length); - bot.send(bot.getChannel(), - sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + bot.send(bot.getChannel(), + sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - if (i != y) - { - break; - } + if (i != y) { + break; + } - bot.send(bot.getChannel(), "This means " + Utils.bold("WAR") + '!'); - } + bot.send(bot.getChannel(), "This means " + Utils.bold("WAR") + '!'); + } - if (i < y) - { - bot.action("lost."); - } - else if (i > y) - { - bot.action("wins."); - } - } + if (i < y) { + bot.action("lost."); + } else if (i > y) { + bot.action("wins."); + } + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To play war:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WAR_CMD)); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To play war:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WAR_CMD)); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java index ba0bb8d..2611d2f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java @@ -47,145 +47,127 @@ import java.util.Date; * @created Feb 7, 2004 * @since 1.0 */ -final public class Weather extends AbstractModule -{ - /** - * The decimal number format. - */ - private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("0.##"); +final public class Weather extends AbstractModule { + /** + * The decimal number format. + */ + private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("0.##"); - /** - * The URL where the stations are listed. - */ - private static final String STATIONS_URL = "http://www.rap.ucar.edu/weather/surface/stations.txt"; + /** + * The URL where the stations are listed. + */ + private static final String STATIONS_URL = "http://www.rap.ucar.edu/weather/surface/stations.txt"; - /** - * THe weather command. - */ - private static final String WEATHER_CMD = "weather"; + /** + * THe weather command. + */ + private static final String WEATHER_CMD = "weather"; - /** - * Creates a new {@link Weather} instance. - */ - public Weather() - { - commands.add(WEATHER_CMD); - } + /** + * Creates a new {@link Weather} instance. + */ + public Weather() { + commands.add(WEATHER_CMD); + } - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); - } + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To display weather information:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <station id>")); - bot.send(sender, "For a listing of the ICAO station IDs, please visit: " + STATIONS_URL); - } + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To display weather information:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <station id>")); + bot.send(sender, "For a listing of the ICAO station IDs, please visit: " + STATIONS_URL); + } - @Override - public boolean isPrivateMsgEnabled() - { - return true; - } + @Override + public boolean isPrivateMsgEnabled() { + return true; + } - /** - * Fetches the weather data from a specific station ID. - */ - private void run(final Mobibot bot, final String sender, final String station, final boolean isPrivate) - { - if (station.length() == 4) - { - final Metar metar = net.sf.jweather.Weather.getMetar(station); + /** + * Fetches the weather data from a specific station ID. + */ + private void run(final Mobibot bot, final String sender, final String station, final boolean isPrivate) { + if (station.length() == 4) { + final Metar metar = net.sf.jweather.Weather.getMetar(station); - if (metar != null) - { - Float result; + if (metar != null) { + Float result; - bot.send(sender, "Station ID: " + metar.getStationID(), isPrivate); + bot.send(sender, "Station ID: " + metar.getStationID(), isPrivate); - bot.send(sender, - "At: " - + Utils.UTC_SDF.format(metar.getDate()) - + " UTC (" - + (((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) - + " minutes ago)", - isPrivate); + bot.send(sender, + "At: " + + Utils.UTC_SDF.format(metar.getDate()) + + " UTC (" + + (((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + + " minutes ago)", + isPrivate); - result = metar.getWindSpeedInMPH(); + result = metar.getWindSpeedInMPH(); - if (result != null) - { - bot.send(sender, - "Wind Speed: " - + result - + " mph, " - + metar.getWindSpeedInKnots() - + " knots, " - + metar.getWindSpeedInMPS() - + " m/s", - isPrivate); - } + if (result != null) { + bot.send(sender, + "Wind Speed: " + + result + + " mph, " + + metar.getWindSpeedInKnots() + + " knots, " + + metar.getWindSpeedInMPS() + + " m/s", + isPrivate); + } - result = metar.getVisibility(); + result = metar.getVisibility(); - if (result != null) - { - bot.send(sender, - "Visibility: " - + (metar.getVisibilityLessThan() ? "< " : "") - + NUMBER_FORMAT.format(result) - + " mi, " + metar.getVisibilityInKilometers() + " km", - isPrivate); - } + if (result != null) { + bot.send(sender, + "Visibility: " + + (metar.getVisibilityLessThan() ? "< " : "") + + NUMBER_FORMAT.format(result) + + " mi, " + metar.getVisibilityInKilometers() + " km", + isPrivate); + } - result = metar.getPressure(); + result = metar.getPressure(); - if (result != null) - { - bot.send(sender, - "Pressure: " + result + " Hg, " + metar.getPressureInHectoPascals() + " hPa", - isPrivate); - } + if (result != null) { + bot.send(sender, + "Pressure: " + result + " Hg, " + metar.getPressureInHectoPascals() + " hPa", + isPrivate); + } - result = metar.getTemperatureInCelsius(); + result = metar.getTemperatureInCelsius(); - if (result != null) - { - bot.send(sender, - "Temperature: " + result + " \u00B0C, " + metar.getTemperatureInFahrenheit() + " \u00B0F", - isPrivate); - } + if (result != null) { + bot.send(sender, + "Temperature: " + result + " \u00B0C, " + metar.getTemperatureInFahrenheit() + " \u00B0F", + isPrivate); + } - if (metar.getWeatherConditions() != null) - { - for (final Object weatherCondition : metar.getWeatherConditions()) - { - bot.send(sender, ((WeatherCondition) weatherCondition).getNaturalLanguageString(), isPrivate); - } - } + if (metar.getWeatherConditions() != null) { + for (final Object weatherCondition : metar.getWeatherConditions()) { + bot.send(sender, ((WeatherCondition) weatherCondition).getNaturalLanguageString(), isPrivate); + } + } - if (metar.getSkyConditions() != null) - { - for (final Object skyCondition : metar.getSkyConditions()) - { - bot.send(sender, ((SkyCondition) skyCondition).getNaturalLanguageString(), isPrivate); - } - } + if (metar.getSkyConditions() != null) { + for (final Object skyCondition : metar.getSkyConditions()) { + bot.send(sender, ((SkyCondition) skyCondition).getNaturalLanguageString(), isPrivate); + } + } - return; - } - else - { - bot.send(sender, "Invalid Station ID. Please try again.", isPrivate); + return; + } else { + bot.send(sender, "Invalid Station ID. Please try again.", isPrivate); - return; - } - } + return; + } + } - helpResponse(bot, sender, station, isPrivate); - } + helpResponse(bot, sender, station, isPrivate); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 9bc3c1a..87263d4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -46,210 +46,184 @@ import java.util.TreeMap; * @created 2014-04-27 * @since 1.0 */ -final public class WorldTime extends AbstractModule -{ - /** - * The beats (Internet Time) keyword. - */ - private static final String BEATS_KEYWORD = ".beats"; +final public class WorldTime extends AbstractModule { + /** + * The beats (Internet Time) keyword. + */ + private static final String BEATS_KEYWORD = ".beats"; - /** - * The supported countries. - */ - private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); + /** + * The supported countries. + */ + private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); - /** - * The time command. - */ - private static final String TIME_CMD = "time"; + /** + * The time command. + */ + private static final String TIME_CMD = "time"; - /** - * The date/time format. - */ - private static final SimpleDateFormat TIME_SDF = - new SimpleDateFormat("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"); + /** + * The date/time format. + */ + private static final SimpleDateFormat TIME_SDF = + new SimpleDateFormat("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"); - /** - * Creates a new {@link WorldTime} instance. - */ - public WorldTime() - { - commands.add(TIME_CMD); + /** + * Creates a new {@link WorldTime} instance. + */ + public WorldTime() { + commands.add(TIME_CMD); - // Initialize the countries map - COUNTRIES_MAP.put("AU", "Australia/Sydney"); - COUNTRIES_MAP.put("BE", "Europe/Brussels"); - COUNTRIES_MAP.put("CA", "America/Montreal"); - COUNTRIES_MAP.put("CDT", "America/Chicago"); - COUNTRIES_MAP.put("CET", "CET"); - COUNTRIES_MAP.put("CH", "Europe/Zurich"); - COUNTRIES_MAP.put("CN", "Asia/Shanghai"); - COUNTRIES_MAP.put("CST", "America/Chicago"); - COUNTRIES_MAP.put("CU", "Cuba"); - COUNTRIES_MAP.put("DE", "Europe/Berlin"); - COUNTRIES_MAP.put("DK", "Europe/Copenhagen"); - COUNTRIES_MAP.put("EDT", "America/New_York"); - COUNTRIES_MAP.put("EG", "Africa/Cairo"); - COUNTRIES_MAP.put("ER", "Africa/Asmara"); - COUNTRIES_MAP.put("ES", "Europe/Madrid"); - COUNTRIES_MAP.put("EST", "America/New_York"); - COUNTRIES_MAP.put("FI", "Europe/Helsinki"); - COUNTRIES_MAP.put("FR", "Europe/Paris"); - COUNTRIES_MAP.put("GB", "Europe/London"); - COUNTRIES_MAP.put("GMT", "GMT"); - COUNTRIES_MAP.put("HK", "Asia/Hong_Kong"); - COUNTRIES_MAP.put("HST", "HST"); - COUNTRIES_MAP.put("IE", "Europe/Dublin"); - COUNTRIES_MAP.put("IL", "Asia/Tel_Aviv"); - COUNTRIES_MAP.put("IN", "Asia/Calcutta"); - COUNTRIES_MAP.put("IR", "Asia/Tehran"); - COUNTRIES_MAP.put("IS", "Atlantic/Reykjavik"); - COUNTRIES_MAP.put("IT", "Europe/Rome"); - COUNTRIES_MAP.put("JM", "Jamaica"); - COUNTRIES_MAP.put("JP", "Asia/Tokyo"); - COUNTRIES_MAP.put("LY", "Africa/Tripoli"); - COUNTRIES_MAP.put("MDT", "America/Denver"); - COUNTRIES_MAP.put("MH", "Kwajalein"); - COUNTRIES_MAP.put("MST", "America/Denver"); - COUNTRIES_MAP.put("MX", "America/Mexico_City"); - COUNTRIES_MAP.put("NL", "Europe/Amsterdam"); - COUNTRIES_MAP.put("NO", "Europe/Oslo"); - COUNTRIES_MAP.put("NP", "Asia/Katmandu"); - COUNTRIES_MAP.put("NZ", "Pacific/Auckland"); - COUNTRIES_MAP.put("PDT", "America/Los_Angeles"); - COUNTRIES_MAP.put("PK", "Asia/Karachi"); - COUNTRIES_MAP.put("PL", "Europe/Warsaw"); - COUNTRIES_MAP.put("PST", "America/Los_Angeles"); - COUNTRIES_MAP.put("PT", "Europe/Lisbon"); - COUNTRIES_MAP.put("RU", "Europe/Moscow"); - COUNTRIES_MAP.put("SE", "Europe/Stockholm"); - COUNTRIES_MAP.put("SG", "Asia/Singapore"); - COUNTRIES_MAP.put("SU", "Europe/Moscow"); - COUNTRIES_MAP.put("TH", "Asia/Bangkok"); - COUNTRIES_MAP.put("TM", "Asia/Ashgabat"); - COUNTRIES_MAP.put("TR", "Europe/Istanbul"); - COUNTRIES_MAP.put("TW", "Asia/Taipei"); - COUNTRIES_MAP.put("UK", "Europe/London"); - COUNTRIES_MAP.put("US", "America/New_York"); - COUNTRIES_MAP.put("UTC", "UTC"); - COUNTRIES_MAP.put("VA", "Europe/Vatican"); - COUNTRIES_MAP.put("VN", "Asia/Ho_Chi_Minh"); - COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); - COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); + // Initialize the countries map + COUNTRIES_MAP.put("AU", "Australia/Sydney"); + COUNTRIES_MAP.put("BE", "Europe/Brussels"); + COUNTRIES_MAP.put("CA", "America/Montreal"); + COUNTRIES_MAP.put("CDT", "America/Chicago"); + COUNTRIES_MAP.put("CET", "CET"); + COUNTRIES_MAP.put("CH", "Europe/Zurich"); + COUNTRIES_MAP.put("CN", "Asia/Shanghai"); + COUNTRIES_MAP.put("CST", "America/Chicago"); + COUNTRIES_MAP.put("CU", "Cuba"); + COUNTRIES_MAP.put("DE", "Europe/Berlin"); + COUNTRIES_MAP.put("DK", "Europe/Copenhagen"); + COUNTRIES_MAP.put("EDT", "America/New_York"); + COUNTRIES_MAP.put("EG", "Africa/Cairo"); + COUNTRIES_MAP.put("ER", "Africa/Asmara"); + COUNTRIES_MAP.put("ES", "Europe/Madrid"); + COUNTRIES_MAP.put("EST", "America/New_York"); + COUNTRIES_MAP.put("FI", "Europe/Helsinki"); + COUNTRIES_MAP.put("FR", "Europe/Paris"); + COUNTRIES_MAP.put("GB", "Europe/London"); + COUNTRIES_MAP.put("GMT", "GMT"); + COUNTRIES_MAP.put("HK", "Asia/Hong_Kong"); + COUNTRIES_MAP.put("HST", "HST"); + COUNTRIES_MAP.put("IE", "Europe/Dublin"); + COUNTRIES_MAP.put("IL", "Asia/Tel_Aviv"); + COUNTRIES_MAP.put("IN", "Asia/Calcutta"); + COUNTRIES_MAP.put("IR", "Asia/Tehran"); + COUNTRIES_MAP.put("IS", "Atlantic/Reykjavik"); + COUNTRIES_MAP.put("IT", "Europe/Rome"); + COUNTRIES_MAP.put("JM", "Jamaica"); + COUNTRIES_MAP.put("JP", "Asia/Tokyo"); + COUNTRIES_MAP.put("LY", "Africa/Tripoli"); + COUNTRIES_MAP.put("MDT", "America/Denver"); + COUNTRIES_MAP.put("MH", "Kwajalein"); + COUNTRIES_MAP.put("MST", "America/Denver"); + COUNTRIES_MAP.put("MX", "America/Mexico_City"); + COUNTRIES_MAP.put("NL", "Europe/Amsterdam"); + COUNTRIES_MAP.put("NO", "Europe/Oslo"); + COUNTRIES_MAP.put("NP", "Asia/Katmandu"); + COUNTRIES_MAP.put("NZ", "Pacific/Auckland"); + COUNTRIES_MAP.put("PDT", "America/Los_Angeles"); + COUNTRIES_MAP.put("PK", "Asia/Karachi"); + COUNTRIES_MAP.put("PL", "Europe/Warsaw"); + COUNTRIES_MAP.put("PST", "America/Los_Angeles"); + COUNTRIES_MAP.put("PT", "Europe/Lisbon"); + COUNTRIES_MAP.put("RU", "Europe/Moscow"); + COUNTRIES_MAP.put("SE", "Europe/Stockholm"); + COUNTRIES_MAP.put("SG", "Asia/Singapore"); + COUNTRIES_MAP.put("SU", "Europe/Moscow"); + COUNTRIES_MAP.put("TH", "Asia/Bangkok"); + COUNTRIES_MAP.put("TM", "Asia/Ashgabat"); + COUNTRIES_MAP.put("TR", "Europe/Istanbul"); + COUNTRIES_MAP.put("TW", "Asia/Taipei"); + COUNTRIES_MAP.put("UK", "Europe/London"); + COUNTRIES_MAP.put("US", "America/New_York"); + COUNTRIES_MAP.put("UTC", "UTC"); + COUNTRIES_MAP.put("VA", "Europe/Vatican"); + COUNTRIES_MAP.put("VN", "Asia/Ho_Chi_Minh"); + COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); + COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); - for (final String tz : TimeZone.getAvailableIDs()) - { - if (!tz.contains("/") && tz.length() == 3 & !COUNTRIES_MAP.containsKey(tz)) - { - COUNTRIES_MAP.put(tz, tz); - } - } - } + for (final String tz : TimeZone.getAvailableIDs()) { + if (!tz.contains("/") && tz.length() == 3 & !COUNTRIES_MAP.containsKey(tz)) { + COUNTRIES_MAP.put(tz, tz); + } + } + } - /** - * Responds with the current time in the specified timezone/country. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - boolean isInvalidTz = false; - final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); - final String response; + /** + * Responds with the current time in the specified timezone/country. + * + * @param bot The bot's instance. + * @param sender The sender. + * @param args The command arguments. + * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + boolean isInvalidTz = false; + final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); + final String response; - if (tz != null) - { - if (tz.equals(BEATS_KEYWORD)) - { - response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); - } - else - { - TIME_SDF.setTimeZone(TimeZone.getTimeZone(tz)); - response = TIME_SDF.format(Calendar.getInstance().getTime()) + tz.substring(tz.indexOf('/') + 1) - .replace('_', ' '); - } - } - else - { - isInvalidTz = true; - response = "The supported time zones are: " + COUNTRIES_MAP.keySet().toString(); - } + if (tz != null) { + if (tz.equals(BEATS_KEYWORD)) { + response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); + } else { + TIME_SDF.setTimeZone(TimeZone.getTimeZone(tz)); + response = TIME_SDF.format(Calendar.getInstance().getTime()) + tz.substring(tz.indexOf('/') + 1) + .replace('_', ' '); + } + } else { + isInvalidTz = true; + response = "The supported time zones are: " + COUNTRIES_MAP.keySet().toString(); + } - if (isPrivate) - { - bot.send(sender, response, true); - } - else - { - if (isInvalidTz) - { - bot.send(sender, response); - } - else - { - bot.send(bot.getChannel(), response); - } - } - } + if (isPrivate) { + bot.send(sender, response, true); + } else { + if (isInvalidTz) { + bot.send(sender, response); + } else { + bot.send(bot.getChannel(), response); + } + } + } - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) - { - bot.send(sender, "To display a country's current date/time:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To display a country's current date/time:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); - bot.send(sender, "For a listing of the supported countries:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD)); - } + bot.send(sender, "For a listing of the supported countries:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD)); + } - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - private String internetTime() - { - final Calendar gc = Calendar.getInstance(); + /** + * Returns the current Internet (beat) Time. + * + * @return The Internet Time string. + */ + private String internetTime() { + final Calendar gc = Calendar.getInstance(); - final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); - int hh = gc.get(Calendar.HOUR_OF_DAY); - final int mm = gc.get(Calendar.MINUTE); - final int ss = gc.get(Calendar.SECOND); + final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); + int hh = gc.get(Calendar.HOUR_OF_DAY); + final int mm = gc.get(Calendar.MINUTE); + final int ss = gc.get(Calendar.SECOND); - hh -= offset; // GMT - hh += 1; // BMT + hh -= offset; // GMT + hh += 1; // BMT - long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); + long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); - if (beats >= 1000) - { - beats -= (long) 1000; - } - else if (beats < 0) - { - beats += (long) 1000; - } + if (beats >= 1000) { + beats -= (long) 1000; + } else if (beats < 0) { + beats += (long) 1000; + } - if (beats < 10) - { - return ("@00" + beats); - } - else if (beats < 100) - { - return ("@0" + beats); - } + if (beats < 10) { + return ("@00" + beats); + } else if (beats < 100) { + return ("@0" + beats); + } - return ('@' + String.valueOf(beats)); - } + return ('@' + String.valueOf(beats)); + } - @Override - public boolean isPrivateMsgEnabled() - { - return true; - } + @Override + public boolean isPrivateMsgEnabled() { + return true; + } } \ No newline at end of file diff --git a/version.properties b/version.properties index 93eb253..e552373 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=007 +version.buildmeta=008 From 89f64cd9f52b4c3cd153f15020a1ba60b19506bf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 17 Jul 2016 02:35:05 -0700 Subject: [PATCH 053/842] Extensive javadoc cleanup. Started to convert to the java.time --- build.gradle | 2 + .../net/thauvin/erik/mobibot/ReleaseInfo.java | 2 +- .../net/thauvin/erik/mobibot/Commands.java | 4 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 2 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 8 +- .../thauvin/erik/mobibot/EntryComment.java | 10 +- .../net/thauvin/erik/mobibot/EntryLink.java | 86 +++++------ .../net/thauvin/erik/mobibot/FeedReader.java | 22 +-- .../net/thauvin/erik/mobibot/Mobibot.java | 143 +++++------------- .../java/net/thauvin/erik/mobibot/Tell.java | 42 ++--- .../net/thauvin/erik/mobibot/TellMessage.java | 24 +-- .../thauvin/erik/mobibot/TellMessagesMgr.java | 15 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 4 +- .../java/net/thauvin/erik/mobibot/Utils.java | 69 ++++++--- .../erik/mobibot/modules/AbstractModule.java | 4 +- .../thauvin/erik/mobibot/modules/Calc.java | 4 +- .../mobibot/modules/CurrencyConverter.java | 18 +-- .../thauvin/erik/mobibot/modules/Dice.java | 4 +- .../erik/mobibot/modules/GoogleSearch.java | 25 +-- .../thauvin/erik/mobibot/modules/Joke.java | 8 +- .../thauvin/erik/mobibot/modules/Lookup.java | 10 +- .../thauvin/erik/mobibot/modules/Ping.java | 12 +- .../erik/mobibot/modules/StockQuote.java | 8 +- .../thauvin/erik/mobibot/modules/Twitter.java | 13 +- .../net/thauvin/erik/mobibot/modules/War.java | 14 +- .../thauvin/erik/mobibot/modules/Weather.java | 16 +- .../erik/mobibot/modules/WorldTime.java | 62 +++----- 27 files changed, 252 insertions(+), 379 deletions(-) diff --git a/build.gradle b/build.gradle index b58a31c..1c4552c 100644 --- a/build.gradle +++ b/build.gradle @@ -87,6 +87,8 @@ compileJava { javadoc { options.tags = ['created'] + options.author = true + options.links('http://www.jibble.org/javadocs/pircbot/', 'http://docs.oracle.com/javase/8/docs/api/') } jar { diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 65a0955..28f2e24 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,7 +14,7 @@ import java.util.Date; */ public final class ReleaseInfo { private final static String buildmeta = "008"; - private final static Date date = new Date(1468718052505L); + private final static Date date = new Date(1468747036593L); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 4ec43ba..5a41e43 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -34,11 +34,11 @@ package net.thauvin.erik.mobibot; /** * The <code>commands</code>, <code>keywords</code> and <code>arguments</code>. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ -final class Commands { +final public class Commands { /** * The add (back)log command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 181a2f4..401b049 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -38,7 +38,7 @@ import javax.swing.*; /** * The class to handle posts to del.icio.us. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Mar 5, 2005 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index daa8890..77241e8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -44,7 +44,7 @@ import java.util.List; /** * Manages the feed entries. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ @@ -64,9 +64,7 @@ final class EntriesMgr { */ public static final String XML_EXT = ".xml"; - /** - * The maximum number of backlogs to keep. - */ + // The maximum number of backlogs to keep. private static final int MAX_BACKLOGS = 10; /** @@ -129,7 +127,7 @@ final class EntriesMgr { try (InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) { final SyndFeed feed = input.build(reader); - today = Utils.ISO_SDF.format(feed.getPublishedDate()); + today = Utils.isoLocalDate(feed.getPublishedDate()); final List items = feed.getEntries(); SyndEntry item; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index 826deac..f8aaee1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -38,19 +38,15 @@ import java.util.Date; /** * The class used to store comments associated to a specific entry. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ public class EntryComment implements Serializable { - /** - * The serial version UID. - */ + // The serial version UID. static final long serialVersionUID = 6957415292233553224L; - /** - * The creation date. - */ + // The creation date. private final Date date = Calendar.getInstance().getTime(); private String comment = ""; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 2be2d11..81fe20e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -43,14 +43,12 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * The class used to store link entries. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ public class EntryLink implements Serializable { - /** - * The serial version UID. - */ + // The serial version UID. static final long serialVersionUID = 3676245542270899086L; // The link's comments @@ -282,10 +280,44 @@ public class EntryLink implements Serializable { /** * Sets the tags. * - * @param tags The tags. + * @param tags The space-delimited tags. */ - private void setTags(final List<SyndCategory> tags) { - this.tags.addAll(tags); + public final void setTags(final String tags) { + if (tags != null) { + final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); + + SyndCategoryImpl tag; + String part; + char mod; + + for (final String rawPart : parts) { + part = rawPart.trim(); + + if (part.length() >= 2) { + tag = new SyndCategoryImpl(); + tag.setName(part.substring(1).toLowerCase()); + + mod = part.charAt(0); + + if (mod == '-') { + // Don't remove the channel tag, if any. + if (!tag.getName().equals(channel.substring(1))) { + this.tags.remove(tag); + } + } else if (mod == '+') { + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } else { + tag.setName(part.trim().toLowerCase()); + + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } + } + } + } } /** @@ -340,44 +372,10 @@ public class EntryLink implements Serializable { /** * Sets the tags. * - * @param tags The space-delimited tags. + * @param tags The tags. */ - public final void setTags(final String tags) { - if (tags != null) { - final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); - - SyndCategoryImpl tag; - String part; - char mod; - - for (final String rawPart : parts) { - part = rawPart.trim(); - - if (part.length() >= 2) { - tag = new SyndCategoryImpl(); - tag.setName(part.substring(1).toLowerCase()); - - mod = part.charAt(0); - - if (mod == '-') { - // Don't remove the channel tag, if any. - if (!tag.getName().equals(channel.substring(1))) { - this.tags.remove(tag); - } - } else if (mod == '+') { - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } else { - tag.setName(part.trim().toLowerCase()); - - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } - } - } - } + private void setTags(final List<SyndCategory> tags) { + this.tags.addAll(tags); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index ee8b4a5..92808e4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -44,34 +44,24 @@ import java.util.List; /** * Reads a RSS feed. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 1, 2004 * @since 1.0 */ class FeedReader implements Runnable { - /** - * The maximum number of feed items to display. - */ + // The maximum number of feed items to display. private static final int MAX_ITEMS = 5; - /** - * The tab indent (4 spaces). - */ + // The tab indent (4 spaces). private static final String TAB_INDENT = " "; - /** - * The bot. - */ + // The bot. private final Mobibot bot; - /** - * The nick of the person who sent the message. - */ + // The nick of the person who sent the message. private final String sender; - /** - * The URL to fetch. - */ + // The URL to fetch. private final String url; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 4eb851d..e2b0e74 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -43,12 +43,14 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.*; +import java.time.Clock; +import java.time.LocalDateTime; import java.util.*; /** * Implements the #mobitopia bot. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ @@ -59,72 +61,48 @@ public class Mobibot extends PircBot { */ public static final int CONNECT_TIMEOUT = 5000; - /** - * The empty title string. - */ + // The empty title string. static final String NO_TITLE = "No Title"; - /** - * The default port. - */ + // The default port. private static final int DEFAULT_PORT = 6667; - /** - * The info strings. - */ + // The info strings. private static final String[] INFO_STRS = { ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + " by Erik C. Thauvin (erik@thauvin.net)", "http://www.mobitopia.org/mobibot/" }; - /** - * The link match string. - */ + // The link match string. private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - /** - * The default maximum number of entries to display. - */ + // The default maximum number of entries to display. private static final int MAX_ENTRIES = 8; - /** - * The default maximum recap entries. - */ + // The default maximum recap entries. private static final int MAX_RECAP = 10; - /** - * The maximum number of times the bot will try to reconnect, if disconnected. - */ + // The maximum number of times the bot will try to reconnect, if disconnected. private static final int MAX_RECONNECT = 10; - /** - * The number of milliseconds to delay between consecutive messages. - */ + // The number of milliseconds to delay between consecutive messages. private static final long MESSAGE_DELAY = 1000L; - /** - * The modules. - */ + // The modules. private static final List<AbstractModule> MODULES = new ArrayList<>(0); - /** - * The start time. - */ + // The start time. private static final long START_TIME = System.currentTimeMillis(); - /** - * The tags/categories marker. - */ + // The tags/categories marker. private static final String TAGS_MARKER = "tags:"; - /** - * The version strings. - */ + // The version strings. private static final String[] VERSION_STRS = { "Version: " + ReleaseInfo.getVersion() + " (" - + Utils.ISO_SDF.format(ReleaseInfo.getBuildDate()) + ')', + + Utils.isoLocalDate(ReleaseInfo.getBuildDate()) + ')', "Platform: " + System.getProperty("os.name") + " (" @@ -147,109 +125,67 @@ public class Mobibot extends PircBot { + ')' }; - /** - * The tell object. - */ + // The tell object. private static Tell tell; - /** - * The main channel. - */ + // The main channel. private final String channel; - /** - * The commands list. - */ + // The commands list. private final List<String> commandsList = new ArrayList<>(); - /** - * The entries array. - */ + // The entries array. private final List<EntryLink> entries = new ArrayList<>(0); - /** - * The history/backlogs array. - */ + // The history/backlogs array. private final List<String> history = new ArrayList<>(0); - /** - * The ignored nicks array. - */ + // The ignored nicks array. private final List<String> ignoredNicks = new ArrayList<>(0); - /** - * The IRC port. - */ + // The IRC port. private final int ircPort; - /** - * The IRC server. - */ + // The IRC server. private final String ircServer; - /** - * The logger. - */ + // The logger. private final Log4JLogger logger = new Log4JLogger(Mobibot.class.getPackage().getName()); - /** - * The logger default level. - */ + // The logger default level. private final Level loggerLevel; - /** - * The log directory. - */ + // The log directory. private final String logsDir; - /** - * The recap array. - */ + // The recap array. private final List<String> recap = new ArrayList<>(0); - /** - * The backlogs URL. - */ + // The backlogs URL. private String backLogsUrl = ""; - /** - * The default tags/categories. - */ + // The default tags/categories. private String defaultTags = ""; - /** - * The del.icio.us posts handler. - */ + // The del.icio.us posts handler. private DeliciousPoster delicious = null; - /** - * The feed URL. - */ + // The feed URL. private String feedURL = ""; - /** - * The ident message. - */ + // The ident message. private String identMsg = ""; - /** - * The ident nick. - */ + // The ident nick. private String identNick = ""; - /** - * The NickServ ident password. - */ + // The NickServ ident password. private String identPwd = ""; - /** - * Today's date. - */ + // Today's date. private String today = Utils.today(); - /** - * The weblog URL. - */ + // The weblog URL. private String weblogUrl = ""; /** @@ -280,9 +216,6 @@ public class Mobibot extends PircBot { // Set the logger level loggerLevel = logger.getLogger().getLevel(); - // Initialization - Utils.UTC_SDF.setTimeZone(TimeZone.getTimeZone("UTC")); - // Load the current entries, if any. try { today = EntriesMgr.loadEntries(this.logsDir + EntriesMgr.CURRENT_XML, this.channel, entries); @@ -1600,7 +1533,7 @@ public class Mobibot extends PircBot { * @param isAction Set to <code>true</code> if the message is an action. */ private void storeRecap(final String sender, final String message, final boolean isAction) { - recap.add(Utils.UTC_SDF.format(Calendar.getInstance().getTime()) + " -> " + sender + (isAction ? " " : ": ") + recap.add(Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + " -> " + sender + (isAction ? " " : ": ") + message); if (recap.size() > MAX_RECAP) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index fd8858c..b34a47a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -37,7 +37,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * The <code>Tell</code> command. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-02 * @since 1.0 */ @@ -57,44 +57,28 @@ public class Tell { */ public static final String TELL_DEL_KEYWORD = "del"; - /** - * The default maximum number of days to keep messages. - */ + // The default maximum number of days to keep messages. private static final int DEFAULT_TELL_MAX_DAYS = 7; - /** - * The default message max queue size. - */ + // The default message max queue size. private static final int DEFAULT_TELL_MAX_SIZE = 50; - /** - * The serialized object file extension. - */ + // The serialized object file extension. private static final String SER_EXT = ".ser"; - /** - * The bot instance. - */ + // The bot instance. final private Mobibot bot; - /** - * The maximum number of days to keep messages. - */ + // The maximum number of days to keep messages. final private int maxDays; - /** - * The message maximum queue size. - */ + // The message maximum queue size. final private int maxSize; - /** - * The messages queue. - */ + // The messages queue. private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); - /** - * The serialized object file. - */ + // The serialized object file. private final String serializedObject; public Tell(final Mobibot bot, final String maxDays, final String maxSize) { @@ -114,7 +98,7 @@ public class Tell { /** * Cleans the messages queue. * - * @return <code>True</code> if the queue was cleaned. + * @return <code>true</code> if the queue was cleaned. */ private boolean clean() { if (bot.getLogger().isDebugEnabled()) { @@ -182,14 +166,14 @@ public class Tell { if (message.isReceived()) { bot.send(sender, Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " + message.getId() + ", DELIVERED]", true); } else { bot.send(sender, Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " + message.getId() + ", QUEUED]", true); } @@ -326,7 +310,7 @@ public class Tell { "Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + Utils.bold(message.getRecipient()) + " on " - + Utils.UTC_SDF.format(message.getReceived()), + + Utils.utcDateTime(message.getReceived()), true); message.setIsNotified(); diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index dc64bc0..db54230 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -32,26 +32,27 @@ package net.thauvin.erik.mobibot; import java.io.Serializable; -import java.util.Calendar; -import java.util.Date; +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; /** * The <code>TellMessage</code> class. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-24 * @since 1.0 */ public class TellMessage implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; private final String id; private final String message; - final private Date queued; + final private LocalDateTime queued; private final String recipient; private final String sender; private boolean isNotified; private boolean isReceived; - private Date received; + private LocalDateTime received; /** * Create a new message. @@ -65,9 +66,8 @@ public class TellMessage implements Serializable { this.recipient = recipient; this.message = message; - this.queued = Calendar.getInstance().getTime(); - this.id = Utils.TIMESTAMP_SDF.format(this.queued); - + this.queued = LocalDateTime.now(Clock.systemUTC()); + this.id = this.queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); } /** @@ -93,7 +93,7 @@ public class TellMessage implements Serializable { * * @return <code>true</code> if the message is queued. */ - public Date getQueued() { + public LocalDateTime getQueued() { return queued; } @@ -102,7 +102,7 @@ public class TellMessage implements Serializable { * * @return <code>true</code> if the message has been received. */ - public Date getReceived() { + public LocalDateTime getReceived() { return received; } @@ -173,7 +173,7 @@ public class TellMessage implements Serializable { * Sets the received flag. */ public void setIsReceived() { - received = Calendar.getInstance().getTime(); + received = LocalDateTime.now(Clock.systemUTC()); isReceived = true; } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 37e0346..d170e37 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -34,15 +34,15 @@ package net.thauvin.erik.mobibot; import org.apache.commons.logging.impl.Log4JLogger; import java.io.*; +import java.time.Clock; +import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; import java.util.List; /** * The Tell Messages Manager. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ @@ -65,15 +65,12 @@ final class TellMessagesMgr { * @return <code>True</code> if the queue was cleaned. */ public static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) { - final Calendar maxDate = Calendar.getInstance(); - final Date today = new Date(); + final LocalDateTime today = LocalDateTime.now(Clock.systemUTC()); boolean cleaned = false; for (final TellMessage message : tellMessages) { - maxDate.setTime(message.getQueued()); - maxDate.add(Calendar.DATE, tellMaxDays); - - if (maxDate.getTime().before(today)) { + final LocalDateTime maxDate = message.getQueued().plusDays(tellMaxDays); + if (maxDate.isBefore(today)) { tellMessages.remove(message); cleaned = true; } diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 5f3b492..dcaa667 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -21,8 +21,8 @@ import java.io.InputStreamReader; * </p> * and follow the prompts/instructions. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> - * @author <a href="http://twitter4j.org/en/code-examples.html#oauth">http://twitter4j.org/en/code-examples.html#oauth</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://twitter4j.org/en/code-examples.html#oauth" target="_blank">Twitter4J</a> * @created Sep 13, 2010 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 99586fc..903dec7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -34,30 +34,19 @@ package net.thauvin.erik.mobibot; import org.jibble.pircbot.Colors; import java.io.File; -import java.text.SimpleDateFormat; -import java.util.Calendar; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; /** * Miscellaneous utilities class. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ final public class Utils { - /** - * The ISO (YYYY-MM-DD) simple date format. - */ - public static final SimpleDateFormat ISO_SDF = new SimpleDateFormat("yyyy-MM-dd"); - /** - * The timestamp simple date format. - */ - public static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMddHHmmss"); - /** - * The UTC (yyyy-MM-dd HH:mm) simple date format. - */ - public static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - /** * Disables the default constructor. * @@ -225,12 +214,33 @@ final public class Utils { return false; } + /** + * Returns the specified date as an ISO local date string. + * + * @param date The date. + * @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format. + */ + public static String isoLocalDate(final Date date) { + return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); + } + + /** + * Returns the specified date as an ISO local date string. + * + * @param date The date. + * @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format. + */ + public static String isoLocalDate(final LocalDateTime date) { + return date.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + /** * Returns the plural form of a word, if count > 1. * * @param count The count. * @param word The word. * @param plural The plural word. + * @return The plural string. */ public static String plural(final long count, final String word, final String plural) { if (count > 1) { @@ -253,10 +263,10 @@ final public class Utils { /** * Returns today's date. * - * @return Today's date in {@link #ISO_SDF ISO} format. + * @return Today's date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format. */ public static String today() { - return ISO_SDF.format(Calendar.getInstance().getTime()); + return isoLocalDate(LocalDateTime.now()); } /** @@ -275,4 +285,25 @@ final public class Utils { return s; } -} + + /** + * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code> + * + * @param date The date. + * @return The fromatted date. + */ + public static String utcDateTime(final Date date) { + return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); + } + + /** + * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code> + * + * @param date The date. + * @return The fromatted date. + */ + public static String utcDateTime(final LocalDateTime date) { + return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + } + +} \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 678ac99..46b73d2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -39,7 +39,7 @@ import java.util.*; /** * The <code>Module</code> abstract class. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-01 * @since 1.0 */ @@ -110,7 +110,7 @@ public abstract class AbstractModule { } /** - * Returns <codde>true</codde> if the module responds to private messages. + * Returns <code>true</code> if the module responds to private messages. * * @return <code>true</code> or <code>false</code> */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 233ae79..0cd95b8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -41,7 +41,7 @@ import java.text.DecimalFormat; /** * The Calc module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-01 * @since 1.0 */ @@ -49,7 +49,7 @@ public class Calc extends AbstractModule { /** * The Calc command. */ - private static final String CALC_CMD = "calc"; + public static final String CALC_CMD = "calc"; /** * The default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index e66b343..d393361 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -50,7 +50,7 @@ import java.util.TreeMap; /** * The CurrentConverter module. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 11, 2004 * @since 1.0 */ @@ -58,26 +58,20 @@ final public class CurrencyConverter extends AbstractModule { /** * The currency command. */ - private static final String CURRENCY_CMD = "currency"; + public static final String CURRENCY_CMD = "currency"; /** * The rates keyword. */ - private static final String CURRENCY_RATES_KEYWORD = "rates"; + public static final String CURRENCY_RATES_KEYWORD = "rates"; - /** - * The exchange rates. - */ + // The exchange rates. private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); - /** - * The exchange rates table URL. - */ + // The exchange rates table URL. private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; - /** - * The last exchange rates table publication date. - */ + // The last exchange rates table publication date. private static String pubDate = ""; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index b13c989..b80d1e0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -39,7 +39,7 @@ import java.util.Random; /** * The Dice module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ @@ -47,7 +47,7 @@ final public class Dice extends AbstractModule { /** * The dice command. */ - private final String DICE_CMD = "dice"; + public static final String DICE_CMD = "dice"; /** * The default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 85f2bec..b30b796 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -45,29 +45,22 @@ import java.net.URLEncoder; /** * The GoogleSearch module. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 7, 2004 * @since 1.0 */ final public class GoogleSearch extends AbstractModule { - /** - * The Google API Key property. - */ - private static final String GOOGLE_API_KEY_PROP = "google-api-key"; - /** * The google command. */ - private static final String GOOGLE_CMD = "google"; + public static final String GOOGLE_CMD = "google"; - /** - * The Google Custom Search Engine ID property. - */ + // The Google API Key property. + private static final String GOOGLE_API_KEY_PROP = "google-api-key"; + // The Google Custom Search Engine ID property. private static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; - /** - * The tab indent (4 spaces). - */ + // The tab indent (4 spaces). private static final String TAB_INDENT = " "; /** @@ -82,11 +75,7 @@ final public class GoogleSearch extends AbstractModule { @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (isEnabled() && args.length() > 0) { - if (args.length() > 0) { - new Thread(() -> run(bot, sender, args)).start(); - } else { - helpResponse(bot, sender, args, isPrivate); - } + new Thread(() -> run(bot, sender, args)).start(); } else { helpResponse(bot, sender, args, isPrivate); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index d945f85..cdbcdaf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -43,7 +43,7 @@ import java.net.URLConnection; /** * The Joke module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-20 * @since 1.0 */ @@ -51,11 +51,9 @@ final public class Joke extends AbstractModule { /** * The joke command. */ - private static final String JOKE_CMD = "joke"; + public static final String JOKE_CMD = "joke"; - /** - * The ICNDB URL. - */ + // The ICNDB URL. private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index cf67caa..3712111 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -41,19 +41,17 @@ import java.net.UnknownHostException; /** * The Lookup module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ final public class Lookup extends AbstractModule { /** - * THe lookup command. + * The lookup command. */ - private static final String LOOKUP_CMD = "lookup"; + public static final String LOOKUP_CMD = "lookup"; - /** - * The whois host. - */ + // The whois host. private static final String WHOIS_HOST = "whois.arin.net"; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index dad186e..46a4af1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -40,14 +40,17 @@ import java.util.Random; /** * The Ping module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-02 * @since 1.0 */ public class Ping extends AbstractModule { /** - * The ping responses. + * The ping command. */ + public static final String PING_CMD = "ping"; + + // The ping responses. private static final List<String> PINGS = Arrays.asList( "is barely alive.", @@ -63,11 +66,6 @@ public class Ping extends AbstractModule { "is saving energy: apathetic mode activated.", "is busy. Go away!"); - /** - * The ping command. - */ - private static final String PING_CMD = "ping"; - /** * The default constructor. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 6d4096a..f8cffbe 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -41,7 +41,7 @@ import java.io.IOException; /** * The StockQuote module. * - * @author Erik C. Thauvin + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 7, 2004 * @since 1.0 */ @@ -49,11 +49,9 @@ final public class StockQuote extends AbstractModule { /** * The quote command. */ - private static final String STOCK_CMD = "stock"; + public static final String STOCK_CMD = "stock"; - /** - * The Yahoo! stock quote URL. - */ + // The Yahoo! stock quote URL. private static final String YAHOO_URL = "http://finance.yahoo.com/d/quotes.csv?&f=snl1d1t1c1oghv&e=.csv&s="; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 05e41d7..4a6f0cc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -39,21 +39,22 @@ import twitter4j.conf.ConfigurationBuilder; /** * The Twitter module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Sept 10, 2008 * @since 1.0 */ final public class Twitter extends AbstractModule { + /** + * The twitter command. + */ + public final static String TWITTER_CMD = "twitter"; + + // The property keys. private final static String CONSUMER_KEY_PROP = "twitter-consumerKey"; private final static String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; private final static String TOKEN_PROP = "twitter-token"; private final static String TOKEN_SECRET_PROP = "twitter-tokenSecret"; - /** - * The twitter command. - */ - private final static String TWITTER_CMD = "twitter"; - /** * Creates a new {@link Twitter} instance. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index ab07d74..e13cc2c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -39,25 +39,21 @@ import java.util.Random; /** * The War module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ final public class War extends AbstractModule { /** - * The war command. + * The war command */ - private static final String WAR_CMD = "war"; + public static final String WAR_CMD = "war"; - /** - * The deck of card. - */ + // The deck of card. private static final String[] WAR_DECK = new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; - /** - * The suits for the deck of card. - */ + // The suits for the deck of card. private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java index 2611d2f..1e55805 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java @@ -49,20 +49,16 @@ import java.util.Date; */ final public class Weather extends AbstractModule { /** - * The decimal number format. + * The weather command. */ + public static final String WEATHER_CMD = "weather"; + + // The decimal number format. private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("0.##"); - /** - * The URL where the stations are listed. - */ + // The URL where the stations are listed. private static final String STATIONS_URL = "http://www.rap.ucar.edu/weather/surface/stations.txt"; - /** - * THe weather command. - */ - private static final String WEATHER_CMD = "weather"; - /** * Creates a new {@link Weather} instance. */ @@ -101,7 +97,7 @@ final public class Weather extends AbstractModule { bot.send(sender, "At: " - + Utils.UTC_SDF.format(metar.getDate()) + + Utils.utcDateTime(metar.getDate()) + " UTC (" + (((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) + " minutes ago)", diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 87263d4..746382e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -34,6 +34,9 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; import java.util.Calendar; import java.util.Map; import java.util.TimeZone; @@ -42,29 +45,23 @@ import java.util.TreeMap; /** * The WorldTime module. * - * @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a> + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-27 * @since 1.0 */ final public class WorldTime extends AbstractModule { - /** - * The beats (Internet Time) keyword. - */ - private static final String BEATS_KEYWORD = ".beats"; - - /** - * The supported countries. - */ - private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); - /** * The time command. */ - private static final String TIME_CMD = "time"; + public static final String TIME_CMD = "time"; - /** - * The date/time format. - */ + // The beats (Internet Time) keyword. + private static final String BEATS_KEYWORD = ".beats"; + + // The supported countries. + private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); + + // The date/time format. private static final SimpleDateFormat TIME_SDF = new SimpleDateFormat("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"); @@ -136,7 +133,7 @@ final public class WorldTime extends AbstractModule { COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); for (final String tz : TimeZone.getAvailableIDs()) { - if (!tz.contains("/") && tz.length() == 3 & !COUNTRIES_MAP.containsKey(tz)) { + if (!tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)) { COUNTRIES_MAP.put(tz, tz); } } @@ -161,8 +158,8 @@ final public class WorldTime extends AbstractModule { response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); } else { TIME_SDF.setTimeZone(TimeZone.getTimeZone(tz)); - response = TIME_SDF.format(Calendar.getInstance().getTime()) + tz.substring(tz.indexOf('/') + 1) - .replace('_', ' '); + response = TIME_SDF.format(Calendar.getInstance().getTime()) + + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); } } else { isInvalidTz = true; @@ -195,31 +192,10 @@ final public class WorldTime extends AbstractModule { * @return The Internet Time string. */ private String internetTime() { - final Calendar gc = Calendar.getInstance(); - - final int offset = (gc.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000)); - int hh = gc.get(Calendar.HOUR_OF_DAY); - final int mm = gc.get(Calendar.MINUTE); - final int ss = gc.get(Calendar.SECOND); - - hh -= offset; // GMT - hh += 1; // BMT - - long beats = Math.round(Math.floor((double) ((((hh * 3600) + (mm * 60) + ss) * 1000) / 86400))); - - if (beats >= 1000) { - beats -= (long) 1000; - } else if (beats < 0) { - beats += (long) 1000; - } - - if (beats < 10) { - return ("@00" + beats); - } else if (beats < 100) { - return ("@0" + beats); - } - - return ('@' + String.valueOf(beats)); + final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); + final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) + + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); + return String.format("%c%03d", '@', beats); } @Override From 88115b6ee8b878f1a359659ff56128400d69a8d8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 17 Jul 2016 14:11:13 -0700 Subject: [PATCH 054/842] Finished convertion to java.time --- build.gradle | 4 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 9 +- .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- .../erik/mobibot/modules/WorldTime.java | 44 +++-- version.properties | 2 +- version.vm | 173 ++++++++++++++++++ 6 files changed, 208 insertions(+), 26 deletions(-) create mode 100644 version.vm diff --git a/build.gradle b/build.gradle index 1c4552c..f136ac2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" - id "com.github.ben-manes.versions" version "0.12.0" + id "com.github.ben-manes.versions" version "0.13.0" } apply plugin: 'java' @@ -140,4 +140,4 @@ task release(dependsOn: ['wrapper', 'clean', 'deploy']) << { group = 'Publishing' description = 'Releases new version.' isRelease = true -} +} \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 28f2e24..05fdfa2 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -4,7 +4,7 @@ */ package net.thauvin.erik.mobibot; -import java.util.Date; +import java.time.*; /** * Provides semantic version information. @@ -13,8 +13,9 @@ import java.util.Date; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "008"; - private final static Date date = new Date(1468747036593L); + private final static String buildmeta = "009"; + private final static LocalDateTime date = + LocalDateTime.ofInstant(Instant.ofEpochMilli(1468789269982L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; @@ -36,7 +37,7 @@ public final class ReleaseInfo { * * @return The build date. */ - public static Date getBuildDate() { + public static LocalDateTime getBuildDate() { return date; } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index e2b0e74..bdaa024 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1042,7 +1042,7 @@ public class Mobibot extends PircBot { } } else { final EntryLink entry = entries.get(dupIndex); - send(sender, "Duplicate >> " + Utils.buildLink(dupIndex, entry)); + send(sender, Utils.bold("Duplicate") + " >> " + Utils.buildLink(dupIndex, entry)); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 746382e..f5dc381 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -33,13 +33,11 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; -import java.text.SimpleDateFormat; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; -import java.util.Calendar; import java.util.Map; -import java.util.TimeZone; import java.util.TreeMap; /** @@ -61,10 +59,6 @@ final public class WorldTime extends AbstractModule { // The supported countries. private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); - // The date/time format. - private static final SimpleDateFormat TIME_SDF = - new SimpleDateFormat("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"); - /** * Creates a new {@link WorldTime} instance. */ @@ -72,8 +66,15 @@ final public class WorldTime extends AbstractModule { commands.add(TIME_CMD); // Initialize the countries map + COUNTRIES_MAP.put("AE", "Asia/Dubai"); + COUNTRIES_MAP.put("AF", "Asia/Kabul"); + COUNTRIES_MAP.put("AQ", "Antarctica/South_Pole"); + COUNTRIES_MAP.put("AT", "Europe/Vienna"); COUNTRIES_MAP.put("AU", "Australia/Sydney"); + COUNTRIES_MAP.put("AKST", "America/Anchorage"); + COUNTRIES_MAP.put("AKDT", "America/Anchorage"); COUNTRIES_MAP.put("BE", "Europe/Brussels"); + COUNTRIES_MAP.put("BR", "America/Sao_Paulo"); COUNTRIES_MAP.put("CA", "America/Montreal"); COUNTRIES_MAP.put("CDT", "America/Chicago"); COUNTRIES_MAP.put("CET", "CET"); @@ -92,19 +93,23 @@ final public class WorldTime extends AbstractModule { COUNTRIES_MAP.put("FR", "Europe/Paris"); COUNTRIES_MAP.put("GB", "Europe/London"); COUNTRIES_MAP.put("GMT", "GMT"); + COUNTRIES_MAP.put("GR", "Europe/Athens"); COUNTRIES_MAP.put("HK", "Asia/Hong_Kong"); - COUNTRIES_MAP.put("HST", "HST"); + COUNTRIES_MAP.put("HST", "Pacific/Honolulu"); COUNTRIES_MAP.put("IE", "Europe/Dublin"); COUNTRIES_MAP.put("IL", "Asia/Tel_Aviv"); - COUNTRIES_MAP.put("IN", "Asia/Calcutta"); + COUNTRIES_MAP.put("IN", "Asia/Kolkata"); + COUNTRIES_MAP.put("IQ", "Asia/Baghdad"); COUNTRIES_MAP.put("IR", "Asia/Tehran"); COUNTRIES_MAP.put("IS", "Atlantic/Reykjavik"); COUNTRIES_MAP.put("IT", "Europe/Rome"); COUNTRIES_MAP.put("JM", "Jamaica"); COUNTRIES_MAP.put("JP", "Asia/Tokyo"); COUNTRIES_MAP.put("LY", "Africa/Tripoli"); + COUNTRIES_MAP.put("MA", "Africa/Casablanca"); COUNTRIES_MAP.put("MDT", "America/Denver"); COUNTRIES_MAP.put("MH", "Kwajalein"); + COUNTRIES_MAP.put("MQ", "America/Martinique"); COUNTRIES_MAP.put("MST", "America/Denver"); COUNTRIES_MAP.put("MX", "America/Mexico_City"); COUNTRIES_MAP.put("NL", "Europe/Amsterdam"); @@ -112,31 +117,34 @@ final public class WorldTime extends AbstractModule { COUNTRIES_MAP.put("NP", "Asia/Katmandu"); COUNTRIES_MAP.put("NZ", "Pacific/Auckland"); COUNTRIES_MAP.put("PDT", "America/Los_Angeles"); + COUNTRIES_MAP.put("PH", "Asia/Manila"); COUNTRIES_MAP.put("PK", "Asia/Karachi"); COUNTRIES_MAP.put("PL", "Europe/Warsaw"); COUNTRIES_MAP.put("PST", "America/Los_Angeles"); COUNTRIES_MAP.put("PT", "Europe/Lisbon"); + COUNTRIES_MAP.put("PR", "America/Puerto_Rico"); COUNTRIES_MAP.put("RU", "Europe/Moscow"); COUNTRIES_MAP.put("SE", "Europe/Stockholm"); COUNTRIES_MAP.put("SG", "Asia/Singapore"); - COUNTRIES_MAP.put("SU", "Europe/Moscow"); COUNTRIES_MAP.put("TH", "Asia/Bangkok"); COUNTRIES_MAP.put("TM", "Asia/Ashgabat"); + COUNTRIES_MAP.put("TN", "Africa/Tunis"); COUNTRIES_MAP.put("TR", "Europe/Istanbul"); COUNTRIES_MAP.put("TW", "Asia/Taipei"); COUNTRIES_MAP.put("UK", "Europe/London"); COUNTRIES_MAP.put("US", "America/New_York"); COUNTRIES_MAP.put("UTC", "UTC"); COUNTRIES_MAP.put("VA", "Europe/Vatican"); + COUNTRIES_MAP.put("VE", "America/Caracas"); COUNTRIES_MAP.put("VN", "Asia/Ho_Chi_Minh"); + COUNTRIES_MAP.put("ZA", "Africa/Johannesburg"); + COUNTRIES_MAP.put("ZULU", "Zulu"); COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); - for (final String tz : TimeZone.getAvailableIDs()) { - if (!tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)) { - COUNTRIES_MAP.put(tz, tz); - } - } + ZoneId.getAvailableZoneIds().stream().filter( + tz -> !tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)).forEach( + tz -> COUNTRIES_MAP.put(tz, tz)); } /** @@ -157,13 +165,13 @@ final public class WorldTime extends AbstractModule { if (tz.equals(BEATS_KEYWORD)) { response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); } else { - TIME_SDF.setTimeZone(TimeZone.getTimeZone(tz)); - response = TIME_SDF.format(Calendar.getInstance().getTime()) + response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( + DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); } } else { isInvalidTz = true; - response = "The supported time zones are: " + COUNTRIES_MAP.keySet().toString(); + response = "The supported countries/zones are: " + COUNTRIES_MAP.keySet().toString(); } if (isPrivate) { diff --git a/version.properties b/version.properties index e552373..30dac58 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=008 +version.buildmeta=009 diff --git a/version.vm b/version.vm new file mode 100644 index 0000000..3921024 --- /dev/null +++ b/version.vm @@ -0,0 +1,173 @@ +############################################################################## +## +## Available variables: +## +## $packageName - The package name. (string) +## $className - The class name. (string) +## $project - The project name. (string) +## $epoch - The build epoch/unix time. (long) +## $major - The major version. (int) +## $minor - The minor version. (int) +## $patch - The patch version. (int) +## $prerelease - The pre-release version. (string) +## $buildmeta - The build metadata version. (string) +## +############################################################################## +/* + * This file is automatically generated. + * Do not modify! -- ALL CHANGES WILL BE ERASED! + */ +package ${packageName}; + +import java.time.*; + +/** + * Provides semantic version information. + * + * @author <a href="https://github.com/ethauvin/semver">Semantic Version + * Annotation Processor</a> + */ +public final class ${className} { + private final static String buildmeta = "${buildmeta}"; + private final static LocalDateTime date = + LocalDateTime.ofInstant(Instant.ofEpochMilli(${epoch}L), ZoneId.systemDefault()); + private final static int major = ${major}; + private final static int minor = ${minor}; + private final static int patch = ${patch}; + private final static String prerelease = "${prerelease}"; + private final static String project = "${project}"; + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private ${className}() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Returns the build date. + * + * @return The build date. + */ + public static LocalDateTime getBuildDate() { + return date; + } + + /** + * Returns the project name. + * + * @return The project name, if any. + */ + public static String getProject() { + return project; + } + + /** + * Returns the full version string. + * <p> + * Formatted as: + * <blockquote> + * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> + * </blockquote> + * <p> + * For example: + * <ul> + * <li><code>1.0.0</code></li> + * <li><code>1.0.0-beta</code></li> + * <li><code>1.0.0+20160124144700</code></li> + * <li><code>1.0.0-alpha+001</code></li> + * </ul> + * + * @return The version string. + */ + public static String getVersion() { + return Integer.toString(getMajor()) + '.' + + Integer.toString(getMinor()) + '.' + + Integer.toString(getPatch()) + + getPreRelease(true) + getBuildMetadata(true); + } + + /** + * Returns the major version. + * + * @return The major version. + */ + public static int getMajor() { + return major; + } + + /** + * Returns the minor version. + * + * @return The minor version. + */ + public static int getMinor() { + return minor; + } + + /** + * Returns the patch version. + * + * @return The patch version. + */ + public static int getPatch() { + return patch; + } + + /** + * Returns the pre-release version. + * + * @param isHyphen Prepend a hyphen, if <code>true</code>. + * @return The pre-release version, if any. + */ + public static String getPreRelease(final boolean isHyphen) { + if (prerelease.length() > 0) { + if (isHyphen) { + return '-' + prerelease; + } else { + return prerelease; + } + } + + return ""; + } + + /** + * Returns the pre-release version. + * + * @return The pre-release version, if any. + */ + public static String getPreRelease() { + return getPreRelease(false); + } + + /** + * Returns the build metadata. + * + * @param isPlus Prepend a plus sign, if <code>true</code>. + * @return The build metadata, if any. + */ + public static String getBuildMetadata(final boolean isPlus) { + if (buildmeta.length() > 0) { + if (isPlus) { + return '+' + buildmeta; + } else { + return buildmeta; + } + } + + return ""; + } + + /** + * Returns the build metadata. + * + * @return The build metadata, if any. + */ + public static String getBuildMetadata() { + return getBuildMetadata(false); + } +} \ No newline at end of file From 80302ce179e7dc43fca4d259e437e263c0cadfdb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 18 Jul 2016 19:34:23 -0700 Subject: [PATCH 055/842] More javadoc improvements. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 2 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 4 +- .../thauvin/erik/mobibot/EntryComment.java | 7 +- .../net/thauvin/erik/mobibot/EntryLink.java | 92 ++++++++++--------- .../net/thauvin/erik/mobibot/Mobibot.java | 18 ++++ .../thauvin/erik/mobibot/modules/Calc.java | 6 ++ .../mobibot/modules/CurrencyConverter.java | 6 ++ .../thauvin/erik/mobibot/modules/Dice.java | 6 ++ .../erik/mobibot/modules/GoogleSearch.java | 9 ++ .../thauvin/erik/mobibot/modules/Joke.java | 6 ++ .../thauvin/erik/mobibot/modules/Lookup.java | 3 + .../thauvin/erik/mobibot/modules/Ping.java | 6 ++ .../erik/mobibot/modules/StockQuote.java | 6 ++ .../thauvin/erik/mobibot/modules/Twitter.java | 9 ++ .../net/thauvin/erik/mobibot/modules/War.java | 3 + .../thauvin/erik/mobibot/modules/Weather.java | 9 ++ .../erik/mobibot/modules/WorldTime.java | 6 ++ 17 files changed, 150 insertions(+), 48 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 05fdfa2..f7d0fd9 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -15,7 +15,7 @@ import java.time.*; public final class ReleaseInfo { private final static String buildmeta = "009"; private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1468789269982L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1468895453561L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 77241e8..ad068ca 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -173,7 +173,9 @@ final class EntriesMgr { * @param history The history array. * @param isDayBackup Set the true if the daily backup file should also be created. */ - public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, + public static void saveEntries(final Mobibot bot, + final List<EntryLink> entries, + final List<String> history, final boolean isDayBackup) { if (bot.getLogger().isDebugEnabled()) { bot.getLogger().debug("Saving the feeds..."); diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index f8aaee1..4adf0d0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -32,8 +32,7 @@ package net.thauvin.erik.mobibot; import java.io.Serializable; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; /** * The class used to store comments associated to a specific entry. @@ -47,7 +46,7 @@ public class EntryComment implements Serializable { static final long serialVersionUID = 6957415292233553224L; // The creation date. - private final Date date = Calendar.getInstance().getTime(); + private final LocalDateTime date = LocalDateTime.now(); private String comment = ""; private String nick = ""; @@ -96,7 +95,7 @@ public class EntryComment implements Serializable { * @return The date. */ @SuppressWarnings("UnusedDeclaration") - public final Date getDate() { + public final LocalDateTime getDate() { return date; } diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 81fe20e..8dcd822 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -85,7 +85,11 @@ public class EntryLink implements Serializable { * @param channel The channel. * @param tags The entry's tags/categories. */ - public EntryLink(final String link, final String title, final String nick, final String login, final String channel, + public EntryLink(final String link, + final String title, + final String nick, + final String login, + final String channel, final String tags) { this.link = link; this.title = title; @@ -106,7 +110,11 @@ public class EntryLink implements Serializable { * @param date The entry date. * @param tags The entry's tags/categories. */ - public EntryLink(final String link, final String title, final String nick, final String channel, final Date date, + public EntryLink(final String link, + final String title, + final String nick, + final String channel, + final Date date, final List<SyndCategory> tags) { this.link = link; this.title = title; @@ -280,44 +288,10 @@ public class EntryLink implements Serializable { /** * Sets the tags. * - * @param tags The space-delimited tags. + * @param tags The tags. */ - public final void setTags(final String tags) { - if (tags != null) { - final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); - - SyndCategoryImpl tag; - String part; - char mod; - - for (final String rawPart : parts) { - part = rawPart.trim(); - - if (part.length() >= 2) { - tag = new SyndCategoryImpl(); - tag.setName(part.substring(1).toLowerCase()); - - mod = part.charAt(0); - - if (mod == '-') { - // Don't remove the channel tag, if any. - if (!tag.getName().equals(channel.substring(1))) { - this.tags.remove(tag); - } - } else if (mod == '+') { - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } else { - tag.setName(part.trim().toLowerCase()); - - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } - } - } - } + private void setTags(final List<SyndCategory> tags) { + this.tags.addAll(tags); } /** @@ -372,10 +346,44 @@ public class EntryLink implements Serializable { /** * Sets the tags. * - * @param tags The tags. + * @param tags The space-delimited tags. */ - private void setTags(final List<SyndCategory> tags) { - this.tags.addAll(tags); + public final void setTags(final String tags) { + if (tags != null) { + final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); + + SyndCategoryImpl tag; + String part; + char mod; + + for (final String rawPart : parts) { + part = rawPart.trim(); + + if (part.length() >= 2) { + tag = new SyndCategoryImpl(); + tag.setName(part.substring(1).toLowerCase()); + + mod = part.charAt(0); + + if (mod == '-') { + // Don't remove the channel tag, if any. + if (!tag.getName().equals(channel.substring(1))) { + this.tags.remove(tag); + } + } else if (mod == '+') { + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } else { + tag.setName(part.trim().toLowerCase()); + + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } + } + } + } } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index bdaa024..68515e0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -904,6 +904,9 @@ public class Mobibot extends PircBot { return false; } + /** + * {@inheritDoc} + */ @Override protected final void onAction(final String sender, final String login, @@ -915,6 +918,9 @@ public class Mobibot extends PircBot { } } + /** + * {@inheritDoc} + */ @Override protected final void onDisconnect() { if (Utils.isValidString(weblogUrl)) { @@ -956,11 +962,17 @@ public class Mobibot extends PircBot { joinChannel(channel); } + /** + * {@inheritDoc} + */ @Override protected void onJoin(final String channel, final String sender, final String login, final String hostname) { tell.send(sender); } + /** + * {@inheritDoc} + */ @Override protected final void onMessage(final String channel, final String sender, @@ -1298,11 +1310,17 @@ public class Mobibot extends PircBot { tell.send(sender, true); } + /** + * {@inheritDoc} + */ @Override protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { tell.send(newNick); } + /** + * {@inheritDoc} + */ @Override protected final void onPrivateMessage(final String sender, final String login, diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 0cd95b8..f9a0b30 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -58,6 +58,9 @@ public class Calc extends AbstractModule { commands.add(CALC_CMD); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (Utils.isValidString(args)) { @@ -79,6 +82,9 @@ public class Calc extends AbstractModule { } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To solve a mathematical calculation:"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index d393361..2a1f3de 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -81,6 +81,9 @@ final public class CurrencyConverter extends AbstractModule { commands.add(CURRENCY_CMD); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { synchronized (this) { @@ -92,6 +95,9 @@ final public class CurrencyConverter extends AbstractModule { new Thread(() -> run(bot, sender, args)).start(); } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To convert from one currency to another:"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index b80d1e0..8c300da 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -92,12 +92,18 @@ final public class Dice extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To roll the dice:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD)); } + /** + * {@inheritDoc} + */ @Override public boolean isEnabled() { return true; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index b30b796..4772b3d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -72,6 +72,9 @@ final public class GoogleSearch extends AbstractModule { properties.put(GOOGLE_CSE_KEY_PROP, ""); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (isEnabled() && args.length() > 0) { @@ -81,6 +84,9 @@ final public class GoogleSearch extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (isEnabled()) { @@ -91,6 +97,9 @@ final public class GoogleSearch extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public boolean isEnabled() { return isValidProperties(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index cdbcdaf..301f06f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -64,11 +64,17 @@ final public class Joke extends AbstractModule { commands.add(JOKE_CMD); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { new Thread(() -> run(bot, sender)).start(); } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To retrieve a random joke:"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 3712111..64460d8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -185,6 +185,9 @@ final public class Lookup extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To perform a DNS lookup query:"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 46a4af1..dd7d76d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -73,6 +73,9 @@ public class Ping extends AbstractModule { commands.add(PING_CMD); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { final Random r = new Random(); @@ -80,6 +83,9 @@ public class Ping extends AbstractModule { bot.action(PINGS.get(r.nextInt(PINGS.size()))); } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To ping the bot:"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index f8cffbe..ad716b3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -61,6 +61,9 @@ final public class StockQuote extends AbstractModule { commands.add(STOCK_CMD); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (args.length() > 0) { @@ -70,6 +73,9 @@ final public class StockQuote extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To retrieve a stock quote:"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 4a6f0cc..41dbdcb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -66,6 +66,9 @@ final public class Twitter extends AbstractModule { properties.put(TOKEN_SECRET_PROP, ""); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (isEnabled() && args.length() > 0) { @@ -75,6 +78,9 @@ final public class Twitter extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (isEnabled()) { @@ -85,6 +91,9 @@ final public class Twitter extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public boolean isEnabled() { return isValidProperties(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index e13cc2c..b0d7776 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -100,6 +100,9 @@ final public class War extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To play war:"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java index 1e55805..72f82df 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java @@ -66,11 +66,17 @@ final public class Weather extends AbstractModule { commands.add(WEATHER_CMD); } + /** + * {@inheritDoc} + */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To display weather information:"); @@ -78,6 +84,9 @@ final public class Weather extends AbstractModule { bot.send(sender, "For a listing of the ICAO station IDs, please visit: " + STATIONS_URL); } + /** + * {@inheritDoc} + */ @Override public boolean isPrivateMsgEnabled() { return true; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index f5dc381..142537d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -185,6 +185,9 @@ final public class WorldTime extends AbstractModule { } } + /** + * {@inheritDoc} + */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To display a country's current date/time:"); @@ -206,6 +209,9 @@ final public class WorldTime extends AbstractModule { return String.format("%c%03d", '@', beats); } + /** + * {@inheritDoc} + */ @Override public boolean isPrivateMsgEnabled() { return true; From cb24ebbfe26303c331be5b0b17673923ab7d4208 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 18:12:00 -0700 Subject: [PATCH 056/842] Updated to Kobalt 1.0.45 --- .gitattributes | 5 + .gitignore | 10 +- .idea/modules/mobibot.iml | 111 ++++++++++++++++++----- kobalt/src/Build.kt | 97 ++++++++++++++++++++ kobalt/wrapper/kobalt-wrapper.jar | Bin 0 -> 11230 bytes kobalt/wrapper/kobalt-wrapper.properties | 1 + kobaltw | 2 + kobaltw.bat | 4 + 8 files changed, 205 insertions(+), 25 deletions(-) create mode 100644 .gitattributes create mode 100644 kobalt/src/Build.kt create mode 100644 kobalt/wrapper/kobalt-wrapper.jar create mode 100644 kobalt/wrapper/kobalt-wrapper.properties create mode 100644 kobaltw create mode 100644 kobaltw.bat diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6ec2ae2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# batch files are specific to windows and always crlf +*.bat eol=crlf diff --git a/.gitignore b/.gitignore index 065b527..6549aef 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,10 @@ **/.idea/tasks.xml **/.idea/workspace.xml *.iws -.DS_Store .classpath +.DS_Store .gradle +.kobalt .nb-gradle .project .settings @@ -16,6 +17,7 @@ /dist /fetcher.properties /gen +/gradle.properties /local.properties /log4j.properties /logs @@ -23,8 +25,10 @@ /out /proguard-project.txt /project.properties +/target /test-output /weather.log CVS -Thumbs.db -ehthumbs.db \ No newline at end of file +ehthumbs.db +kobaltBuild +Thumbs.db \ No newline at end of file diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index c0dcd8b..7651f0b 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.0-beta+006" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="false"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.0-beta+009" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/main" /> <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> <exclude-output /> @@ -16,29 +16,96 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name="Gradle: log4j:log4j:1.2.17" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.6-beta" level="project" /> + <orderEntry type="module-library" scope="TEST"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library" scope="RUNTIME"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library" scope="PROVIDED"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> - <orderEntry type="library" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20160212" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> - <orderEntry type="library" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.5" level="project" /> - <orderEntry type="library" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.5" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.6.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160212" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.5" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.6.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160212" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.5" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.6.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160212" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> </component> </module> \ No newline at end of file diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt new file mode 100644 index 0000000..b054d5e --- /dev/null +++ b/kobalt/src/Build.kt @@ -0,0 +1,97 @@ +import com.beust.kobalt.* +import com.beust.kobalt.plugin.apt.* +import com.beust.kobalt.plugin.packaging.* +import com.beust.kobalt.plugin.application.* +import com.beust.kobalt.plugin.java.* +import java.io.File +import java.io.FileInputStream +import java.util.* + +val bs = buildScript { + repos() +} + +val mainClassName = "net.thauvin.erik.mobibot.Mobibot" + +fun StringBuilder.prepend(s: String): StringBuilder { + if (this.isNotEmpty()) { + this.insert(0, s) + } + return this +} + +val p = project { + + name = "mobibot" + + fun versionFor(): String { + val propsFile = "version.properties" + val majorKey = "version.major" + val minorKey = "version.minor" + val patchKey = "version.patch" + val metaKey = "version.buildmeta" + val preKey = "version.prerelease" + + val p = Properties().apply { FileInputStream(propsFile).use { fis -> load(fis) } } + + return (p.getProperty(majorKey, "1") + "." + p.getProperty(minorKey, "0") + "." + p.getProperty(patchKey, "0") + + StringBuilder(p.getProperty(preKey, "")).prepend("-") + + StringBuilder(p.getProperty(metaKey, "")).prepend("+")) + } + + version = versionFor() + + val processorJar = "net.thauvin.erik:semver:" + + dependencies { + compile("log4j:log4j:jar:1.2.17") + + compile("pircbot:pircbot:1.5.0") + + compile("commons-codec:commons-codec:1.10") + compile("commons-logging:commons-logging:1.2") + compile("commons-net:commons-net:3.5") + compile("commons-cli:commons-cli:1.3.1") + compile("commons-httpclient:commons-httpclient:3.1") + + compile("oro:oro:2.0.8") + + compile("org.jsoup:jsoup:1.9.2") + compile("com.rometools:rome:1.6.1") + compile("org.slf4j:slf4j-log4j12:1.7.21") + compile("org.json:json:20160212") + compile("org.ostermiller:utils:1.07.00") + + compile("net.sourceforge.jweather:jweather:jar:0.3.0") + compile("net.objecthunter:exp4j:0.4.7") + + compile("org.twitter4j:twitter4j-core:4.0.4") + compile("net.sf.delicious-java:delicious:1.14") + + apt(processorJar) + compile(processorJar) + } + + apt { + outputDir = "/src/generated/java/" + } + + assemble { + jar { + name = "${project.name}.jar" + fatJar = true + manifest { + attributes("Main-Class", mainClassName) + } + } + } + + application { + mainClass = mainClassName + args("-v") + } + + install { + libDir = "deploy" + } +} diff --git a/kobalt/wrapper/kobalt-wrapper.jar b/kobalt/wrapper/kobalt-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..696ac55f0e460946fd8f657732a41a26126faf84 GIT binary patch literal 11230 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 literal 0 HcmV?d00001 diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties new file mode 100644 index 0000000..708f458 --- /dev/null +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -0,0 +1 @@ +kobalt.version=1.0.45 \ No newline at end of file diff --git a/kobaltw b/kobaltw new file mode 100644 index 0000000..c5186d5 --- /dev/null +++ b/kobaltw @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $* diff --git a/kobaltw.bat b/kobaltw.bat new file mode 100644 index 0000000..d578071 --- /dev/null +++ b/kobaltw.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/kobalt/wrapper/kobalt-wrapper.jar" %* From 5e5f0d84d1de299e4c7b788f1bdf0359579ab72b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 18:13:10 -0700 Subject: [PATCH 057/842] Added OpenWeatherMap weather module. --- build.gradle | 20 +- lib/owm-japis-2.5.0.5.jar | Bin 0 -> 40271 bytes licenses/LICENSE.txt | 2 +- licenses/OWM JAPIs License.txt | 19 ++ mobibot.ipr | 30 +-- properties/mobibot.properties | 5 + .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../net/thauvin/erik/mobibot/Mobibot.java | 4 +- .../erik/mobibot/modules/Weather2.java | 172 ++++++++++++++++++ version.properties | 2 +- 10 files changed, 220 insertions(+), 38 deletions(-) create mode 100644 lib/owm-japis-2.5.0.5.jar create mode 100644 licenses/OWM JAPIs License.txt create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java diff --git a/build.gradle b/build.gradle index f136ac2..c4c58ab 100644 --- a/build.gradle +++ b/build.gradle @@ -60,18 +60,20 @@ dependencies { compile 'oro:oro:2.0.8' - compile 'org.jsoup:jsoup:1.9.2' - compile 'com.rometools:rome:1.6.1' + compile 'org.jsoup:jsoup:1.10.1' + compile 'com.rometools:rome:1.7.0' compile 'org.slf4j:slf4j-log4j12:1.7.21' - compile 'org.json:json:20160212' + compile 'org.json:json:20160810' compile 'org.ostermiller:utils:1.07.00' compile 'net.sourceforge.jweather:jweather:0.3.0@jar' compile 'net.objecthunter:exp4j:0.4.7' - compile 'org.twitter4j:twitter4j-core:4.0.4' + compile 'org.twitter4j:twitter4j-core:4.0.5' compile 'net.sf.delicious-java:delicious:1.14' + compile files('lib/owm-japis-2.5.0.5.jar') + compileOnly 'net.thauvin.erik:semver:0.9.6-beta' } @@ -136,8 +138,10 @@ task deploy(dependsOn: ['build']) { } -task release(dependsOn: ['wrapper', 'clean', 'deploy']) << { - group = 'Publishing' - description = 'Releases new version.' - isRelease = true +task release(dependsOn: ['wrapper', 'clean', 'deploy']) { + doLast { + group = 'Publishing' + description = 'Releases new version.' + isRelease = true + } } \ No newline at end of file diff --git a/lib/owm-japis-2.5.0.5.jar b/lib/owm-japis-2.5.0.5.jar new file mode 100644 index 0000000000000000000000000000000000000000..a0ae89c200c4da9481813310189d2ac5ee9ada1b GIT binary patch literal 40271 zcmb5VW0WP^wl$h|R$7&|ZC2X0ZQH2SO53(=+m%LTrES}IdD`9Iy=R~I&b#M})?!8c zm=V!d8-0%6XCGtAOM!yH00BWk0X4?TivoQeApiLJI-tK!84+axS_xTEdQc$wzYVPS z1G&Kd8i4h6qW<$h839=dQ4u9&IvLShnTc^}DO$Q&cqv+{sfp<ZMTQ0Dt^J*Skbioa z)4zNb?CWK=CeDBR;J=Q6`)iDWm6L_7nfd?kShRl`Yv*QTX<%>R^#6M36cdEr8W;#@ z=<ENp{kw+*44s@E4UC*c?Ho;v44j-vge;srNQCU{9F6IWtPPx;k`%@zK^akb%jTmO zEqzo@QG#GJ`>yMh!6;D^=jf=~Wk+U;7sE&tt{b$W>E3`^U|v{vCZB*J%;EPoG7_9* zZR11!ba(+_tBVf^O$AHfEAbU$DRMK#yQbvXdF+SwQJ10g#a4}qE7MPX7sS624T-G_ zhw(TJwK`tJ&o$GUdrEcTvZ%pgdQ~8CE*pqK0uX8_O6D+Fs}dQ2`dS(<Sc9LOR8CG8 zK|g#Sw!~YhpCj5EM$eQWT(oB1OQDuz4{TOYu$Qk<v5IPjI&VImb0et2a{S?lIp^Y% zC!~;EJ*oXDU}fVw#w<>>-w!qL;`e&XI3oY1;zHAE1N}ah^BM7ZjtUT@KY)|s^)N*8 zxjVHbF`pX}s1g5-ZgLR2L%Sr?xh}_uJC7%E69in&bV<_RPzTMs64FP<h|8{`-6fL0 zx}BCvUpKJFeL7AUtPmL|2C`dc3L=zfRyrvVjy2b6nOYyh1L*Q+{~tvD10daGb!Eg~ z=&Aq0EyMp+{QfI)S*kX6tIDXpw&{aqrzA4T{rU`|7Gk=;c!u=@qXi_?#KjZl3QhKH z9F?eD#Ls`Z@(22CVO$l<Fd!0K`%6F}iZ`MW#6I!=AmF=oLOfhi-7%(H5l`~E<b2FL z<g}}MJ^b<G6JpDtlgtogXq4OjN5QCmHa*5ueQ$~xI1f1yfCxj9F;RpkMiMT<yJlEE z!hj2~tF5`?N)J^EYYwGu0ENN&$`q(P=klAz6?I&Z79c*$FafWaa@urKWLQ?`TE;RR zTiuw%j@njmMB8SYk-m{`5{d!8UXQk}tHPy|L^%6cHbtwe-&#;Zv8Y-eWzs!8FH-DR zr>vz}Ho+>{L?tw5r>8}>VV)4M2VBb}6bY+h5>YywE~?ftIWA%pNaC=%bm5As#q9hm z_t@0FJins3veQ<fHlzOfKvipbk;cPzy<Vosf*!MYMVZDcJjLMGSh`|(_Pp~X-!wz~ zWYx`g&Zn{P;)>rFi|v+diTBaJP7PR+rpgU1wI31<yp#%%r`8}C74W81;TlL=XoXTd zKAP^ER~pSYyLyvIn5Ds1<TH3+Ypjei!iNmjYbirgUbtH9q3NEjQf{hKyh(A~BBAQ` z?pnrRHx$o-_Z;;OiOlNTatS5u3xEZut$xlhekb6OvHvXaH(WnTmvIr;g-^q5=IsMb zj1`gXe@{)k0*V$--5F;g!;;;gD+t<kk0mU$fl^PpBBGnaJW@u0w#was-N=-5$EvY6 z)ab2%d)7M4+qtVadC0OnFJ%#`u3+)UJ*Ra8LI8(@9@4j7MH8}{y=3%q3i;KUP++|C z3(gEQ(smK-flHRb_dpv+`QoQUc6U*{{2!(}`9fua9orXf!9%a3ZOnd5iN}{cjxjMG z$N0Th?;DP>5pPEr3qBNQ+1on%2THq~hr0#q71EbEs5rB1S)0h2FqUWDT5pIv+@M=) zNThCIo#5C{@VX}6$P50rJ_G6*PgIbqOQOg}ikC;qd<2RX1j_7+K___TZz+LMA@=<q z&@*5joqoiE6TN65EwwSmguJ5G9kI3qCqn`!?u58z9pY>JOJToMNMFlK4|({GXkt~V z3Y9v<ojQizCiswfqSAPA9C>}(q~}(_pF$m<Q!QPy^54Ono=`Eau{57>>~>+vbBt^! zy1c8mNV<+Om!-E*vjm-e!AiPt%%EcqbAEk>GKjd`a|JOt#CXpWM{_~wy@V&-^kU(i z183!BZ4N<G)cXOAs`16TBka}j#~uj~=s^Z#LQ6|L)g*SqN*`P6v+M}Wo`H$pa%5le z8<%Y5*5!iuaqqn)OmogTWe>GwGoNybb>lmJu+BV>@gE-5-d_4o9fA&^0|q{bXPzl} zaC>RKF@K_M;p9skoHBEq@qi27!D$X>-63d1%DNfc>HR(7Y~Pf9NPiKA%oky>{KpCB z|1ig2bfGva2_k^XTd-Q4ZlaOS3jtxeh0gn35K6G_D9~WKVKukm*!HaQUWXT)_YT-c zR-jA7P4!%W$Ws4ejniqW%`MgA+1nGsK5`ug&ic64CePX+q>m@bKT-?sc%|=WpLxii zfV{0ye&-StHQ|&sTogrT!I&6Go_Nn6kWua=XhOv(qW!y6rlUEK{-G;I)rq+Y#N>!| zlXi)40AlXWoToQcs@Dk>hrurQyb8K_GNGb_pxK!R70e}!cioo5m;Q*1c223lMl*`{ zQG?O@0gJt`=!Ny9<Z%{n-;rB&|FVJhU>KoYKX@i(KQwJ<ycprZ+D~#lBLoXicwR-8 zGUgdpO76TQi^316hgcjliUZ^oYKe{5BAOq>NZrY)GgSu=w1$?|8b7>+Etj{mQt`ut zJceIf85DXUJPK>m%)^z1DvknkgH(uL!T%neQEWtmDNrCF@Gm-`{!gRxS5Q=Sl(0=u zeRY?N6KV+w`3LP*6*H`<Dhz*17Ri8GV1Sm9h?L<^#8(?Io-r-UaO*z9cDlZScR7e= zAu$y&>pt}B?Iq1SCCA$KGl`mVbR2dbdQ3S@y<WijeZG$90d4fjf@o9<_k2Qr$l)}o zn<$w&g<xC`+cN^=S~2m38D#5GLz8fHPfbCPt1Pb>-BM=Au(4vfU_F{xLl6FvUA4xn zCeJ-;wrQ549dMCR<EPTta05we(sVH@(O%c4MYFok3fHYrs;#%<VQNe^U~fwQ(|mRi z&tJc{RBg7jkvw~n%IGbc$mj0Uq)NS^ffo8|D^X(yD~)iIT>i)&s?Nut^T#|L#4wnI z#T#0Uy7|~*xP|Wb@_gn9<m+(WzU7e4A1iG5%f>(t^a-x+V{l2?k>c(kI>au6<>VF` zhU{@W2fHbu^0;#K=C)bRCDv6JqZSiKBiR9S8!VfMcU}3L2b%ArNwdbW0d3kt7VJ$v zf9hCm8`?YU*g8q8X`Euc{g-EO{0FnQt8--i_xKRSl}dS*l*0wrYA#|r1<JyTkHH;S zdRT%1_Kp0z;<Nn(o`fl%eoEOS;WE3|#m9Ja%_swKj6s<&bS~-6O!pG;d_VVeA(V{A zi-e4M3#%<<OV%02(d`D$YE!}KN(n+b10q@rw`8;x7-Gh8=8e6p^|4v_UG5f9z5VGe zWZ_qpuF$$w?LqOW)rVcc+3QNT&@+h8!RcZKU-Uet4{I6He;p1gj_-^(j|$|+Omg0- zW=8u3G_*BnHT08{NJt3C^N}$6O$kIHs~$3=4Obt!9D}SYYqWP5Pi&iXg7~YAVGmgC z_269@?e!pCk&pHuUXjObuNOVST~>V-MiraI`&!&mDiyVOVV1BHRJal@U`65zD2~J+ zDhwgz_dIKZ>xu=PF_El{L$CZ|u}zpmu2xj$H0ae*zq-`n66Jlshhi0#)1h@HtOza< zM`L0vcZ_^O5%$`r;CUxUUJafYWK5)smm_d+(G^E$O4uC{R|r*showsBlCr7U{!Vq* zd;$9_8}%G^?wal0C07gn(y)%)CQQ>{S?T05R(v$(hd^@cujzMWg&%q7M-Js;lvlbr zN1!>W2a>{Zx8itU;_*K5R(T||C5N$?c_f#w`ed^d8Gne@=IDjCx_~(5ApJs5eQJ*a zi3gIh<gdh71{=1HYJ|4L%=(UrHWAHwbqYiJp5?C`L(k}n0VW}Vb`K?ZU}mDAn=s&V zF%Wcs05egb`4^kb<g5Ho+L&$dmyd%&j`v%ph!jLWh`*-=T6Nknudjr?`lS^A*R-H$ z;$&}U>tym@X(38UM{ZspllO9^ilH3=CgUlJq8_2hS6Bf+W-7>3&tL+;v>WZ1jhvdL zvxys|{tVl2*bOEN2*c1t-i=}6vr{|mjd!nm+&$ww<UF)^z5Ua231qu#i(HV3I|NlN zbqR%3(nbP95uHo&P#=H|V@s84Hlvym)iim(f6y?j>EzL{;@&Q;-!;Bz-!)Pdy>W*# zf1-@3sIuzFYwLmC*<!^|8om7h%~bw?uA>oUFtP1*3j8Zpor!kdYk~kR92@Q?mrjYD z*2u=K#Ds(1y}~Ti)-~git<@;p3+UHI^D)!JZ`y(RFEzzuefqE=oAM~5<~Qfa8`9GC zV{&vuInS0Gx9fBznU0B7Q>WluMc7Q_r?c6p$fA7ku_Bez_~T2sw5^ldyLx9MKVaU3 z3*gonRLT<A*c$>%4m(upK$h`PJ<p~p9mnRzG}EbHiWyQ&1gj$Q9SgiYTq`wC*2YV! zh}<W{;GySWAoktLAT~YnIo9G-CQx*fdGj0D4<tq+0May`ddZ>XV^_L}diRxGt!5pW z+&nBhOgI$uAp=Cx6s0GUkvKn!#8G9KdL#8+L83CFKnYs8L8F<JUZ3O{P+dG3F8{|_ z1G!JO^eM0ArBV#|oaav*!jqpNJX1j32g<q2MJt%+q<RhBLB`LB*FK}&JnQo&w0ROC z@()NcXU*bP!52~#J8_$|Kjh&$lI)Y?=fr;Awj$o?z%_XL^F-t0=Z-F@ykM4-zkOC9 zePGc6xDY_IB4=g`A4^6nzl|9Srb*>Z1lS>n$4Q?Zp~f8w5tYgBF%^R%@gj!7(vy$T zhm2mwdV@0~?Jbp{>`jW_sPvqy*!>;DYR5-Vx?dQ^`ob{d|3+<UCI-&tCXOUR)^;w& zPJa=cl9b&%KjH_Q!w#9tDvN}ahZ0mQWEEu$EO;T6LLm{g01Q}|RFg!?U|$ol7btSd z2l%(2xngus(1e87$dH3+dV|o6%|c2^1pdT!uBH#KuH9+Zk2!kXAnJYlB=H<Y)_$PK zcjGc!&fc$(0pwxK92vrR;Ao_1hsrdJ6Km!RR@f#5z8!N*>dbI}#@StjR$GUT0`@|y zl`IuNS-p{0{A&GJl|O;HUaO74_)Ay)J{%h_gsMrX9;=O#u36{-tPDiane~eq{5*P7 zpQ==Ai~Bh82bye#mdr(ZH?K2Qy%n<D8+%@}?P@=**4iytniPPxaO%=4QC`gr&-UJZ z^X{%GnOtWh*$pg3XZt{q#iTQq9jB$>k!NJlC+c2Ru5rISC*_2D%rq6-3-G3Mu?~lJ z6O9>0qT@!nf^<W#KqXke4M(BM>Z7pi^Gy5N^qV1~5r9<)H9#vhR1blBs8VU(`ZI!x zjxq5=h)&3Ip+O!@2e(yP%nqu&!GsTHJQJ%Rm2mscWC#n4{NUD4mcK|oPc(W>Wqcbf zB272yPpR8;xnAm8&t4_XoIY6}Lm1RIfdSO>5(_?J`PhdFL9bw4Dr6C+#tu<N(a(e- zIsGB$J<$A15dP#v3t<Z%!PpYvS2!nQi*1LAU44d^24@9v6|qQrezIJuq9hta{5(E9 zq328rJ^63k!28}%t3?xA4AF>`0}43U@p{nK1S;M!M`qGz{3^NvVcp9n(U8|EUh&fK zSXj}!gRx@xH-p~UEw8;bk3Wrs?7<@XJdt7hLS*4ZkRTHW>HiK^8p<c+qA#%Ce}VOX zU6%hhSpUaRiBgubLQz2duw@!!IX0gssk*mmv`k-&zPF(kl#nh90+X)VDqbrTVshYW znUF?ZyQ6P?Kxjo0xC+*%<zoxba=Um#svdMA7y7<RfSc+x&FR#A$#c<ldp`Xt1H_yf zAv6$FUk5(O5Soda04grZ?mOVOliJ?~T)3MGs5TTG0!PEZQf#S6x{>TwY*SLFoTR6v zTc=$!6i?QCBm6^a%YIjWnCfa2ZJ?*SR6Sy2m!@SCNVpDLm52diy|jY|N-Zg~1UHLa zY~#s3HX%#HOyN-{xs?}hfxZ8rIccBlKFMS(t8lo;<R)zaDLZ79PA5b{AJp(Gmv*Ij zdyn>_9ipv7vU&UX&0h+f&E?v>YUq!QT24({_Q2@|Z=jdV0M=vQLa%x=NhV;!F}P}c zg}NzO;qky&rQA7wl3|V=U{E`@j{GQHLpxrgd!fWRM}8){g=3Am@q>^BROY2wXh&t% z^+a?wak9t_m5Vl{+5oKJd%U=XIC0TVJYc*vf2SN^)mm+sOxW_+jD@_awQK=Eex*}7 zQVA@!NsHkgki9?7BqUqx3NU{~Dp+2JEMQQ`JzsOhpe01$A9Nn?l;%#Q{iP6fS!7Hq zG@=TM%Syo!^o~iyD^S871hqWC>l#TW%uK{fIU;P8sQVpP1c$)J-w?BEpshu}&G?A5 zzgt=N<%$u%H1>_(F`;@yjA)}@;Km%6fRyJ%FAE6=R-7{lDW7#WL6(fzLU>buD~u#Z zdboYDnj}Zum+T!@cB>-CxKU_9Cce+%Mf4HSm>|%uu?d0PrUum`^hT+?kCk|JOXs!l z`O#H!Cfqw}Urrx$gP)D><1y^OwKWTx9a?!f^zCXI&%O|rZyuxa4D9m+^xJkNeFMkN zXU~!EHqSTex}Lv7o*&UPCG2bIK@J53ME{@WXBh(v+rM;%ijEw%7%Fd|TbEUXcN_nF z&`n(IApba-%u5`9f_!9PpbZ7YOP-`T0d?oXhDgUL+qu+-;3m~Q!d+3ha+ypypR&&d z{fSZn_wH{)Heyt%ZstSxsWk4#i^|XU&zc_KwLw=ra2gA_PhdyPzIrJ92z$ApS0Ww; z2g=vGAER=3)K7zh<zew*lqmBam3h|s>-<B##DG4rsL2;!n<B-{O^pg0M%40=H*DLM zRySkz{NDTMWK0NEANt5vjPq;NkvtJImDeUo9f4|ZU%=Xmt`&=G%b%&((~c&|vie)_ zKy=P?6bMyQhL!3T;<(uZxpwAT68rvIl$i_DhIv=~j#)RjWs^Tn1zvDzJ8O6zl`k<C zee%EWGd)Xuw!paGa!HY^UqU*YMnKiJQWx@zvFGG>t4H)zc&4FMwRqrg4Kd2IQI_9M zW2t5u-|bdM-Ui{%lL8rFo(%5N1`3K%CSYjMjs)vYAjKXWj=Y*V)(-mWoG@v_GT{V} zO)-_K^YB<aD83zo@x((G4tS3-2s&43h3;XDxk$y^zYp4w4k4NImQ8QPp%hh>^%4vB z7}B;jvO4fC2FX_4!uO!yaG9tjI<lywh+HFTr1qJ>rZ5<6+aQsNq#_%9M}c6YQC2AM z4WGJf5kc9Z^fS!0H9*mV;4?|}I#*?Y%i-%@2T^j2cl7DlmjBeccwB$z$vc7&n)(qY z=_^)^N#E^>CFw-!*(-bOIDSYnzg|0ajBGwsf3$vfYFs%v<3jtBDbLFjsQ|O!nPYv0 zFEwcMtPgz~me>8~l@_mi?9~t+k8roLTRMjD@jeGujM6m+tyo0&0-2(2UYd}m=x);U zq5f@f_m9{(g#?8ZM(Tp=j5YJ*9TK>3`rJ6C=~D1Ygk73I=;Cdrc?T6+VjBl|r<);` zz-RYy{JaE&TEZ4>iqwKpUZGC}!_(k^B!Z|J{s3peK<Hn{-zDX_SWV?E$zW(gERsiv zPDhv;j?UQ$l&~*ta|)aN!8?V<7&{||IL11VK?iPoh;PXOegS#!aJ08l^-8L5Q^TI$ zxN*3dmL&1dadISzrs0cXe50pbil$|X<Q{NxuqAUI6ZwWY`P>ruTI{(W>WuJp7M}kk zJ_*FA%MD&q4{2(?$y0vtQ!dW$v@5>lDQ3z<xFzkxBpMRaen=8t4B%p2=2Lv`9Ir=( zv{vuyg8Y3}YaDe`x_-^-T1X%urvG$S|3zqj$u5<3yLnaA&-KBugawv93&|dRZ)ctK zdQa40eUsXFbOzYt1!a>J*mjw!MIB3wRa|6xP`0R=kuDi=9zxxjMhPo&nNSQ8yr%-P zsit-B-$JWG7rcj0Z@xWVQ*F_kAGeRxK;#TL0^dq@-9{ydBc=x(MQO~2ZaiQ>v7~pk zVe~@Zy<r9@BMa>b1L+xw3uuf~hjD}+EN!|ycVE^fc9PnHv0rEcH8l=5ovQk(RH_a* zi<`J6Yiq2vsz+$;!q}VP0Pnc9R?MDB2D!v++-lVHM|$)px4$o(yJ!p<or3+3;o^tb zL+j0k>`$ZHj?<b&1RCg?_c-U7x^CgFcb<?%#vfpcOsR`alv~)6;mzXc+2I__JEpLu z))<5FAxP~eow4>#As!duLIh@M$L)W?fAJr0YNa}$<oZ&(+(D+n&ZhEKX8yNnvd;9j z3w=-&fZN3&SG_Jv#b&*URz{hnr_*+!%ff+418mHULim8;uG)dU5T-mgzWW?X?VS%* z(|`FPy0ofQwhKDmOAWpzL-`U4(!*!yy0i{m(<4i>%3X?Dsi7sa-C+#1>-_X&u25gi z9x((JPG7zc@dgGk#1zmODFs8f3o~gPn2`*EtA<6eOAU^uognbI{q%_iAylm*J7dU| zfX`%8Wje!ap4t*<6&sTZxlZFXKTpf_65+*N5J6T<NqmJhB}{T2CH%ng2bb7p_W<2) z2A-yI{$hiy9kt-I2!lh=&hy)yE1}4_Y8-h-%m<?3!V)3s${Wk`yNxfxGvtbQ@C*xU zy$U0TAO5#TB){pI7=TnL!srWtf#nBW(gz52S)I+Glr?s|I<ld}$M!8AI;S|u8Kn5x zH>NYX`ZK(jr5H@(9}3_EHaXB@*MwUYk-=0ggh-GK`xVlq_1{+oB+X??Et&%(<um2I zK$r!JkyE`E{K)QKN9!OlO%WJ}@wUOd&MF=uVPx(py@QC3nIj8Ovv;l4ZJO`McP!5+ zjgL7CpCXFI6?sg18j}*>p|Qx)S@vU1W6cZMM=M@~>H;_PK7+3$u($OSuG~os3{x1c z@%qi;g7C$V$=sk5E(zag<L!|(#Y>;Fz$O7)f8aN4LCMp%<c@ZmF=*a5z<sfxv%|s2 z+xu#W`^bs=#%!-vq3q#G-tLtmUoK&M^6imEVtpb1K8s7V=1uy(<R}dI{{=UzS=jzn z-iuP!l0y-|<ej(eXtcGVEZV|FZNe}a>i9_#)C@L{Y6MTx9cz)+s5MTvyl%Mz>({6D z4%&^0f+D;dc)g6jO{$CM^P7C9WwlA!98Sj7tlO@~shio%^u^%yoEr!w6`D98vhf-Z zN<d)IBkOd4-+q^UO%L;saRZVy5;hfT{!auG-FPd}E|t)osz+U8eW(1dszkfPTTXy` zT_3)E)7X8wX0wb;jSQpk0o;0hn=SpdsT9MYtA5+tTb6e=p?(!?-LGC4xIPsHX7JzO zC9FCPW|(hfcgTS(W|fL;nhVYfmQ^Z29w}<+{T7zW9!_By=_p<jW7SsZ@Z;&SoxcLA zVV$bSXr<)Yv=)M^ZeG!$D=UTvnjrN4(6}@kE|b-)j0dc`7({cb&;7`x`fe&?qYkL% zn!})mS7<81k*QB8Of{%AgLkQR8sDaD!4Q{f6N`2q%WP3BB0KYo7!3(cdDB9r!=`PZ zf=O@a9Hgt-jlP75$5dqa85AHuVyvxXkINn!OOwP{rVld@BfFCrnE`j~lfm!R+)#5~ z6bHs$gk`A_dn<FUu=?bW$Hx#9>B4Q+xE5|~c}Y1|bbKf5HWysn2aP^ciA+DsRtytu zXY{H-6E(*e^>dqm1V<E=G5v8~<AIRhp_BTXg0Sr@&jqHaQ7j%)92LNVO^!8k_+!GV zR(kCb#`lr7%XvFQI~S}rCU%KDXxVIV3BwX3ehb6_Y-BwmaS%EtF#!%gs({Xd4Nsh) zHMo1^n+F@-iLh6?cM;i_tF_74@7pR9Z<8;Zpb$AD^k;P0sAjzo8?<hM?9C3*7e>Pq zV(hbX0MEI*AxEd)2#=U^V>|8M9l)f;aTP?f9CU}@uBYQHgxfyPu{&Far_&pEZ;0XE z0gH3=bNT^Hs-w4?AE6e3@-b_e@lh_S8i#JX6!iu4@3W!)a(A`$YbMx#Rn)2eLr(jP z%T!ODlvEHuvpQ`PHu$9R8@E6a86^Be5#%-FV$sEAu&t$}1Snd@NV`a7EFFbB$|yqZ zuKf`f<R&=Y^O;Y?nP;#@<h-v6{DS?0U%1&VG8$*0$Bd`fd0$u88@ycYKAv7T?0{Ui zxe?fiFNVoTNM=bniA~11q&aJHpRRj*8v`1qh^#)xg%rxaF*_1h5$p;u@4%Suzm!Aj z8n2J`C0gtDaUv`dYQ%76rtAffU=$@t#cL<;u}bKs?7@Spp>~#S(E^X8#>qmJ#yM+F z1C%uBN)Evi8FiRT?kWQ+l(A;uMD1mIv8Igcp~3B7Tl>qT`2>Eyd~7dm33GhsUg4(M zijuWDY}A*OA`cueT@)wp0RP^f<~+49cTk%A>(r%{Zi_PCur6<dtCM)E`N?12xc1v6 zU7b;!z(x1SQMk39`JVk|EPuh!13>hJOq2-J2gC(>azQK-ODDGtlYfInu}@d>ST~Cr zk1TVnL<ShiV1`s-VKglXlag?l*P3KLJ#Bh!??Yvkcec?MqN#y1`^zy*%Sc-^jnZad zNn>u%T%#$nm9jYB5Ob==BeqF>1v}&Wq+tF&MPlAcX<2yR_cl?OjXO)G)Gn<@R$V08 zR7v<8KVu+^C4JYNzBZ>3LZ3cH7C7G~%|*eCmXW!*Yue&`MwyjC<lkIwDIMDfOE8q2 z%v&--d8#*NM3M2E4ysYLpO$j=UP-gstqq6g(g81`UeO!rlx(m~C1+t0O{8YJr`b_P z8LNT<@4U`?1MNAyOQ$>ZvyW;3c5=KX=P)Y2kI=?ee0uVxd`|sTkt2y+1ER4|xzd0* zM%CXsJ^%)7X!Lpq`;2MYo-8No6cS!zaZ$zKJn=(0$ev$>-BY>qrw>*3h%O~JjlNVW z%!b-EFkj*7FKvmWn4>afLNxqci2255eh^w}aVa9Z#&9_XG8^ymEoQVFsS)<;;NAzw zv629K74P60m9_ZA?!Fc(*)23Qa#AlCAu2KwPWXqbWK9}a>vz4Tq{>$-*oAbLrsH3E z<cOsbf4)b6=rmELK$KAHdfE?1*pEc+&oP>bEGrgqYHUwbVoeq%fSh+XVKi<|v$;II zm{e3O&`@{Ogtzy2LUt&6a#_eqNFiC2+3XJoF_)q4GNd|h9CZ5=6YrwTj$T2Td5^6t z=Hk|4S4^i2|8n?Myok(WujLUW#T+p(5XVgSOQL@<K@#axlXZlwTZ#R)=~xufRi7Lz zV9(k;{;itngYrf$<$5~SuQ_rv>V8RSm_6RFI@0aBYdm_dLG7Wmsa~e-Gry?^pGvKn zfz*HxUXiS|83G0o_!V~vWaryiJcn;HVprjLtlhgVhkYzLhj2~EYyi{?g-V3`1y213 zradaEO#~*Yum2Fg8t_#0tNF=IyJj9uFE7F~;)=NAE&2**#8qTM89R)5?xDwm1`=X{ zs|Pm53=6_EQThn1yLk=q%yBEiB~j;YRy24c>WrjQPi~aMc$WN@+R~VB4DZAG6kAD= zGq!LOC&o<opWnyraw*E1gYDO{YQVT|vlatOtR<<RhtogT+9K{jq=zM89`eFiehI_n zqcA4lLPanS8sn3)hg(E$gG6t04Lg92aAwKsXTQbZP(E@fywG6NUAQnfynDA#^!E%R zk1f#AmDt7cy`=TZt_}ZG$;MQC!!@`8*DW3~+<vf63bWI1<!S8C<UIR96LMP5Hl3(a znztvY-&&5eTJ_1mr1X0fYFdW{nFh<@m$<JcINNydl*tuELKC{R*7>ud$`zw&D-<8U z4fY>Vje2;ux<0^WovjO$8ZGs?$mBi`d%Y+x;w0fSggS~eFhUJmgOLpbSWcy5JP4kK zPyET2(s(ch<PS}3_k60Sx|QCNiU9USpo4DHSqba?@40UdYy;>a8K0Nz2H1xKDB3=I z@7kc+9j&%*t)Kr=B$c;^?a}xuVY`1RqJL}73As2rn%Fx3w>S4sMsif#q-;MwYVdTT z;J(<i*h11Qct9N^9V1C`SXi+?IK&@N>D;D9)0GpaHX?^@&?os}?%-5$^%>)f30|iQ zcDLfqA31tJ)eiB2(d96#pzxqFfQhPIZiN`a-|d);N2&~Ha3ZVMB0qE^gS~f*ViLn~ zrCZr|<`kYa7Yf=?kOyu8+$AIW1217MJb2*1i&C2Di4uB(qJLy$^u6})*T|Y{)+P4G zWP`^X2xixWcfd_1<Bp~s1N`myzCphCEukfpR;(~3igOD2RD=++!x?fSJw~5YynKdI zr}SKd%#D`X>u-hTb$MAqL}PJjy}Ru)#Cz8(^x8LrW^H?8kjcj7;}`QqLFj<o;Wf7B zrBN1jfxU`1KT;edMkVDoFx4kwsFKY+qCoNXexD&O5j0)NB5jlrgtt(YgNjNzjfoXN zA~iIpAZJD60!3xDY>ejleZ(QGJN@x5OV8Z!pN{HZVRHTo)4w&8{xwYhWTD2%$o2Ch z0yg~x_k|P?g?X_!uj16<HyB6&gTnK@z=4P0Y%?%b%joYq+2TA;pl=Gp%ZAbnI7@}| zQyrH)%&YG&A5UQR!SgI|sLmWL3QP)QBz8E{(5pf2O>6|1xPCOyoU7NyZjAVn_Y?qT z)tlxY$PNDT(NlgR#F<>cgtON^tYpTQymIAJ(qR6^Z3bRUje|~G?g?f4J(D`uJ7B72 zd5L%Z<dpqo%1NcoA{1&idNMTZSG=cro2L(eYMfwN-o!GIDd?dnJi+!$`UUX~PS5gw z&k$I(&TtxJ@snD!omBZ%PbL)dpcm2Ca|Wm+%&J%DL#<lqB%4?cp-g(I8q6QwU{{-4 zHDo;!)c2%Eyek{qsf%p&`ZJ`8nPzHai*CeF_@5XvrX`|er%|MapU^TSM_fdSs?|RP zkvY$pi2;G7WYfI8jKhKGaogWP#YQp9@c*u*Je!NmRK9{_{}m*<f2XDXB}o5d`o(Sj zqY-4dr=TLk@7EklMa3vAt9ck5JYi5SLoi$ip3MwnR@8D+b_I1_1YT}P_*5wv0E6x< zjd#=g&~56>`_tPOxV6^-`I{r#SqDWc1@exXWJRrPXfm}<!2DGXDdD`bW^-{uHcL!C zyD91F-K*gmn&`4OCD*n*c;^~y5%jTERgp!c2%@-Zg$%8bTql?$0wDX74=YR!%{{7& zQ87eLF-XWlvbj-|A~~ZQ%e|oY&xTIPy5*y2J^tdEcr_X)`Q9H-sIllDtUewEXVPuI zAr6VJ$>u@{Z3m(wgake2`?PV8sI1oibP=%R_ZX6A+VYbnvJvipDRfi33P0_G{`>^@ z)47pUEt}%Uzdfgomar;7$FU^t^YAn<^zr((JwOla;;L!f>B%Ov_hTEF@nixzIQTRM zQ4FGEB2?c_H(?)d3v*lGR@@;f5VSD!Un>!rz1dI6U%@&3szm&!0aE-QS5|r4cAg)V zH-W<e1x_ZbG0_bcd=AV~(7hQPx(EhUq%4s?Zb^BbNfJl9qaGI~Nq#5*oO|&N2s<cf zP>Kf8syh^IC)$YA0y|IhW+Ic@Y077!$?x<18igOYC4PpX%K8%ond4_B&W%tj;^s%- zmc$N`DL|jZ0U8`l1Wip@+XS;)Pp`as;etk04dyW!ZxQoOGUCTBVCWp6@TfCyJHd!U z!`Xc3w5s>jjdzxd#@*ODF4Hk%tiO+l3O#rzvwCO8EZi_Z4ktP0L9~oEkN*4NrQ9C> zN<~V%Wa@iMa+{ST&UdsP3s08c;*kPcjW*lKG#4HLm2Kg)-hn^vDSC^yt*cv}0LNHG z&8IM{ek84%s27Qaich=C;k#^+$%jjTNN<ZK(#1DUtaz%ZAS_YHYy)wYHZ|CU0v5bS z8m0kZp(me?{cMp7vQybr9TXF#gM<yNwaCf_fI_6jOx$9uKsuqKd3kH`HkU0T1#)Yd z0lE{_0apF%WE$b`jv-kxxU^nlE>Ru%ELx`urGvYljg+uv1A6u=ePMNH8`<iqy15Z& zY2(QQVd%_VR5f!&Cgb&rW)DaVWMvwx@(BWc3OM}?RZ0sDNP|!@ciUiS;gTX&I;s%| z99_x+Tn?$KZpUy19wd+OG$mr)1m7cnmHgzxLqz-8CZ8HYavNu8gG1P{NEknW(g(>L zo{&dT!a&3#2a)f=n=J4~4cL&p?#w>Q=app}8*@5311DQj-7>BuUT|RwQ%WUZk5nxY za%%vFP;8qH^%c9pRo>@KJPN7y)V^p)@i8b76rp<J$KC(IRReJ%y=`4;6vV}dH9exU z_s3^XJ;mFWKx0|Pm-#17-?RS2j{DUg!v*XV2e>f5zo#JZLXMH7|4W1`+1k1NRd@Jr zZ$nE{jZ%xhGcZ3nzp_ae*oU$#g~euv>dV^zCO=;^NjbA6y$<f#YVhkV1tS$89mdRl z7vaO|UbbBUi84sYO1quRywy2aeZRdtv;*?s5f>N=s_%v+ae(9Glz!OO^nRxgAP+uG zJqgV2hu=e?AhD<RUxj;@i?TGCEmg)qzm2J&&t^2=hwvM4iCAS_ztWkno@BtG=lTb{ za}HIN&I-^p8(Yn|t)96+se^O*3HYp5ia%os)3Z{|<in?^$LKPeOIg&CgI+V~H3Yd_ zlG|9Pb(D5yN}pWj3&sc-A@0L$U|Y-`rcsP1lWew6-<rgL7hTM3iu%-K<mlOS)RsO1 zwtRJ3cjG0uV~oe6wdS$2jZT?vP85Y`UYVH+gIK=m1ScL}z14@=NMvFbooDLCfU)$e z4p%AMh&353*?Apw(l2-~<F6%FHiQ$xRm{XHFowbjk?6^7MTd8^iskZeb%$F_kT8tL z@>)*89Z%6VGZ|pQNf#Gj2b@@^*=d<f-HB(^+pE5FmSAOKPT^gIwS{rEk4Co{b7q<| z&R1N)=x8>RP`eyM0UJf8tAMj&p=J(4-_3K$@L~%@Uh!kdD(00cH!d1CnMcn{p%tQ# zTil0EF{DE}{^)ho+{4cAE42o{i~Cs$bViK&08ROTWzp=fWUQto?C4JSqz|y?L)}Le z4LYdfcUFvmC;Gtkxh#AtOtvl%`Uk)<%+IJrm%jksO1j|61c1dP{Q@|KklQ>k#9W(= zuv@HWOwbLmq;o+PUdpR=2lbhAsW|v$S;3o_SxLzh2VDr4XDLd;wKX2W=Lyls${scE z*e`(_TLeciwLr#*hCaOr1-rFDERx56*(h-JWGis>mA=Qmb}Z2Tha9Wq@mJSF)K^>I znlh&Elx;GN6#62+fO(Ry#?t!Aos^c;m*HL-QL9`pU#1DiB6t)(L$bP3ZdBuGMBg=w zP4BfE$wj;{i$(9XO<%UT`$v5w?Zhh{aRAx$*F?5cHv8pm=HpkX?scoe4hW~e1M99J zxl<q)EygIEexCMnJ>+p@mb1IFKLx8hQ;s;Nufm-+%JBQ^^YuLo-IWg;JgLioSpfD1 zVqTGVYG;Z7`@tMXVjmA(ah}-~8M-hgQL?bM;b?R@%5M!9(X6V;OkC60k>p0|bj3dt z;36(I1KOBGCGjR))u!$9nq7I!jlqSIX@nU0*9I!QJ^<zB;NrPK<t8>xAukV(lX6)V za%!qHu6#0=)?tFyKdMK_ss`ei7+8ZQ>kf_Jw)v|o2UURMl|Pb;BK4uN1C8(@vYbDz z09Ff0^#>=YuCTn$&MDu>;pgq;7)c;+=binh&oP;$yH?b*=g-sG)c$zfEOx5qELsQd z_ENM)0ECq9zgH;^vAA?7|G^O>PsL|;QJbaXR4x%2YH#l>tip^|v|MVoZ9jCL)LA(1 z8Z0!&!^LSh12-9~lMX)QVQeX!?hxW3h{J|f+BWoY&wi*Zn4V9vP9!Gh-^o8%=yX)D zJj7BAqE>EzJKsGuu_BibSB=6}?y+YzN`uqb<R7}JYvnI-(KrzbuO*lAqL7Ly#XmP* zA=qF|jmaRqXxWzaSf^j9ue?Nqh*zir@Ycj&cA{iqpL+gmAjKI*g7!cqL4B^rmy_i9 zxsHsAlX%qODle?l&m;|vu1e9O|C2-;K-&ilFaA2r-)b&2hz6~8jSeat`{>e1yMqhx z53%Nq+WR!Q!j>4imIf4+a=~k?w_Z%En@Rt%u&zRDF`UcuvUd`p?xe5UQ)vF4Et;v6 z<`&BRoVx8EId3Xu4Js0k)zB)wCR5;%E@fb!7~@28dB3FyQ2JfIRxmNb@@JyN_<kI| zN>*hRT}AIR9?@)CXaTq5NM{)VkQF;4X3n1VI1|C(&(`ln%pvs@24zd$Ngv=81WBY$ z#)H}CW1Lr3&@cfxy=w8$nKk@%0qJ^=ZE2Yg0VJVD&skteZ~=n`bL)e1)`b_gzy#A3 zsuH3vQO$W<>`NUuUCx!5uSc=;UI^%K5)O2WMZf>VdmwhL&8dxekMudi;F0l-r;jn> z*nUUH8IJX1m73>oiQP|jb5XEOK(UH-2NL1L&IIRbQ6jNA+Ui5y$I%)fMpaZu?wDx< z*p#@<ee{JK(RoK*V1e)s<K8KDwgk3DIObe>NFSklZ{kg-6!dbUaERS-_rVVN7QOiv ztofF7e(-0%hZUUBXBs{-_J0h+y+UFvfnuKNEkSRY68S%(1f;kDgG|#-yrRqQX#9FZ zEx+@Kwq-00o}QN=IJM|RWZA>=?5AE-cmk2&yk->MW((RQH_)eja}bO~Z6R(A%0s0l zwx*p&r&juAA`8aE8IRO6_z=@Pa~yiKCz!P@UxIpc7ccul$F-09Rtp$uMm~##dH@6v z=WQ{rJU7rBH*Ty87LpUi!;4G}h8s5{FXK_)5K;MuB8L{b52TO1BRNu$#0<v=o8Dxr z-$-G?j)le2_^t@!g_6e1-`R9?zH6YgVM=B9E@L`cF>8d8_TWfOu_VL{#-gO&sU{Vt zf7*>5owtYXn!h9bT^kJW*;FZiX@h@kk@-*6??2Z){!9Jtayaa^BE;Vbc+7)@!Lsf* z<^BvpE}{xo6n^0s$xkmV7$j_>_5vCI0I~A@2`?xJ60T0L=#4}@i2KJ9do(|p=h4Ue zqKo+R<@O!&C-@q3Twi4OBZ~OIU?o!xoB>PIBlfJ(4pns;MXtI2R4+Urj{H7pWpUWC zs<Ww4RnKHpmHUm=t$odk(*>(l*DB7Sb<ZnrFI)R?JzIZ9t4p4#7DnjA|FrD}Nhn`l zyF`3!vp8tB{4`9{u7of^W#b4Y_>|qlEeo=`-l&9b)mGea;Jz8F(qtM%plp0VKK4(w zry$pxia5?n^rvabfZO^E<$VCZyQ7f(GJ0Pn6)8X|xE#MRQ})O!+R~zE7D=Y9lmhFQ z1lzz<FAhLDBLv2b@+AEGGKrqlC?Ed4<>I^U3@VzM!}L8$nwn9AJ&Xoy0jAPQqrV62 zH{0*(wf3+r@M{JXnT^z-Zdl=<4fyPWSXZWcT#VmmaD`SjlT3OrCcW~nK3&I$n9ff} zb&g*$f5qFX@3OAQ<mC@DI)N<0HIN%bNXcWM%rL;i4&-WKYDgIWJ`(?PTqL1LBDNwF z&KQs7g+DQ6sTcc%ine!*BM`7)Hq)e%y7GN^%nY~C2&AOIO0FU_FMCdmCEml!7QdZ0 zMdl#xB|NVMlp%+>%!a6>E0lr!+sQY^TaLhnpkMai4vp?!<KQEgbK;%=cifmSUPoaF zA)}ONe{}JTIMIojw0nmzN5UN9VGehH3`H3YjkT&5q?O+?6^olnFdG=fe}G_Vj}&Hq z!2TVdeEXFLrVv0t*GND>l>ZsE|CemdQq!_WQNjFZmZ{Hh_L8D!fl|-qPj3Y41X7fj z1e3N<lE`HY-C@&NNpuDsOV+g8f)^N~)&;6@H^{sWV4j;8g|oZadIHFGa}EUBbzg4< z?)teVut>;ds+cZ)zWH@ua({Au`c2vTeY}oX0WI!IVR+hW_p4&aZfTIexy$z7KA<ps z)*ZppyyOQZOmvgv<$%kDahwZ+MmKDYBL@)aI`rAR)JQH7?nJ_#7k{P)=@99<8Ppy3 z5E7E?$Y3O=eP|)OM!2sAqZRw9_Q!_0X{+13TuQo62R}N<4tgMAfGrtH*hv4xnPXu^ zk)~X(Oh=Q)&MGRQEiW<~6;3P^H63Mm(C5k`1Q4f6%Eci4;zY##h97ws(hss0nDC2{ zG*+?knqPUEr0JRu_`a_(`8S#jzJlVsr9UD(!U&l70S6v!P|Ks3n>^Pr2j&X)56pHw z{((;H3;g^|n*zz?M4YffKJ0wsAmDgIS@krgo#!=A2v!vYijaoHF<}B;X4Wzs2~*I% zV`<Ufcd6w^D3-<J+R!GJa;a9gS_O;@b=FG5!%sAEOJq#42S4nIJY*LsLRi01<2qP2 zRK~6^QZ!g2STl<Xsb>VUA~@h^#cL||sPTumF=SyONSn@U{`_t2?yM+t<g-p(h%FtX zVDTL?A^m%H_IDL#ucX<;U!G)H@`}WgWMgmMGsSWwl-DY%^|@feY{W8TxltKFE-A~% z5TZ=Ktl>mdxt$UzqnG%wi6PIku?#Z|Bfpa+p1E3fmsTH*u_V<lu6F71;|2#V_#F_z zW-mugPB5DyU>y<&)RCbZX-tHCryOiA;6c_vG)T&dsr4?k-$ztUMjmY~PbpeEIX}pn z=jxxV@n2+Rz#p#(@<5~3IBi<g84_9?6#kX6VWmRsxAR>h-@C$-hH5VcA0_mS-hZ@{ z--ECcW)FdkT2f>EP4#)0(&RQpZTaNwnfNrkxP%$n#VkInIoWYSa}kFLaGe{9VC)?l zV{APH0n9qah=sq4g7vt=$5r^<B{<S%iC|YryHIMtlX&!K#I-$*CPhaS@s8?Bv#LH4 zcgX??pYf{W>Zb@#MP4N0(a*{{b!My%MstPiB=KRyqhJ`MoUtbyJt%bxf2!Pwh!gzD z6D(3j1gq_}cxQ>?n4<$10{sNHZQMGB{w{Ihcr0n=IM?V=pboVYXhkk3cE}~2eJZ<h z;W?2iyew74yMA-(-;|v`4@R6mFLyIy>L@<!>rwECwQ-%_-A6Z-cj!q!vyuHcpEIQ1 zzTdn`dUHQ(NqRr5i;@Qq+qw$Fb<kca^<XEjEY&V#1B#lNX_^uY%H-gr?#Wk0KaUCK z;JnO1$S04$f^_)&z6DJC;okeYMKOe|NYz5C=^N#V_N;EejNlYX+0SgjWO@`@;c18= zEng-d%1?s`b!VTNw}u1U<-SFc6^;e591<Gn)c`w;csZFf5>}BF(jqKd-+EXl_Np|F z@HXS1r<Hy~*{94s<BM76r;YYH<6JY%Pfjf@0#6(`bigb%afl%fS(Q;P3DeZ&595n5 z&s81dHT*^`qnr|^slqS87h{~OI>M{?%}P4)(W}55$rCUuIL+JGrQobj53(e-AFi-R zSh|FOi?EA|mq&jnc1?>nz)JCi2E8_j`O#0<7wnLSB4L0_7M#T)P1OtBpj_A-^A5~k zfTU;~+$mDdn65DbKWL#W)xJ4P!?+~=%rc2qAZ;7}0sNg)+=qG+H&FZ~f+7%;WR6qZ z1Jg!;Rz--oclQ;z@ssw#mMt~#l1CW3s_Ef$M-Ug{sx&UwI{pS4rm-+UAD^_}*NP06 zylH0HNhJu69?6mpCd!eyg4$U1kv<Lg&@`C>#+GE<w}L#Y_*6w$HaT<wLu9y>sOq=y zl)Z>@wYV5-T!VI;kaisDZ(%xnk>NI?tgjnK5wcbyo!`RUJK-d4L>rbwDQ!dv&X&?~ z{JMiv=}(T}oN9v27-N*uXk)A*^|u)?H>`9TyHna~oVRk?j^D*N_KGEM=(TSYj5+oe zC2!#AIHmrK2s(<9KC{z#d>`T1BTd@dY}>=)dVn3vO4|F;x~IX@N9d*!|G5{RGpK}9 z-xBW~AynNrKYWW-N!pRdSE)KLCP2SFOFarPN=MugeP&pHI@X-f6<C>ZmSv1Rx&eOp z<L_h@8@ams{M8GF`n3+_{dZ&~Y+zyS@qf1ak^J|KvWbnoiKBtDi{oGWO0rZpm9SM% zKj_X(*)0eN<u&ud!7csG)wIyGEYYCz$|Oo<gth!;VAr7|#^@5(<xiwOK~Eb#`SrM_ zI0~wE19uY;K2Sce`ONRvGHgZx+zyFYQ(cFvuRf2NPSagitMfU&5Le%A$r%7>=O6U^ zI0d874OG&)sjLRoFiH##w632Bqd8z3Vza_y!pnp0VW(t#;#&|@5akejEVX?M&Q<lg z8<o|`)piHTc1*_M9o{)wE*FcHk~7Y0E6B8(*V;d}Y-{^YX}JZd(0(cSJsSZQ^7QB` zb*h*mafm9gEA|CFXakZK?HU<(zxAj!VSB;R$mtF^tY*({L7r=@xma=wgs!~0dYQ6y znzpjIE<0px!3=Dz@sO5Qxw}#9DbG5bJ66=0uvd6j*hWZq+30R=$%s7>mV$Ff+x>K8 zxYeECIPmR_rD?5R*DiC~Z1DjY8RB=01*#@sVnkHqs=vYJNH!UHxM6pAPhVHzxSc#^ zOH8<u=_r?<j<x0gsnqD`KGe*|Jk{!WaIGcjX9E-G8*teFPPO^%(dpdMB_J=+4k@rZ zu@ovwmH0+w=WPp$PtyHaLxs3P?a?OXhO1og_r;KyrE6|zNc1&_kHRpqy$Vkd^$wvj zR*byDU_z;T)j@NX+n#~g)Nba{HObUg5s7ZEGBQ1klV+-gfoV|M5GpDEyZM$Wa?2I8 z$3jg14Jd@p1EI%4-~xO8C!BZspxINV!L{E5n9$4BOOsr>fwW+>nTqwv@Algwa~04G zxaH4U_#~BCG3T7lE`q}*<Hwk3+^+YwXPotHAr8Gqj#=jsdZ+f66>Z(dwYwfM!F${y zvB+<564LrO<mSHd*=4|<b7WWADL;figj0S<Zy2ZK5Pd4A<dA(n)A@m6IaISuhZZF5 z9@}Sr8%eXzOxk65SLBZE*GTs$>@&~Qa|daT#d-n`oU@!enHw%-EoALUSe=T?0!Tc_ znLkBrY?h#|Y+{mEDMf}4n!{rc@>Gd8v-g6~Jga`0A=?K%Lgcht{%bt1)N{+y-f&?f z;=b>Qp4{ndJgC7<h<MTco=_$E_B28B3QJJiaTIGAMDuxdm}F`Gw-dttLk=0F_9?gn zYBZEeICdqo64$`*xZH9QUpn;EpgyG#p%9~cWGiOW8&t>(%H~SJtR?knN>N8dDkL1v zj>4spkE=j3i#>_U6F&<@GE1aLwlZd_Q1OtqD0)`ROtQ>Lq0H!EHcjC~yI--?D446A zqsGo_)U0FFGsIg%V$pL<gY?;kRO6CVOY#Pdlygz4JC~%VfCRg1PlWVYhjjUjl%8@F zR6Y5Dx07hI1laf*NjLfP!NlR)wco#ZTS0yIn4n+1I6YszIRAE6&;NH$`7a?{q_*kw zC4_lb-<C+$1;cDCs6~Wl*eKz_!Sg~F-+t<+FOb-P&$i1XJ4>}|xv{&t?~Eadh?(Nx z4(SyjFzs?gatUHNV~&LIUFAk1@I9qG?LDne#@o(Wq1ljczE8DyO}SmRxv?j#`fYTA z>!UUyLdf4`e<DE{Vi&8Yni>Ft57jxw`YF+~F&wyp5X0d*dsFElYJUW2bPoOkdw81u z1b&TC*$2u8o&cAJ$;Ih42YMBhw-+7k3<U@FPqd{VU2X&uw~$eabXQ@M8A6@WRYp%i z6>7Ew5D~cN$Bs{-C!i#N^rwM8qj9PxHAz<LW0k97q0AutVV#d>wbHC!44pATQEc;O z;?t#=l08TW?pNk(yXe0z3v(vY;i1fJdG43y%1EV^CQE<@_Q{kA+pF`AziUsg8DefL z6ZR)t)}D$fX*dlJwzdV(s57C?H>yz<LQVZa|6OmHRUkrNq1jejNMQm@m`t7+o;Txz zTU4$~oskf(pvY1`Wi?tZ8d!Eb&Z54T`BMl>rq1w2BLOZw=6E~;_9otP7`a}P*X_@} zPSoH&TifxEe9Lkd<)3Eu=IN-ro@pz#Q>E$gRkTx-q6*m+RMymY@!DC*@JR+GM@<JR zE4{prb2*+5-%OngkhK_GXotOjP0~`3T1DPtXvEe2w7uYU*39G%f0$c6G`V#j5Y-&` ze<*v$AlbsLN%*vF+qO>IK5g5!ZQHhOyZf|l+qT_3ec!n=^W7WYotQVGDxxClM@7Yc zcJ8%uW#%e#x2oT56f!lQ+=K<&Vrk9T)O1m)CU0Z1TenFEU%YQ#rlwkPZc#z5C{ruf zc3>eous-@L!sN_UgRz)poVb|rD0{S4-sp8CLCpA@?Sc1+Q*p+E*)P%D#vI8gklPXw zw;upP4q9TRzYiRW0rjb#eyooN8j9@&kBmT%7uU!tI+TqnFI*S`(-DBD;VQkPQt?E0 zNv1oL1cI&~o}WkfI<E-aT~nkr@b*U>d=TFhO|-)^l~>@kkV4Ihi>djokNK8yk4bDT zP_sHM0T+^jp1(YG)MQ^?KEBXblCGAfO}y2M%sJ2b?RE(0{9UoQ$M2p8H>1&k<dt|G z=o<pwq=)oYHy+dhaqYZI6Nu-$i`y5|dZXMIK)WH~Iui5_E~?pzr{_FH4%bj%978mP z?l9Cf#da3?o*5SnAEj|3IGltGC+`e^JLcOThXNjFyUyZzane*U9qr8-?hL)cfc}b` zmq2CO1A2<(p&g?TIpczPL)qyXRS$Krc;2|hI?@)==+A1FWizX}Jw%|H4~M_VIscvd z86{TI!9%tC%afUIN1C|ZKl+5S(A0I_$+8=;h8_V!dysU=#}y<?1ztVlPvF%>WN?ck zfhUeuO7&kAoj+Y0o0?B-huKH<(-byVwC`E=HV$fzV_uVoz6w`1v|!7fnEafBE~Z)6 z`B2vg-gx4kzVofnNO<uUc)LuVkytOlJv$2PH8@T(nBJScNNj(iop!;v#MQ74W%M>y zSNoY6eK?7>dp&@jb%IYwzPWT*j&@pVe6H3aq(wFU&THaKgL3HDI_+uVZ2x?)cS6*} z`3B`sY2y&Dj&AjvYSGL>Rw1kPn{uk2VyKq31<K0M%-R>@<X{jT9)9{Xhk8wpxUh(o zu^r7;zLU|sW74dzN$MOUF=;jQp?dwD!qrO>QFV~hc0iqiZA$VzMsnPGpiA|Sst$45 zeq?T$=3txs;P=6$>R%w{r}=kXEXoz>9!Y`3%Uru(l+7LiJ^=q(r3pOqCc^zxX(>Ob zO8);;rTtUesJJdaFN>@dk{lc?dj(FY4c~}HywNFlpv(sef<5+kC&IzhKpX~wl>cy( z&TeQ|38yGvf{o{P2kZsvE2v#27k>`pkl;A8>v?R;BBv|fhL_g|U?##1qbXD=S7M09 zQ9{<}WtJ#U<-&^*@B~i(S4%)?FV1cU)owO4NZp@};at@{#9(4B-O|*0OPYS16n^pa zVq-GsE4FmOqxk}{KWMY*irGCoXUP><<u?Nu{$|`J_AcW~=ptW`a7B`nO0P_!tPbbp zkM=~<tWKg?JMDE^)T?q_10fU|x?-$OE~$C!ne`W$=`Pd?S>kjPbd`rqkytU*OfN{v z$>B;RjtUvuYpU}XB;CfbCA3CHt#uPtS}UScWBOo;etC6<%Am#b356S1?b22*A7FOw z5{O})oxRj9uc=n|R>8sZEL?Pc=ML!_o~&jZw5&`*HDsBQndiyqV@^yJUHY-}OUYTY z#f~-43OCjaU?7vGdCAb}fq{A;MCc?tYW9F^BET}i$8CeaD3L@2dq{|+D3W<{`~j-C z`soDDuzQH{kjC^C_(AN!Pd+_kGir#;%}1@jr(4Nd-w*{!F4Z%x>|&sIZ}K=`tdABN zEfnTl3m-~7_3|2g^U~N|HkpE$P2~@)xY~*WjnEavTO{Knx!9jg%^t-m)3o(VIViOX z=ctP{)!p*ubD)*>SE-X;6RhMuVkrJlBFc@qkVacPR4vbK`xR3c3wa=^8MX4k1!9gk z#7Xm>xX4Ps-<wwcLgn;Dte{#CvH`o}`KSO%oEGkYmF<Z6Yrafd2uSG+|KhttHgbR$ z?j0z(r19_?ybinr)2C(CQHuT0@&Y$Qy!S4vtnmS&cuAlboV3i-e1HD#5hG1iB{$L- zjTmK@pUB5cN*^pf5kG_~u0@q9l6@f+KTaY53vVB%ZrX1|n3L<Xg`ycG!_05h0<?9? zAuJq0b;3ucq~Ge7Au1J<KvPXXywY@)*AA_d*U4#&jE^1rRL+7!7{e3-Uu+)Pi2#Qu z%zeq|hTML~0qpZ{lHmxelq$y`nalJKm(2d3ZYnVwC;R`u-sk^oLe7%YlK8LalZ*=E zC%rT;frS9IMxh5oU;?qhe7IolnDg9@s*bt_)X&Q7K-@Qg&nW*ll-aDNz<e+Z-D_@# zpYG;*f~$*{3vgxF9TO8(apYWep>80St)82c%QzIRB?DT0&;}~lZ2n28YTF`g$G=6h zd?KxTaFZ0yvgw=JTyHs<Lm53@i5DI<g+TCV;$BGBt}RhOJ|O)3c^w#5N+uArC%$?4 z6!Xh!dUv^*S^Cp^XUl7}MMRgJiU~Qq7|#(}^ex+Nt91E{MpRW#O{$`T@Dc;n`;fj6 z%g`pQylQCD>$>Uq1}nQ47?E;<9{a<952pf}vj<W}oe{bfMH>>=S(qNNlHua32&sRG z?F%}?pj98p&fTX;|6z8Q|5@zBmr)^U?$-s+TUKFewCOal8-KX0u~`0_qoDxWvze`= z<e*fdGid0iqbtdg#z8Dzy$CGCgnl%a{)lk8o1~m}F#{Qeu~>m@a(yshz53a|T}p|? zX;11Od>r|qU0MITm-7Eemy(qJ;fX;03chld=$Nsu*wF%}5v~e#=_HT^{?nDHyC=bv zSVv9UVdzqEJDQU_{RN;C85Y*^qU(9rt8<WWJK;=0V+qZC%I?vfcAepHlxg*&i}?cj zi+;2(rZ4ICh-4~CMk~fdJ1N{|OA$OitKzfcv)Afh!Jrwj>yC}onzvgVybjHMU-UkB zw|Agfr<tBcjb@x|0tvkt<n)z!ENQLeKr+oxc?K*voM_dC+tO+D6T&2T{oCnD&dxm| zV=XuZ6)zF(4w^yb#^04z5>!Ml|7?ZDf`u<Xdu1iDBcBy|{0FIo7>UNA#}d2#ln_G& zlY}=#q3lxMq9}|UTtKp>v>A-B{X(&sv}KW;({o!#K)G^<e#<n1Fc~|jrqWz%7=ek< z>(qsy8_sD{TvDI%uB)43g<*A>u4X?Pr@52e=Z`Oa`;zQ+E^l*MZnm%Qd#5_j-;)}V zWPy;Mm_cg~q14{DF0%tu+(rtP?laDy%g^#H+Mz{M+HHadKPRqC;`@tApq>9fMQAfJ zeqvU2m}yxY4S>Bc8`2mJH1{CE<V!G@_mn7AR<>ImO(B_}y;!Gwj*mjFq3Hdj*dH0q zWsEWO<Q0T@#Sj@dN2;PZy^C^Px3iUQ>woD0ZSv#*nS^8is+iSvDz(ny^Jgw?Gk}|0 z;E=Rsx6{lazbsheKo~P!Qrp?WlrD`ab3<^5jo-#JeruE*mN1BRMoeJ=G-@xqn2q1g zMxipH<ZITU#WL^u-3zaPt|`SCH@_sLF(r;S_7gJs()v9z)COg{9M7w+-*8?QU<o#e z1m1*0z7oi6DU7JGc$vf&nd}n}K0T4|i=}RDgw9a3hcF%IGibrIWhr6>ZD6}IK%KW1 zmP}ob#P?CzRppPCtQs6_gLY&C&qPCGj(oefB~cLw^gc4lpe<$^e%rY>bYXoC{A+M@ z%S@t+Ea5d4nb08zmMJA83i+I8@EQ5{EIVR(0?eY^cYy<;q6xAIV3uZX!B{)b1wI^W z+#=|!CAr$FI4%T94irfR=ctL`h?P*>6Bi7JDLD~aFels9w=hmW{wuMyo~+sTznPJ~ z;staSesI*~XD0U_BM1Kzmi}*DL()H$Y5RqC@f^$t{773xAa~?W8hirggQTVYh$L~E zAic%^n9cPz1zh@$mD~VXs`>&8>-W|}@j%)Q+X<RA<U++<cR5_AIZTawJ-!di{IYOO z6~b;ol&wP-r3@q4h~Ae*bdi&nQ<jsHGn#3RGB#LtHM?j|mqBNI1n?REk?C;%L#A_O zMPFzhefhfaw7!7}5j--pY7lCb-h9ei+^A^HBxEs#dpWr%rNJ!nj<b7p6HP)~IDe>| z(NR3nufK4eksa;<eM>WpqILB;aG3U^&@ui|=-}>Rf;Cnz|4)^U=3D!{wa?#>;u2mb zk!mMft`24h(_nP8KTgA!r$EznP87AP7@^fY1a9T^KdN4Db2x}|hZ+);_*#|3r$D^I zHhH&nPVs<J$H8O@C!_DfCc<|X&A*w!$i_c`4KsCPd@c3GdN|J*%F`7w1fMBGsVs3& zfBvD#iEN&Do-;v#Lr<O#kbx{t?TfVy{!(S=FPKxqno=%yPvX%c@)*oOXc1HVBEGl{ ziu=hy?8SpJG{qi3f}|x1QwwK#gGLsLjayQ{E()>scb-GGn#0`$ich-MMR|f5OUeD# zpFsgw`JI2kj#w!Tj^CJzrH1UATY^q_312U8E#RxjEl{2wRx2pPi})9L12ick)uigz z^PH=*zXtx73V7NsWpxnQf<X1)m<qY*h_SJuZRiSE>0RPT$tVKD>PRF~VgvzmztaXd zz~jy@{J*cB<}ZU`yJ@zny_33H_}#f)AnepcaIYStdbnZ##&)i4<(OFfJo1Nsj_QB= zIq`pH$VvZv;H$8*PIfa-@(`R6(5MfH3W5_*7UG7x<1X_LTe2GeF3MR<m=Wa7#Y5SY zg9^vB)V&^Fb(q>nbNhDn0+JnahWt^u9%xmzs}d1ME9K^65_IcylCVA2l0Zbjpp1-{ zgcSd<W~&Co^Wa{y9VqIn-hdo(q8MmcfvfYf5<01&u{>~qW#`;_k<1fq3j7hWX26Vh z16^|b9s5B6A=R#mL!3}Kj@PdeozGgTa!Il)Hi7MzPEzQuXv-=oDwEX@1?5=_P594& z8#&klXX%q(ZsixHt2vxFPQp5c3;}^K5`lL;!wz-g<X;q=X6a&3yzL2uXJXuWHVCla z4N!VEm@a)+$ki+SncJJy=supdH#qi6yOO9BF#L1{+YQ(X$s(Ar7{qNdG2)SQ(ULE7 zq@XOu>f=JEx}hsJAw0&W^!6h$YTK|7)I|Nb42GiVC=(9a#|+18#cBo>AXBiAs<!bt z|Jny_q%~w=|3His)c?h~{2yTEpEFiv4I3<B<gX!Z4==U#6*Jl+<v*Bv5{NBkf)ewf z;qx=VWB`_P?R@=f+9X(+PKTQ_%HV#j5V5Z3ST>#b)`((7$)N*4aB(}L1XCB*0vC}* z)+mEa#~06D$BZTypU2z0U%<AQkKn;|$bA(EiM7~6@X7+{hePF&%Y?CY16eg8^rZVy z;Q7&7uzH#zB>sU0Js1MYL+Zdl(Oyy_N<$jZc|pn=6&1yUxzw1!OSHe0<`*PEPt&cL z(LWA5HdK|J0ti$so1HNYLz2fTsLl_~(+HH-<hNCzq!w4m6RVo7EhiWU!fE;&75mi{ zGB`2K)?Z%7+z-bdA=6+M=P2Krm1nPH4|F$^vM4apm1MV>#Elwe+Pk`nw&o`L7MG+u z++29)6Tq;@upr5-)=#r;MUGhzR&DMeOS<gJsJO1IIz(~z?}iZBUFBFEEu!^^70jDh zAvKK0$!~b7mYN&eSu(T=ehn%Itfi2j1~=C?Xw9#oG%YQ-E^9b7wG}rzS$9Z}t}T=$ zQplXjk}|?c$zgy7rOmjthkoA2v^O)MA}rU78hytx@q|e&W?pkx`jC1A5Quw=TF&Ao zwjb%X?R;mmxrsGm^##GX{bsJ<{97lf%p5Nc_$0z5IwP9FhTF`!j8UV0^)e`4Rx#9| z9h9kdfZ^0K<REgTD>C-bSxs}|Sq$3VtI*j1d6$|CV6A$-gd;Ju4!PAU1v33pjyZKo zu<jwWm+K;vVMu#e=le5o4cm@c%p*q<?X%!t$N5v+Gb#o{&}m|@v}Xzo9j2Q#Ew(!a z3WDLc#zF5c(TrnQ1>dI?+<Pw&bs{swvr}iFh#9?ml11_eT|yhcDbY#K#LUru?v1)) z%j~nEXA_Kvu&t88uJRPVq|WjT4i9RdK3pbA&ugFBnnum>Mo}=30Vw+h;=6f|9diEo zUvc$c6Jxl2zdgh*T*MCK?#liG0k;{jv(|;Y&>ox#ff0ZqI^!dt)yHQv63m_%f`tQx zvB$PTwc#Jm&EX<;h$+s?gT=6y76$yeEC6hC{3`;ZIPV7=&0gBOec@kn;xI;g1O$9T zAaa9%^2T6%3z2+7VZG;o{sNe@E3wh9@`9jKz<0u4qM4bW0>08=mYHW9iF8MO={cax zAjrvkOSkK`CSFJ%>CgxTf5)Y5n=Xng;=@Pnf~OcmEf@Ti12+e?2G>*9PYF8%Gj7XI zj(A08FGJ2qg&g*X^r}p5FGn7KL3WoVdl3KQGt3!*6NqE)JPAkEjQeGCQNw+p0~-ef zc93GL#s~Wxe+@NCS3r(G>^|BdaK}{*J~FC(rz3C&sErT{<}vC6HNvCW#ETYJ?35lW z*ed;uFG<%R`0xl{oU%c{#cz?gL}r#`<`urAxA8X=`Rm_gMq)7`C$K*~McxnR@*ia! z|B5yLi*y;6|LG#Qr38YZ4Upl10djH)!lH9u@I(24!$4~(DN0-Gg6V^M^@h?h6ra4q zlu$LAkbmMtH0y4FKEQkgxN*Qk4f7(rH#}svMRV3aFOQGue!Vdi<U@>t7Ak7?8U4hJ z(N~k7q@4SPgI9~#!34-*_dAt!<n0tg!i<V+^u=b*K?b92pqi#SayJ;h`1vL~Vb71R z1Yc+H43c#uXOdJ6+m8=92<pI4Yow$%@-f>a;Si}lO8U+AO!Tu4Lp$!5*{~myY<ktz zTXHpcdW~s4UNo6cWS^083%JDTjo0}j_&vE!J&wKAUvs>Deq2mk^wq0D&UrZi?nZWu zn2I_i8%$MJQ>SY8s@9g7O|_BqXESk0jCG6+nL|9_E=_|~%3Kj2WSRdzh<Fdj1q zyO*<VK`|Y<0S?0j=S`^+brxBIF3zhB!$Fq}K)>&=c7JzJ__>d(G3!8<x(WO_DkPDx zVh`=1UY(hVhFvmBfCC}IywXi%bz<X&z%GdKf}}DZEeN(VEc~qYhk!{iU9{Ddex@gh zeal%uF=W{o27+Oigv$I5X)r7xHMi`A<0?SD*jG2V9mjRf0r60R9N6d7GRKlun+C(p zDt<SgM+qNyj#w#HfK8^%zksh7wmSdfHqu--CLVWMVLuyq=oh%Pqs^cL$6kfek}A?- z)an2*0Ly_bz=()|zBg%JWW5Phc8+hH+a}<gx|?LZO~IdzC#$wfJhSx&s>ds3AKirW zhI-We6tYJmY>QH5WZ2eh@tfgTpb3n~D9w>BY@Tfl5@TorFhYVk5A76vAW}RFLBh+G zcMtHJ5o-bN6yaLz@*B#t6?Mh3n(GFsT0z(vh?5Cj)caBrtNecZs*aZ>I|f#qDv&h9 z)*8`;cVU;&P4`mP4MAZ5$*3QWJ|(=vi~~WPjZ%>G@$cC-%j6eJ?(LffNZNE$2f4jJ z;in^l_>n_x)(=yRM}&@EaPaP$;&cmmbfm}yvf=B5Ej0&h++z!7&H^`ip?Ct)XeyaA zYr8Y$_xu}?3Fj|xQ})xKpZ~n6{!^naV{2snPmi9Io-HxM|GcfDBDo=dyk9}vrp%Z? zpq;+C&9v+R<f_0>Q0FYLuXdX-tEDatH@|TIZ0H&ta~TJ-cD`y~4<qqK&V*bO;RAa( zR4HsBheIT24Zi)YA8nI1Y@$AtLL9QlqBMZYBH1`NIxrAYHMI`pYFXAyeJ@H>(i^<~ z<|6flw{ALeFDh)ZgpiX!`Ew?D5lvD8qxS?xoz9^MwtdF&W6tJo;eOb}Zim$jR!4*3 z`PY)A>z&FT>jy$IApS2U%fJ0&Nj+;5Cq0vYPSukVHB_*a(1W*O2v#8!&_VyuG&eOj z0dfnKo1#<(!uRos^+^I2RxSAz!(##l2U2EFt52I$9rq`xIJY-ew>v(z^Y-TK$lUZD zw)9ALjKxEZ4t8GOPr9slnAEgAO(*gGde{lU_f&<{!-xT~W%Nyg2bGhHDR*z6L`Y&o zBYbb&BO~5Jv>4-|U?99rte25UMVuyouN){Ha2VsEV<5ftuc!M%Osp;cWE5r>rjqET zL`ZR)USA_oNIXUL9xSB3CuOu9pBn3`vFbuh9p;kcCDWJes<R45%!r6b`5q^vyk}s< z8<(2+qPRLuTue+${@ymwIDj_hjqz6n&`*^yoBk33AkFrVgOUj?U2a;Dd^a`?2Nl&T zieG=2d^e)=R5kJ+hx^?khX|-Cdp(j&^{JHNJ82^x9bA{>XDzzxf@0;R=1H;m%(O6K z^C@Z3crNl(oCZSU_<}Fj2}~8zL}|4&OD?PigGOX3;a+tC4kZn3-4)5DGpykHH1aZ^ zm2i?I2v}pJ+t9<Pv~U<ZbqmJ<N%;;13@S!L;|WE^czF71Q7Z?oEc%ei6?hb*`X$v% z_<Uu`_%f$(5!1`D*YHLbN-1i#3TajTmFmP(`UT}@DG|g*bHAIgN!$dVMGECbfM-W% ztM(N%Ee<IkC5L?3F|vvXrdRu+Gzxj*zgh|=z@VJp9edT2Qw)d_4bpaV6ljVbHo-Lq z)bTIxP(=cVseH$v2@^2EGV*3d-FzCbYe8-C3T4S<lS@%c;$`(d%_lIW*NT3A8F%_c zXZqhqza1i*k?K>*4_7~I`K=0^ypHr1wfZ3*A=t`xEj*F_wg7_t@ZA9c*USgEUBTGV zPz#3=kkh82`l+>=i|{5YrKae5DLS*d^%dER^kY}dO_&BPl5Sz@m+`^3>XoGgqgRsr zg5g?W`ex4k$-xlLOb(dT&QukcKepbbBN?SBvk_ZYD{aAKt)fc3_)V@h3g-q#?D;X4 zDqhz!Mh#8MS%hXM=go(bLSxjs1{98?uJ8Q`V0W9Z{a1(imf0vR-i-DZ8^_IdQZV<E zhTmd34^=?xi)KKDb~2(|EZjJcjuZ2NCHvW=+){4oPmX;G;|lBZktJ#}WZYSIj_MT9 zP87eo1UDJ=@%&xUWB7KEXL02pq1xbka<3@Qd<YR;6SKV^rG?11dm~e}BRHx}ErV*_ zRhWOMXq1I2s#KDi#W`Dbn388lywfuE$lPpHevC~pXU*WA_nB#M&Cc0^uq^?}emZ+o z<l#0I&MpO<V^_hkGMYt$><Y+O)xWE2fahzox$=SUSf1>HOIW?*;93Gi1xHNH^uHN? zv!ZhtKo#t=s$;C9w}3`E4_QUFh#7DifbCbcZ?E)rCb*^G(pwvBO?r0FMsZJgq&=VV z@53H_PPm*A6eP4ov}xEd!_wm!@)TMO=^fGEYrUZsguH^^Z@MSg!8y>~ceyv%;oZv} zy*q8X`nJWi>DVyf8uAQ%#9E^RO(1kVx`Z!um^M1_ph-Db_X<a3-FKKaH~^rnUt~JY z>K!{mUJ~c6SR+DSkwCPs$Ox#EMikBt@*_?Pry52?5D`n!x{If4VUx^iJ;XJ(vWe)o zhOVUG({8o8qOOg==Bo+SNkH$$hUV!&8%PeEzW??I(bJcT-0MW|2Z{R|qmQj3gt#mO z=@ZWUg^2W`p-M<>IZMEppK*2YxGq0HXaT|CbZM3kX9Nvn`Hl9kQ2UE>CI|5+sgC7m zoyUKSG5<HHl~Ix6H#D?2a`>mUrc1@l5laQ-OC-4}jxkKj#O1=;4pCIw(8R1^L9LVA zDfTixDX`g*P|B$7E;3dmdQ#amk5*z_Jr}u8{s}d`_*Osy3Iwvz7S<Lv6R789n(oU1 zHpF8)a>&3;GsfN4`=t9l`F-<n)8+WA>bv^`(--%H!C!iU-Ys*KEX^}~v?R?lcoa!8 z8`^-E2Wv>rlh&WsQ+o)lukk~eR7d{~Ie{+ZCJEen@3Q9Zr7^N*)Kz3?ndk+ydgP@z z(q{DK_qCVkPy=K4_{FoKXHL=w-w+(p3xYTGej9l9u#Dk{;t*7)lhRNGW3})NV)eb} z@4#1hRAS%|Q7}*vX40WWah7DIN5_lCwCR)6e#E6GEcaeTr~Axw8gs;h-ng$>zq~jk z;@^6OrbW%ACKCeSyhKmb4Ty;ZTFZBzYE7~;rfb<VEKJ2biufaSsdjGqnv+J5b+ehp zIm?O;lH+g{ZOQ}^m==qg*5txBmSW&^2~)s!!Yoo_?VkKeQ%LHYS{QxYgn9-VMIIf~ zap~T>!d`?07DR(#66#-(?i3YvY>|cO+$Bd$TrFq;N2<YG;gQ*RJlTH^n*gQSy;d8^ z`IT`jDhP?UoX?f`6;W7hg6!3qVl-45@~m1^s&61TfjCZuSW<8aXZ7q5ChAC#RdUd) z*OQ^5L9`YJL|;^FKcG<l{C;>5w3F1$r5V-dsW~m~+GgF%VJHz@fUvhNQ9*PMW;`7c z0xgMGpPHEAgx*Dg0OO+f%&!*56yBVW(V)0CD`{$~yF9ovRue5uKx|gRM;GBqnpH~3 z`7CaB-M}KLNXKX(m)%3`QxlX~zo#(}VaUmf#&V-6LzQ&7;A)}PXjZapyIjeXXWEMG z;1XCpHnXn^#a>+}+s&<kx*58yAH363SQod6E4Gg#>6VM^8W4DC0~T{V6H$iHx#Ql8 z0xoV(d!S$KH>BGPmT_}OG-nhKYIKre&4Ye%m{s!O?$_+w7*Bof0-7)%(-L3hQC3dB zI35M6C|p${mUz3;^?FvRsu`<SpK~U&z^c^FIBqEgcK799T(r{@mx{?OhD=!4!>|Ti zqT^m2B&B2vnupwqDu5KRi}BZsBp1^a>lN>m=-r&a>4gBP+4J>Zo*AFlkridorgsGK zn61*qv%%S`3cw%!o$|h=GGVBoo}E3GYIX!WsBh?!!at<h9GQl15b4R0EHA~<Tw>uP z*-LlJfuia0P2Sb`{A{1&OuFTUAE-m|!a^&nv@UM3F*&Q9WqRzccE{Q{w&te9Y}EJ$ z8hqjz;?MM>+#pRjgV}BA^sG5qkxc)JF6lWg%S$kQ;qP#iGnA)R)KL!&8xs)$N#KC$ z9`(1O5Hp|-<eeewY_~|7sYS1ymDpF0hkUZ$UZu<{NN<dtp=>mYTv6+ioj8o_EGq(i zEaV?#zw~S%xwuhUJc$nuzQ*8UapdCv9NrlT8<;ump!jQ*>4ohjC1a;?cr0O40>S<G zs2>5t=0QndxqneM53B-eB79-BYdBw<OkAnKl|=8F=WR!j&7zo)0e(H*BhhHHhP)a4 zoMO`vGqU^pjci`{yZ>w^5WW30V_C4_7jpWw-6ZRL3~t)aX;#>6E_Vr;=>FsYPOn@% zn@9ryuo*1*tOdA^V4)Z8UBuplT|UBALUupqsoTS>XMz*zCps{&i|mQ<Kx<Sg{s3lj zR5Oxx2-p@BxP%JGR@IU?7#*QyGVt&SfS+fdNOqxG>`?Iu*OCxcl3Ha{^R&KJqXFvU z?35ZawYhn|7=L?k?gJv>oYR6t!wUYSUIjB9vnUO*s6#?QoA>|)K3j8Muq&lSGYkXf ztD@Bd2WB<L*_o@HyAmJO5&#y*-=JARSFN_2^XxtdnMsTBpzRyv;=Kz3O&!{g;u88K z7mb(DRxl2ld@b(`ACamDG!^Fsc80131I(T4AT(t`_9Y#NbDMG9B*rKnK5f;<LscKL zN+0TgLDmU?yfbN!$j8W56Z}s|>vsB>p`k~Im6_JKQBuOos0_5)AQO!=W`tb}ThwMX zsPm&4OPWSisK=?k$t3|G7>CxP56G`mM&?WO^da`$2e-Wd=5Enc&)(m!glQAgCG6(w z8&mGqBy)NcY5i7*?q7%Q&XdvE#u{)AXoC$77M^rhO+a6bRdN;3bC*KaYJZ;=p=Nuu z2<EWoSvEr62erE4;8u)zButVS8j?nyKrU=6e+ZPtuG;!d8=E5^O$6tRvc34w+F)Wk zfIS5&;_YgzL{Gok>jMd;1kaz}``sCEFt04ut;I_G3MlpSeSq|xR!f3S{oKU2=$(?; zGpx!88BPaTmSv`6eO$ZTyZP}2pA5~d?la>mI>}t8@6+PFUe0(3#c&7(nN@LYudCWx z;ce_?l!*ODnT;B)+-Q2VebTTdw?ITe{h-{cz6qs6Wv6l29W`mDVQr#}a&SGN341I} z1?I>L<ESnPgF`YMq9b}Um`}Y&b=n^|W$EKZm(#@ad}Hhs<IIojy88_FT}_}C7`OGd z2x1k90&J`66V16&^`?(-zHO5%yBQ<o3uU=rvzBni@O&jhYs3@;%UBA!5~}tZx4<!8 zbd_XB9S~)KKD@tR+=1sG5I!Rc4pqFnaQj5Kef&OsaQg+#H+h3cmbffiaWpK(ASDb; z?Dc{73|xCbNH;O_G`6^d!(Fz--)vp^ryqfA-{@C`x6WhfpNb^cY|SVkW}Oj3(wJcI zBGDq@{ontlZ48c&zs3KV9$5Ua(Ep7ZL)PBL)$M<EvPlY7Hi-Q2-l)~sGxLNCOK(e* zrNc?~x!wxmnEB#JrWuOssoraSpw-&XBU`g)Xh`32q=TgC{}pGVCNz(RI8MUK)WLZ_ z!PL;e>*MtfkQ-h?L0nfj$W3!q5+|@FL=Xk$nmUAxn^r4@j1eEQLH8h-hh87$<vpU6 zGalW6LLuXv9W~{_1&x6yw+sb^f;8UPRX4w(*!vC#y_B9Pu;_J%aG+!@hS2t*C3dUD z%EqyB%uVOW>Mn2vj_VX;ey6=_!)7GL^<0Ryh`zOw5Tsq`VOP>Rsk;!+tea})Dqr~w z_Y~+p`Rtho1*^WXeKa<IK3kkuVfahXh>#<Qa<P5}VV0C-k6iY0k*95B#Nu`Yidb-q z_G`C9ZkQ86z@6%(BH8uOjJB<zZHyxm(v3pHJ6DZGcmZY&^^a<uEyyx;A%j)#9AN}w z|DF$Gx-E^O?BZ3-=@ltes@@ge7sUo&nrokII&kDJXwwz+NIEnTt;Rxdx>s3+btIXf zRMfgVEaR~tdT~MS)}gr)g)}mBN2`WODqezhOEtn>3OR(t!;bs|<bGOYH>__X%bcUA zBVqw>1a{O4wHk{$R-sAOAwa4ECjL<+G`x$nt|x}O4q2LS_GnBh(fZu)!6^d(cUhag zCGc|)g42c#7s)QCXoyqv+ZwUVzaO5kXY&-L8{m}D>w-0$trBa0m8c1sh_Q&)d!bPh z2X5YB{<W%&J`7DD`#Ei){|y4+r>gz06ZgMn9-IG=<$rG)Hee+O&CTfdrz{F!CC9*r zt4j<jn_Dq^lIzGfwL@sAf@idLoQA+j>v+y!WX)oD-3<Ela_D2H-Nu1^lMg#sqmmO$ z_@<4v9cMdCUuS!GA8#Gx_<X-%@qvQiT)GMlvrGPtdm@d<pti1arHV##l-zZGpr~59 zaYnk@!GUpcLLWJsxuqTMDIp3mgJI&B(pT<($K-(zou-wUb}Jf|0gkLZU)xfgz4KZ} z^W__$fFicXS`wtvYTRgg@Z4d)S_|}*r*-r59f?=JwThuZCd=Kj)g((rZq4SvOyM@? zFvn0OtSy@>r?DkHZhew8qO$RTQtmc&U@Y=i(yZqSInI-VSi)IzCq7=|Ut4>CFyhv7 zZ*0;_!r|rCXx=nhhg`C<S)+Wu%u0bp&}j&`xd>a}qd^K@xgQ>5F<F;e27&BSB9LUY zg;bWktFOjWwf3t|v$VYdE#dmh6)3nuvvd!-cK%Ytunk*3F#8JXVUNRdH!WVAsOUUh zOa2;9n=jel>HFw2${{z(#F#R&FNcbOYCv6GkB-)AF8~kV)vb67`GgaqX2aX58|c6^ z6M?2ijo#{8=qK%nabjErCJqw;np{gg<DM!DgNNA~5(CWfjH8BlU(`o>+u44$+C5#1 z*YGm%_m?EhGmK~Wbm_J9yt5ZZa4quzNU$`Ln3b#S=junW7QXVkj3T?AX*h;yMM+F` z#i!dK<(m_ZVn7ahtEdu>W?6R-nNhdhuC0rnUbMag3@U;aPE>n15{48@BpW?0W#>h9 z^pK0MyZ_ru^XK1k<e$*Svi&Pa1L|VwXMxOzrgld_Ma@F87woQSNWzWuLg7}<!xfw< zj{|hFsI}Cs?FeR%KMm+nS;i;`4z^fw4IWtG?441mX^PExJPCDlx0t05mNkM1u;~M! z>Vu?f!U*DZK`EoW+Jb!~4*>G}l7S0sDWMB&8KnzsctIbZt=Ps2McZiFN{H<DE<!F0 z$up|2K({$|Y0M<E4^ZyTxiufbM>yzI7e*c^G)j&3ik4^?7$^yHpM_-|CrPAA>4>Br zZt8<+ytBVwEH)9wL&4#?`i5C<;hzVIEW_vy5ID_GgG3{Bp;Th`au~ZCb_tYAp&_Ga zIN3V%VPTcxQZaZ|1WdqhNzw;Z5a%jswrbQPDq~|YRmlB~-(dAa%Sq`Y<7_j;>+KoK z4KLMjzLrD7Y_cWPHiuONm4WBbx&0S~50(wQBviL?0tP!7m5c4P>)b1^A<jb#YTNre zhcFGw9#we}mt=-@?GA;oO6P|KE5U>!#_EoApKaqYMX-0uG;QKiR1=z%-c>oYAzo*0 z%~u9Fw4pjwhgnt!E4!l48%2SU5T8MfKm-&|jHh<FUVP*(9g>brBj&fsai~}mzW<G> zMUN4z!22-;b)x+=)c=o6?GNny>!O(=RS#F>MU?N&j*JOo#=u$x0=_@4r4nM``084+ zdO*E=i1^^l3-TeG;`OzwTE-?p_>YSz&5O;l<&Dj<5mG2}o>uAxz3P17gh_<*is4GQ zRY~58JOk4*mu(FV8J4L@y5A!|VB~T2nl;>YJZ1b`GHne)4|_eH8o7RFi(`#;0E@x1 zL4ouv)$1h@YXfzM!{1FcqUFCqGx7}l+KtZ(e}Kz>m2@MB@p(gpF@I~&Ta{cZ63J_) zR~7m7QilHwVc&y)uKAJ%#fx?U<o_WMs{>iT6$HcR9Rx=5q24C~0S9qvW2hPKjFAG) zh}h$_s|OJywnAJ07&AsbY)s%I89Pj3IoglpD&METNV@YIEgN;8iE)UCpcNLQr~f)= z(?sjUahJXKi@)zT#5B`L++mdhAb?iHAn`}sQ3L9NngeJNDfZGsc`Rmx88eOs)>7mj zCo^hvYwNPYD2WAAdS!O1@*p(R^4a`TCv!)`6u&TN+PU;9QRPN)S3l!M<0<r^ho$5) znPhu1kbeGsb<jnJJZnw#h0<5(sd6zsgA)xbXp6_&rNMOXfOa+^L>H@pn&6`fci9P= zym?aydH1_fp#anfkv;~*=yMu#bErYK8N6jD8cS8k?&k8`S>>AM_%;ddCW(cN#j#jS z>*`F46GNxHQU~Eg10VO8b!#nog~g<jsv8SABC#FBuL0X67$G|T`_DMnm6`Q;vj!NV zwuNmbqK;*8l-~h*H|(f{KMQ1_D}vcV&^*d3E~^3+iA)Eis1~8!^2AzK)8Q<`1|DTK zoP?G?%qLE=)z^y(=>n$J&aQ@ieK8wZ-O{8ey_#u{Q`3liyr5N{%YQvNUwDcS+18l^ ze<t&Dw-^qHus3g$Hld3+zBOWvn8?i({-KvwW1{lP7NTgZ<rP>;$AEOSQqzpUIBh0W z162QWh01EAEl6XP@QGl~?yVQ7C$@a{7$}aZbgDFq;3x%S%7H?E89=wSP<X1I+pCGd zA!3opMdtJHFqwEPFZZeLB)hShKLN!qqAE`hw2mU#%M7wuDmUZX2AS{AoGaI>Vprf_ zvrh^FKmY}vMrtS}uoQ{PpG*m!r^JtrU|zV{k9_1pz7mFpa?zJl2EW>mjM;&KooE9> z7iL9g5aX``yc)s%vpV2Kc#f*LXnwQA&4^xI8*?W8<hLp0m%j?i1rop~$~#JDFwjC> zV07Rz3iiPEN$ARPOdgxohAp$TEFnu1OOvvG^<b24X?&!Tw2mrMM@F*0psS$JTQR<k z+$>E9da#joB}9gO*ZNVS=<x}-h6gZv<<2!*?hbgf0*qC3ZeR<_2hjD5_?26**RAeI zBwu>*Il`t!`HmL0lC2~<S2Pf!H2F)($Yak9XqH?KCb<-gnGFSwhgz@43hnMTbQ0Ri z0S08pMp~0Agi_j8AC$*5`WW2-zX5vM*mS2YHExuMqaak_lmv&K;|SN@>AB7GOzg&D zB}OuLi;3d$q-57L2!emjnX|_@7k83~(Z*9`2iM3wdqfb!q^VeQB*&y_81RWeoT;{C zh5RQ?z6LutR8S0~c!|hFfRIf*dm0MsL|Vr<C_B=kW5Hy%)Ve60npYUDW&?d<rEz_) z{FXVm@4-P6e~t38R_BPpx&V~Nn3zy^S)QttL+YDd66SopqCSC9dhAcCwJ-9YBViOg zWZZQS1Ub3o_(hr^$I8!O_4o5sF7$ku2so14)F49csh5rB)V#PDSgGZX9@GYkOj=%t zMe;1%atq-a9*0US>Mf`-VVq5frZinG5#ppNLM-fZeZ>iZT|?N(^&IthpkCGXAtHCX z9@#%V_@wi2(6BA8l%Wn0%l<=v^yYF*VM}wBam?7vPc-MhsIWevUDaNhY`OHTtC(8Y z!>s%gz~Y8ea((H9xkn`u*6E=fekCB$PaFWllVxj@VuDO$GEdC!6d>6yB_+Awn@C`a z6eD<I@SmtnZ2P(yGQzc`%3u!z#&d|@p&*sc0=CCJ#r2aj`;8%uRMAz`^0#9!>R1yd zkvz_e1);|8pr(t}2c3@mEp%BL+r@^Ohqg8ajORX}LEb2*?)8F+Xa_mQ&(^jb_Y@H$ z5+U8GZ}H&_auiLDRithfxI<dzCMM-Us#m0#?*8K<hwtL!M+s7|BHj#liVJzV^<D8t zeTnjLU9h@%XX&B+kJM^K;mUeXj6nN0lwugqhGhaPu^-Qq*g1q*zgq&@Kz!wB!L<++ zlUO}AW<)#)m3Z#ob80H*wZGPZeRzy|WW+6ndJ%KA!pf7tWZ1TReuf-76SltHa#ats z_1Lx<i$ovYt2cNeJU)dQD_6|j>y)`W@ujb3iljV2G?kIMqLIY4eU`d0a<wrg5Wkfs z2`kc_d+l#`vjZpFk}OeUCy^?%-LfV!*2BD`3OIMFiFmOgkS~S4?IWai@{ebGx^Z3| z-DpuQgFN^Efv8ODTKm%e;`hQ-+rrr;yz7*U>5_jYJ@r?^Oo(pK*C<k74wydQ=u`M1 zoYPk#5n*xKL40bhi9x0~{hjxd!y&)tSeK(pq;rXBP84;bNXZz>Dm5*9h&<&pS}q2l zs48qLY)$pVT0fawg=k|N&RG9iEz>`i=As<fU#puvcSlTd(HAfPtG3li`<oJ>1)(Wy z0H{K?V>O(0JPW^ss<w2Lv%Cgulf5EqY=wT6<Y67l6nSiAVKQZ{Cv{<^DlgL^KV8eC zL&Q{hua>bhISnmw+QvUhpWRcn{zhDvEivFFr5@l`6rL4JzGo3pL^B|bV#!&}9z*3A zD(=Q7-!`*OW>q-dythH)2*dkV%`@_LK~$z5G15dqm8GKYNCH5)k2I}dD!C!4u{*IT z{eIJuZe8^L(GrX^>edsVZntQZ{dxAyOq*ClyZ`(ZTmAXAOdl3aXPh<8ivhRrQOYxh z9rqw)AhmOSB4l;I+5i4Hw!bCr3F>6H#eja>5Aut!dZ&XSt|xHx!$@-9gFc|gAG90o z{<Wet>{<}X2gc6(m&MDuYwX3<UdRop57h9hUgp8|4v;sp$t#5CM9eOdcP30er%qo? zZQh5~@35Z!;+ulXbVHU8^bGNHngKeJHtD;=fg0jh%J>?hFMuyxjp=O@ncD<Qs&BYK zpY+QklNIeRg!N%w!}nJ;=kS;L4`j~%ZwWVcsZ|ca*SFA=JGfU{?_?d(&X@xh;u<n< z*wN#7u0NiUK4U8P(=Y0xusX*hhghZ?V*8|E`x!Wy*UoG&xba<o>I0Z@i*HK>e|zi| zoP@~8`Wwyc>I=?qOfa{M?HMxHQ6Us$lh=JFOX{1*W8-BX9jRybvCczTjR{RlK5Fej z-^ZPbw#Dolmy?%J(+xwOb+9MC|E?_Tey4TWL!BGXTf^<8+_M+I$2x*&MNu%r7JB2c z6h`TKo;T~n-OIVtI5iyk{x=bSOP6Xx_z$&$_A@R2kJ+&QHoj4^Hgo*ntKj4m5P#Nd zRa0L8Vxbe3l_kjoPzD;C=kKyHGX%_r&arW8ofcb+_fbcDRxMCuq+{m1%SjzqAB+qH zT=a84Z`@~HXI@`)^?ZANgWyLv@gh$E(?_tQ5K%G$1l|hKs$rxp)0gTI@U553=`6r6 z-bh-fDE>H&njtyY2c<x-tBu%bK)X!U`nH(eDo94MDAf8!Z(?ZrIoKwcc@|?_=S_{q zr1ZK_HrSD$P;G`bH<UoDS|5vRwKm8Z$gMaGLKCB37^YJ<I7Sq)GgQHghbpj&HU)z8 zp0sp6x(%$GjWkY$>U$)O8g8}e10{k<v|G;K`?hx9NwZ36jb4HJBL=>^NZ%<DDOk)m zD(%-}H?jc32hR;X*#l(Hp~G;E!nr9`C>Gh7#VULBu~j$B)`gfhD#ZQG+tODlcEm>l znk-uV7psroWb38X0h2`5tHV-)7w8vYBqYpnTn>V;X6yWqDalFP_8~b)W9M&wS#Sg+ z6r3)An3atdP~Zfh6AFuTQJBUTN0&YbZn~cgElmUqPYNTATBwh|i{>B6vd*)f1nb?t zacye!tevAeE|Wf~*7T%S9yL7F_dPHseUv5w#76yz2vvlHjvj3j2mU}GL`T1hXBAfV z_EV^>`RRj?ncSfF8{Z5gjUagaMq#}XfUHGq!T+3Lb)v}{_VKCoaj%`H&~Ky+Qb+?8 zE6I2Ihos{RnHzs#cQ0(9m6EAo6NLNI^bRK@7*f%yMC!-et)URR$j5`(v7Qq!bM@^@ z2dv+;r@rFJJ`tvq?eU84K=UszCXLW6!?^jxU$YC>)|X@wDzXD{wjp}2_H*@F0wfe3 z>l6XUS4+*xni&Qv#|~Y7oap>3e`9sK6if8eNFC$<Uvy;udaVB^ZPTUh=Bc=d^0O~4 zLG2EoAnq4HIE0)4AO@l^P$;9JpWmoHs;N3KZURGE>cl`A1boLTs>Zr`g;mp_I;B>k zIhruIBIVES9d+S#bF|BRt;PuFS=u#W?&GP96jmmi1F4r;pR11JEsyT&f21osOM1JV zEP6x(m7)#OR`#iAs`}Nm%f)bRn*P;-#dhqE?$BbkAMxvdRhd&~%}m{iBZ9?t66{@8 zZ4E)g-9I|t*chL*v%cCt^0vm+owTvNm$L32oNU3^;Wz$(eeNEgd~S6c@w@J*`vu2& z6N-hx)*BFW*Eihy3}o#+G8u#Ix)F6Y;7{h-8`JiTWbHjRIgRZ)2LmU~c9RIi>#~Ce z2ruioYfHbzTsfrQo64s*eAHa6Ysril4Fa-3?Dqa%(2Fj`E`Aaz;vB<yBN6{$?y4(^ z1I}?n1>j-BhFroDy+!-|+onV&cYwF>N|C$2acmwZCb$o%0=Sq3!CDh$n6<3mfS)Xz zRA9;U?DEXq?AmS*!%rSpb(`O6yLl<PI+@glRq7)h-YtLiTms`8hQQIiqtj|Vox>et zw*^pm&9|n+Mx&#pYA|dYp-FFM`C^~}a2`p}+a{)_q$TZVy}<b!=i0JaKu-xhG3Yvn z>Q}(j%KFv$sq<dUn{P`)Lf&_w#mmp8o=n&5gOO?k?%45<UEPfWZPbEXXm?X%F&vmd zE}Vkio1<JzJZO>ih1v4a8HF84v&Dnv1q~3fa(MrQ<k3x}(uQ){<pk?q5Q4QWC=g?1 zldH2mDZu7pG2T_B1Gu1oO9LWK|5;q8VcXaSd_>t0*0Ndn@wHqU9`t<rOrt?)<C5Jd z{(29oCX#+5%4$Z|1)k)Q`8DiV;68Q)4YhPGF13lqTi?Z~xjIuHQ2$$4?s?C<N)Ylr zn8k>nm4s-1ZcQ^e{J%(#SH&e76M7a1g&B)s<*gFb`nh`^T+)Q01BRzkt@*WRI|k&O z2T5@nD+&s_y%|Z9nTxkDGp(VB>RHwZsF>I(d9)MIFpFStqlESW{b|RNjG>r9>WRlx zsnLztlIW6G{pw7a(W5Pns&H%|2Zz@e5g_j%8xkz8bQK`MhuL!b*$NVD1jn!VY?*Nh zNBz*SUmE_3AjOx>qk<9|?7BK~*|ns;C8w5GZ^b51LD1Ck>so`F5+h#11ue4m1VvP6 zXwU$`Rou#YN&>dXo5P6q0i|>CCKa+^Vw}pmNNpWO=cy8J)ER_E*YG0Z+|-_|&TP&~ z8}#yJ{xyLU8Orb9uZlCJ&mG!R*jRp~z^{<D>tLLbjN)1aHOifs<g05X!VtSxh-2*I zsnCH@i#c7RZk!Z>*KZmxEFxC5VChzlo4i=;w?HZ6!Xf5vSN1z9`eg*%GV88hOw`d~ zwhh?31fK#~RMujJ6F~U_B>?#qt_QMCKQOs{@-%(vWl)8N@~}$-C}B?#qIydLlA>GT zOE%0H>GWN_Q?-)6gZEP6@3U(JQe%6){SxE<z2^g|J!k=W7$Yl$+e}5Mvx2O5tPQ}j zLWB)zJ)pGVK-sF?imXYuflQH55%Ag!Mcrr{G%GJxs!P|u?Vr&qdc(@}!4u3nEXM5! z`2;20d)XS`F^{}KSxm)-9}Y|@)BU*0Cf`pX%ax`IL01;vSP%Dz%F6MYJMUJjDGg4I z896Mf;?C7rNGm|)OrjhqJ9RhoP+_+?FWa&qNEh_m384!y#k^aghkW&UBte*{nUAeI z{VZs_Y!vRXCfP_&)0<H5ZAJ9tVZMcLsj<Q+Pm%?C+Sc(zgHjMB+|?83%|I$i{7*4D zX=ZoxR5ESOvW$(q#5+e%jR!Y_ibIxN|91mmbD<91Ii6Gbpn^3v`_fd~XSY6V3nqB1 zu~L7%5#S^{Qab9f_=E%ceLRKkk^B_d=#)`?VQps6Wl7lT4H5pR3+}xYH}^WrYXiZk zGfzX)!IZK32pK;X#sjmF=9ml$=cnRr6sh-(;}{-Mv-#_LAej4M4J12>k7}mXTV}>V z=4ywHoD(Tp80o%I{Si}Is+N^HZ3A4y1Qi<+e1rOQxpi7)ha{;n31nF$hGH%*%&%~{ z=*3oeK(x5T5Efz&m^=0O3}}0%Q5w*;qyr~VH||lMfKI=ID9}|I`wV_-vbKJFmw3-Q z{wu<^b^Pbp4U1UTxXxAl9t;xAKXN^R6<DuDkYU~TrZ6Z33+2Qt{UQ|rMMwAp{0ED; z{9js#dLbVXl+#xH1gD^-D#@x5-oOB>9g6mhd%nRZWCNZ{Rb?cdB&4nZwgkktQ<9Yn zw;Rl8gxFUM_Sr(XbFno63AKJh=|r`Q*a2NN&ZF{nz#t*J__M2Ie>4SVeBbFI&G=3$ z*oBxvWL<&hVGZ62ag1seIud4_^N>5_g6uM5s0yW6AYeC(MSfnI!Fs3s1&y>hBBJn1 zmG?h>MQ743g%Zlx0F^(cBz1fNsG+)w3f41;S(Tp1!l!Q@&~MUO-}XY2j}h~aHNO{Q zjP`Tls(qr$ys-_A-bha0mebCz3vb9Cx*F4g&h<an3dp6V)4U3wFa%-SA3p8O-ib`{ zT1@;niEyrd^?ETtZL}uOsq9#(?HCvLZ%WyQ)c>`cqTg7*g@vINuwc8UVq^J9Zynlp zLrNXgVengBbCGuM+mc`&J}qyDYp~<R$p(WCDHSNG{L5Jcw&8(hq@7Ld(<0IxVmMUJ zOr%pDH(i%j%PV;i1OE=8?~_=ImvC`RI^+2CC#y@|;OBvTV7I&FQD#!`%H0}%w2~ju zWQ&Y&?^s{jS!Fy5d?Z~gOE?sxK%GO+rdg<fy6Y7`Cp`M(?Pj4#gG2}SGczSw3}G&( zDI6{7P?bj*?|e!xBGr_cd#32OlC5+=vC!P;5V3D#S^8jJvbfdT>;<lHwCx2x%1Gdm zg5^pW?2Va$NOz>?ACxo9Kfje@dPY^2+L1bvoSu7bqrUQj##`R#@N+-CB4M{#aEW1G zUAtQYWB5czeVB!^KeDO~Yc?o7DLC(8%=&bj_zMNfJMQ#kKT2%66(qBhuqHyv{=8RA zjnN=$EUCmX8v%Sjm&j;L$CE^3FcnWQx#OPJU6ZLlv=J#|GMd^jDXv2Trkx=$bWu>j zEHOmQXoUE?HRv;u@d(xHd5hejoZSGmq@jaB5LueQ;Ay|1cwKB+E9#-)cUD>IN2cAJ z)dx1uH|TmRp3aP_;Qy%XT%e)O;y6A=9$Sh$LPXgl<5j7nhaM`EiSfv&mUbqjZK7eC z*;HhkL{U*5O`}LPwq#cxg+fariaJ6sIz`3QFqx=q+U%WO<8pV#{@rutoIhvo=X?M6 z{{Q#P|9^k?`#V@(=ubD<)9$vpX06XeS~jo|cE4>|5#@bZi~H_&hWEFScyjecm8Kcw zoD5!w1*5^`$z`6qiEWJK#+14PdwSbz?TV=9ecaERYJJu$i83##2{BGLYq~9t@>=b~ zJywulNIO+2-m*1$J1@CT%vUIUHEF`4_EtlmlZ-VB-aKhL!?IMTB`l;>?F`YO>}Ls7 zlwwo2_%6M#I1uWvH2I|U-X+4!Ahy5Kz75&DNUz?~&2?uUBd_eQC@aLfgB=Sl-~kH! z*u*gc*fzd_EIQ+-Z8pK7JGT3V&{;N=;NYOaC#IhhX+hdvAX7v0_09rkix7+TlbHg? z9g}wja_~Feo=p`MM0Kri$*WGh&j>E?ylA^+SL{@Bqwa<8+@`KQTCPFrC4B>H3KW4s zZcOm3S<C2tD_8FBsM{^F;w`&$y7teTFRp)1JF|!t+V#FU6jyTKsT%dxWKp;}pLta$ zQh%df+bb2$dbOT#Z{ggu&}(tsUo+H6YxYEtg?bN`KU{o&+FW8vhwB4EW<{z;omR`D zjO%67Xsi=Uj7v$2Yf^i-q>Du?vHC;0Xj3jJp4D-gy8Hyu{Fr`pV~*}_v1(*fgHOOq z*M(Q~3X)1zBy+jj2`Q|=WdW<re=Ck?I%Z!NpJ-3*PElLp%!y@*e8@K2EMMO$<W6~> zPqnqvjgDX)K9^;r+(FTHyF}zu1`g_G?(K|HsQ8rT7fi2T+R+|vx9v57xmriCy7_CB zk(g$%uIJY&iXb3({QJhHA4*T}t)bj{b16GEO=RQmUHuN<1w!aoeJ1A8Uu>B8*f@Ub zJ!it<L=&~AA6>g{7>x4`dYsgiwY4Emx1~&!Ur|jR|NXT%73D8^H`qMyb9V^b32Ii0 zH!4lzF?CWK%P$M6300|&NaG*T!G8i$8#x4z7V$u2z>^zd>-=kuDO@AL9~YZ^Dj6kC zq3#3TWxdm9i%jpHnD>wz7R-4*=bnwH3vY|vCyyZ7i~WC9C(K*?a80ow#5p-T*YWzH zojrHC)lT(0TP@gMal)f73v=FPYiA}qZ_|3q3l`^V|E~N@H6qExf3t1*0`q4R*&5+i zm0HRL8k>&$WpQ?lJL@z<jjdT?R+*?=pfzrteL(t=vg9_p;scFZQf@_A{<y?-o%6d5 z%hR=s)$HAJXycp97<ivphGIgxXY5jC>U5|2Bb~)*wF2ADxdWm}y%#1N%9)^;w8$lJ z&FrwGw&}z<2l3HN{GINp^+LT$1HH&w3&USeE-x=WRi<)P=&#K*O6)WA?87${9evdo z?HX}Nli{aa=ut)3%2dD7rhDf06^b~N(rwlrV(evKrr%S#zMgcp`MlAW%!Z)0XidB1 zBF~sCQ)`!&{+RyxOpBP}gd$-~zd187rr(PBi)oyw_EkUsxq=}-?Bo6q!mLBH@0t78 z+iq^(*JteRtbo^61dn>Es<^Ai-HG59#DVu2{}ixzKUg$yT@I2%vLnU8isVWJS;j|J z%lic{0uih@eeltid~>kyIp*MKs2bV9l|*#(q*#-Q!wD=z7(@;X7K!jKa4eY?Myml_ zyN1<B8LT0lNJguvBdv&&XjLIKho&GDA=Ja$1c6<-m7udEo5!Vv&*=w;GG{RdKNfqZ zuRnXpi*Oa4y=Uesm?;R=xH&5~Q(_pM2L?itA4*#IoMNDgUE|+JbClltA@iFHI0G_Z zNuq+Jh0n1BId-wprKciWq1r&h_db~0l|cI?8)u}2&$$aCo-rxN23u{kstE(lZU&t* z65I+dgi*mG*Lu-D+3;4NN(Eqj0G7DGNeiEoq5eN$SqZvOyrp@cW(L521o)8yx=s@- zUjF5RCO(IacLW#tsJ$X>JhT--n~vsRhHwLL&trlQipU;PG&3yZw;E#Q59NtP8;>U1 zgz)#q*x<4ojV5p##x<40MN&`=x8y-vjpiML^pj>7>GCfYG+!2kF99>Q%<)8;F8^9V zQ$0a;3jrG&CVoOY1x>O8**8H-5-jb8DR)pdn%@Mn&w{%Z6I+_?1Vy9%{}BBQ1gc;` zLnjB6je563_TGh<*s?Ef)MXc9&6dl-%J#>c`5(_+^bAn*O(+$%YD`kH&jfX%gZMfp zEO^;e)QSsIO<d$qNAA3aro^Mo0;o+Cl<6XmQAYk9i(2785r)SYMTU%V&_PChOrVAi zWsDl~?*-Is1Bx_xjZH-Mprae?A>Y9p6JItK-C_)}Cx4WK9l81<4bw(D9o@+b!H22X zz$5qdqI`7w8szu*W8<Tn*-!;EcpWNi2*Ru|B9t9fK!cT`LJebV3bF?mjY@?46)epB z(SNt1%g|6FZyz>^VT;me|DcP35FWE16I^z;pl^O4wkK8&cG!C$cV{Kwn<vSePQVpS L!r?e^lE3~2PkGp6 literal 0 HcmV?d00001 diff --git a/licenses/LICENSE.txt b/licenses/LICENSE.txt index 4d3d187..9690435 100644 --- a/licenses/LICENSE.txt +++ b/licenses/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/licenses/OWM JAPIs License.txt b/licenses/OWM JAPIs License.txt new file mode 100644 index 0000000..24a127e --- /dev/null +++ b/licenses/OWM JAPIs License.txt @@ -0,0 +1,19 @@ +Copyright (c) 2013-2015 Ashutosh Kumar Singh <me@aksingh.net> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 7002b59..7febf85 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -38,26 +38,6 @@ <property name="caretWidth" class="java.lang.Integer" /> </properties> </component> - <component name="CompilerConfiguration"> - <option name="DEFAULT_COMPILER" value="Javac" /> - <resourceExtensions /> - <wildcardResourcePatterns> - <entry name="!?*.java" /> - <entry name="!?*.form" /> - <entry name="!?*.class" /> - <entry name="!?*.groovy" /> - <entry name="!?*.scala" /> - <entry name="!?*.flex" /> - <entry name="!?*.kt" /> - <entry name="!?*.clj" /> - <entry name="!?*.aj" /> - </wildcardResourcePatterns> - <annotationProcessing> - <profile default="true" name="Default" enabled="false"> - <processorPath useClasspath="true" /> - </profile> - </annotationProcessing> - </component> <component name="CopyrightManager" default=""> <copyright> <option name="myName" value="Copyright" /> @@ -81,9 +61,6 @@ <component name="Encoding"> <file url="PROJECT" charset="UTF-8" /> </component> - <component name="EntryPointsManager"> - <entry_points version="2.0" /> - </component> <component name="GradleLocalSettings"> <option name="modificationStamps"> <map> @@ -175,10 +152,15 @@ <option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" /> </inspection_tool> </profile> + <option name="useProjectProfile" value="false" /> <option name="PROJECT_PROFILE" /> <option name="USE_PROJECT_PROFILE" value="false" /> <version value="1.0" /> </component> + <component name="KotlinCommonCompilerArguments"> + <option name="languageVersion" value="1.1" /> + <option name="apiVersion" value="1.1" /> + </component> <component name="MavenImportPreferences"> <option name="generalSettings"> <MavenGeneralSettings> @@ -227,7 +209,7 @@ <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" assert-keyword="true" jdk-15="true" project-jdk-name="1.8.x" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8.x" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="PropertiesComponent"> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 6110975..bf2f692 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -41,3 +41,8 @@ tell-max-size=50 # #google-api= #google-cse-cx= + +# +# Get OpenWeatherMap API key from: https://openweathermap.org/ +# +owm-api-key= diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index f7d0fd9..aeeacb0 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,9 +13,9 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "009"; + private final static String buildmeta = "010"; private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1468895453561L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1491181046873L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 68515e0..43d62aa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1,7 +1,7 @@ /* * Mobibot.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -254,7 +254,7 @@ public class Mobibot extends PircBot { MODULES.add(new StockQuote()); MODULES.add(new Twitter()); MODULES.add(new War()); - MODULES.add(new Weather()); + MODULES.add(new Weather2()); MODULES.add(new WorldTime()); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java new file mode 100644 index 0000000..ea8a3ed --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -0,0 +1,172 @@ +/* + * Weather2.java + * + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.aksingh.owmjapis.CurrentWeather; +import net.aksingh.owmjapis.OpenWeatherMap; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; + +import java.io.IOException; + +/** + * The <code>Weather2</code> class. + * + * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @created 2017-04-02 + * @since 1.0 + */ +public class Weather2 extends AbstractModule { + + /** + * The weather command. + */ + public static final String WEATHER_CMD = "weather"; + + // The OpenWeatherMap API Key property. + private static final String OWM_API_KEY_PROP = "owm-api-key"; + + /** + * Creates a new {@link Weather} instance. + */ + public Weather2() { + commands.add(WEATHER_CMD); + properties.put(OWM_API_KEY_PROP, ""); + } + + private String capitalize(final String s) { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * {@inheritDoc} + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); + } + + private String fAndC(final Float f) { + final Float c = (f - 32) * 5 / 9; + return Math.round(f) + " \u00B0F, " + Math.round(c) + " \u00B0C"; + } + + /** + * {@inheritDoc} + */ + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To display weather information:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEnabled() { + return isValidProperties(); + } + + /** + * Fetches the weather data from a specific country. + */ + private void run(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + final OpenWeatherMap owm = new OpenWeatherMap(properties.get(OWM_API_KEY_PROP)); + if (Utils.isValidString(args)) { + final String[] argv = args.split(","); + + if (argv.length >= 1 && argv.length <= 2) { + final String country; + final String city = argv[0].trim(); + if (argv.length > 1 && Utils.isValidString(argv[1])) { + country = argv[1].trim(); + } else { + country = "US"; + } + + try { + final CurrentWeather cwd = owm.currentWeatherByCityName(city, country); + if (cwd.hasCityName()) { + bot.send(sender, "City: " + cwd.getCityName() + " [" + country + "]", isPrivate); + + final CurrentWeather.Main main = cwd.getMainInstance(); + if (main.hasTemperature()) { + bot.send(sender, "Temperature: " + fAndC(main.getTemperature()), isPrivate); + } + + if (main.hasHumidity()) { + bot.send(sender, "Humidity: " + Math.round(main.getHumidity()) + "%", isPrivate); + } + + if (cwd.hasWindInstance()) { + final CurrentWeather.Wind w = cwd.getWindInstance(); + if (w.hasWindSpeed()) { + bot.send(sender, "Wind: " + wind(w.getWindSpeed()), isPrivate); + } + } + + if (cwd.hasWeatherInstance()) { + CurrentWeather.Weather w; + final StringBuilder condition = new StringBuilder("Condition: "); + for (int i = 0; i < cwd.getWeatherCount(); i++) { + w = cwd.getWeatherInstance(i); + if (i != 0) { + condition.append(", ").append(w.getWeatherDescription()); + } else { + condition.append(capitalize(w.getWeatherDescription())); + } + } + bot.send(sender, condition.toString(), isPrivate); + } + + return; + } + + } catch (IOException e) { + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Unable to perform weather lookup: " + args, e); + } + + bot.send(bot.getChannel(), "Unable to perform weather lookup: " + e.getMessage()); + } + } + } + + helpResponse(bot, sender, args, isPrivate); + } + + private String wind(final Float w) { + final double kmh = w * 1.60934; + return Math.round(kmh) + " km/h, " + Math.round(w) + " mph"; + } +} \ No newline at end of file diff --git a/version.properties b/version.properties index 30dac58..c5b0147 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=009 +version.buildmeta=010 From ef59258eec059d51463fdc2ac14951a77660bf81 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 18:52:53 -0700 Subject: [PATCH 058/842] Added OWM url for queried city. --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 4 +++- version.properties | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index aeeacb0..d1e5ca5 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,9 +13,9 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "010"; + private final static String buildmeta = "014"; private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1491181046873L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1491184254508L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index ea8a3ed..2023256 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -149,6 +149,8 @@ public class Weather2 extends AbstractModule { bot.send(sender, condition.toString(), isPrivate); } + bot.send(sender, Utils.green("https://openweathermap.org/city/" + cwd.getCityCode()), isPrivate); + return; } @@ -167,6 +169,6 @@ public class Weather2 extends AbstractModule { private String wind(final Float w) { final double kmh = w * 1.60934; - return Math.round(kmh) + " km/h, " + Math.round(w) + " mph"; + return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; } } \ No newline at end of file diff --git a/version.properties b/version.properties index c5b0147..16901a5 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=010 +version.buildmeta=014 From 4dfdb19636a9a4ac6a5b74de48e9b748a504643e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 19:11:51 -0700 Subject: [PATCH 059/842] Added TravisCI configuration. --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..58e8252 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: java + +jdk: + - oraclejdk8 + +before_install: + - chmod +x gradlew \ No newline at end of file From e14357c295cf8e71f73d2504033c4eda4f254d32 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 19:23:46 -0700 Subject: [PATCH 060/842] Added build status. --- README.txt => README.md | 4 ++++ 1 file changed, 4 insertions(+) rename README.txt => README.md (64%) diff --git a/README.txt b/README.md similarity index 64% rename from README.txt rename to README.md index 0ece869..7def4ad 100644 --- a/README.txt +++ b/README.md @@ -1,5 +1,8 @@ +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause)[![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) + Some very basic instructions: +``` { clone with git or download the ZIP } git clone git://github.com/ethauvin/mobibot.git @@ -21,3 +24,4 @@ Some very basic instructions: { launch } /usr/bin/nohup java -jar mobibot.jar & +``` \ No newline at end of file From 3d753f192764029f11de451073a0854fbe9eb7b9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 19:23:58 -0700 Subject: [PATCH 061/842] Upgrade Gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 53636 -> 54212 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 68 +++++++++++++---------- gradlew.bat | 14 ++--- 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372aef5e24af05341d49695ee84e5f9b594659..60bd3bc497ba831b732a27ae2cf9ccec20be152a 100644 GIT binary patch delta 24988 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 D{5G71 delta 24260 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aad1c32..c07e7f6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Jun 25 11:57:02 PDT 2016 +#Sun Apr 02 19:18:15 PDT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip diff --git a/gradlew b/gradlew index 9d82f78..4453cce 100644 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..f955316 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line From 38852c29ed60ba49fb3010b815fb0fb1dd021cd6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 19:26:16 -0700 Subject: [PATCH 062/842] Added space between badges. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7def4ad..18969f5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause)[![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) Some very basic instructions: From 312c0b572fbd6cd473d3635109a9d9c3178874cd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 19:39:06 -0700 Subject: [PATCH 063/842] Updated dependencies. --- build.gradle | 14 +++++++------- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- version.properties | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index c4c58ab..3a00d21 100644 --- a/build.gradle +++ b/build.gradle @@ -54,22 +54,22 @@ dependencies { compile 'commons-codec:commons-codec:1.10' compile 'commons-logging:commons-logging:1.2' - compile 'commons-net:commons-net:3.5' - compile 'commons-cli:commons-cli:1.3.1' + compile 'commons-net:commons-net:3.6' + compile 'commons-cli:commons-cli:1.4' compile 'commons-httpclient:commons-httpclient:3.1' compile 'oro:oro:2.0.8' - compile 'org.jsoup:jsoup:1.10.1' - compile 'com.rometools:rome:1.7.0' - compile 'org.slf4j:slf4j-log4j12:1.7.21' + compile 'org.jsoup:jsoup:1.10.2' + compile 'com.rometools:rome:1.7.1' + compile 'org.slf4j:slf4j-log4j12:1.7.25' compile 'org.json:json:20160810' compile 'org.ostermiller:utils:1.07.00' compile 'net.sourceforge.jweather:jweather:0.3.0@jar' - compile 'net.objecthunter:exp4j:0.4.7' + compile 'net.objecthunter:exp4j:0.4.8' - compile 'org.twitter4j:twitter4j-core:4.0.5' + compile 'org.twitter4j:twitter4j-core:4.0.6' compile 'net.sf.delicious-java:delicious:1.14' compile files('lib/owm-japis-2.5.0.5.jar') diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index d1e5ca5..4ec5102 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,9 +13,9 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "014"; + private final static String buildmeta = "015"; private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1491184254508L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1491187126281L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/version.properties b/version.properties index 16901a5..1dcf088 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=014 +version.buildmeta=015 From 8aaf9f1246b3ca9f2648e069fc52c80d629d7597 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 20:15:22 -0700 Subject: [PATCH 064/842] Improved weather help. --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 4 ++++ version.properties | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 4ec5102..28233c9 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,9 +13,9 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "015"; + private final static String buildmeta = "016"; private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1491187126281L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1491189257001L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 2023256..56ce4e8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -87,6 +87,10 @@ public class Weather2 extends AbstractModule { public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To display weather information:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); + bot.send(sender, "For example:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + "paris, fr")); + bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") + + ". Zip codes are supported in the US."); } /** diff --git a/version.properties b/version.properties index 1dcf088..2979c5a 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=015 +version.buildmeta=016 From bbbe8930a76cf061f27314f79c340d6adb21648d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 23:19:19 -0700 Subject: [PATCH 065/842] Spacing cleanup. --- README.md | 6 +++--- build.gradle | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 18969f5..077ae76 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Some very basic instructions: ``` { clone with git or download the ZIP } git clone git://github.com/ethauvin/mobibot.git - + cd mobibot - + { build with gradle } ./gradlew @@ -15,7 +15,7 @@ Some very basic instructions: { configure the properties } vi *.properties - + { help } java -jar mobibot.jar -h diff --git a/build.gradle b/build.gradle index 3a00d21..69a9de8 100644 --- a/build.gradle +++ b/build.gradle @@ -73,7 +73,7 @@ dependencies { compile 'net.sf.delicious-java:delicious:1.14' compile files('lib/owm-japis-2.5.0.5.jar') - + compileOnly 'net.thauvin.erik:semver:0.9.6-beta' } @@ -142,6 +142,6 @@ task release(dependsOn: ['wrapper', 'clean', 'deploy']) { doLast { group = 'Publishing' description = 'Releases new version.' - isRelease = true + isRelease = true } } \ No newline at end of file From 87dc05d3925a9ab74b491b8700490704982293e7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 23:20:25 -0700 Subject: [PATCH 066/842] Commented out OWM api key property. --- properties/mobibot.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index bf2f692..cbb39c4 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -45,4 +45,4 @@ tell-max-size=50 # # Get OpenWeatherMap API key from: https://openweathermap.org/ # -owm-api-key= +#owm-api-key= \ No newline at end of file From f8906aaf6360eea20dc83e2f76d021ea0ef8cfec Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Apr 2017 23:20:49 -0700 Subject: [PATCH 067/842] Updated Kobalt build dependencies. --- kobalt/src/Build.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index b054d5e..129bbde 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -50,24 +50,26 @@ val p = project { compile("commons-codec:commons-codec:1.10") compile("commons-logging:commons-logging:1.2") - compile("commons-net:commons-net:3.5") - compile("commons-cli:commons-cli:1.3.1") + compile("commons-net:commons-net:3.6") + compile("commons-cli:commons-cli:1.4") compile("commons-httpclient:commons-httpclient:3.1") compile("oro:oro:2.0.8") - compile("org.jsoup:jsoup:1.9.2") - compile("com.rometools:rome:1.6.1") - compile("org.slf4j:slf4j-log4j12:1.7.21") - compile("org.json:json:20160212") + compile("org.jsoup:jsoup:1.10.2") + compile("com.rometools:rome:1.7.1") + compile("org.slf4j:slf4j-log4j12:1.7.25") + compile("org.json:json:20160810") compile("org.ostermiller:utils:1.07.00") compile("net.sourceforge.jweather:jweather:jar:0.3.0") - compile("net.objecthunter:exp4j:0.4.7") + compile("net.objecthunter:exp4j:0.4.8") - compile("org.twitter4j:twitter4j-core:4.0.4") + compile("org.twitter4j:twitter4j-core:4.0.6") compile("net.sf.delicious-java:delicious:1.14") + compile(file("lib/owm-japis-2.5.0.5.jar")) + apt(processorJar) compile(processorJar) } From 57a71a8ea53222d0a3ad6193961ee77862789977 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 3 Apr 2017 09:43:49 -0700 Subject: [PATCH 068/842] Fixed space in weather help. --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 4 ++-- version.properties | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 28233c9..5ded894 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,9 +13,9 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "016"; + private final static String buildmeta = "017"; private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1491189257001L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1491237382787L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 56ce4e8..17077f5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -88,7 +88,7 @@ public class Weather2 extends AbstractModule { bot.send(sender, "To display weather information:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); bot.send(sender, "For example:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + "paris, fr")); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") + ". Zip codes are supported in the US."); } @@ -175,4 +175,4 @@ public class Weather2 extends AbstractModule { final double kmh = w * 1.60934; return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; } -} \ No newline at end of file +} diff --git a/version.properties b/version.properties index 2979c5a..74f8acd 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=016 +version.buildmeta=017 From 4de4d001415ad1753b2a90d3cabe179b0d0647e1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 3 Apr 2017 09:55:04 -0700 Subject: [PATCH 069/842] Added recap empty response. --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 10 +++++++--- version.properties | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 5ded894..913e103 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,9 +13,9 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "017"; + private final static String buildmeta = "018"; private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1491237382787L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1491238266337L), ZoneId.systemDefault()); private final static int major = 0; private final static int minor = 7; private final static int patch = 0; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 43d62aa..c617572 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1435,8 +1435,12 @@ public class Mobibot extends PircBot { * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void recapResponse(final String sender, final boolean isPrivate) { - for (final String recap : this.recap) { - send(sender, recap, isPrivate); + if (this.recap.size() > 0) { + for (final String recap : this.recap) { + send(sender, recap, isPrivate); + } + } else { + send(sender, "Sorry, nothing to recap.", true); } } @@ -1684,4 +1688,4 @@ public class Mobibot extends PircBot { send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate); } } -} \ No newline at end of file +} diff --git a/version.properties b/version.properties index 74f8acd..75c612b 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=0 version.prerelease=beta -version.buildmeta=017 +version.buildmeta=018 From 67884791f451d57d487e2f4da9e11ec23d55ee84 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 3 Apr 2017 10:03:10 -0700 Subject: [PATCH 070/842] Updated to Kobalt 1.0.46 --- kobalt/wrapper/kobalt-wrapper.jar | Bin 11230 -> 11264 bytes kobalt/wrapper/kobalt-wrapper.properties | 2 +- kobaltw | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 kobaltw diff --git a/kobalt/wrapper/kobalt-wrapper.jar b/kobalt/wrapper/kobalt-wrapper.jar index 696ac55f0e460946fd8f657732a41a26126faf84..3acc29a5b6afe2235fb862f2b7f728cc733c8c7f 100644 GIT binary patch delta 9062 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%0D(X{RB|q(HxU*Q&&{w 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 LMebqxZ{`00<8tsm delta 9028 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 diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index 708f458..d12a84e 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.45 \ No newline at end of file +kobalt.version=1.0.46 \ No newline at end of file diff --git a/kobaltw b/kobaltw old mode 100644 new mode 100755 index c5186d5..287d453 --- a/kobaltw +++ b/kobaltw @@ -1,2 +1,2 @@ -#!/usr/bin/env sh +#!/bin/env sh java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $* From 88169c111b866f1509ebc63dd424981db21e52f3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 3 Apr 2017 10:18:35 -0700 Subject: [PATCH 071/842] Changed clone URL to https. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 077ae76..989b506 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Some very basic instructions: ``` { clone with git or download the ZIP } - git clone git://github.com/ethauvin/mobibot.git + git clone https://github.com/ethauvin/mobibot.git cd mobibot From 8ac439d04738d5288266a06a8f9e63b99773b32d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 3 Apr 2017 12:24:49 -0700 Subject: [PATCH 072/842] Updated website with new weather module. --- .idea/modules/mobibot.iml | 132 +++++++++++++++++++------------------- mobibot.ipr | 60 ++++++++--------- website/index.html | 6 +- 3 files changed, 100 insertions(+), 98 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 7651f0b..2e3e406 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.0-beta+009" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.0-beta+018" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/main" /> <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> @@ -17,7 +17,70 @@ <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.6-beta" level="project" /> - <orderEntry type="module-library" scope="TEST"> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> + <orderEntry type="module-library" scope="PROVIDED"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> @@ -35,7 +98,7 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="module-library" scope="PROVIDED"> + <orderEntry type="module-library" scope="TEST"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> @@ -44,68 +107,5 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.5" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160212" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.5" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160212" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.5" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.3.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.9.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.6.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.21" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160212" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.7" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.4" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.6.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.21" level="project" /> </component> </module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 7febf85..ae1f521 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -314,31 +314,31 @@ <mapping directory="$PROJECT_DIR$" vcs="Git" /> </component> <component name="libraryTable"> - <library name="Gradle: com.rometools:rome-utils:1.6.1"> + <library name="Gradle: com.rometools:rome-utils:1.7.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.6.1/b66628b06492dd8a9fa44d90d1733f843c285589/rome-utils-1.6.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.1/36592957a19bbfdb6f1182ea3e6036bccaec0bdc/rome-utils-1.7.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.6.1/450c47fd73c6ec2d815059f284dfe3d613e45df/rome-utils-1.6.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.1/3fa41b350dc73b3338322471294a83b5bbb1bbae/rome-utils-1.7.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.6.1"> + <library name="Gradle: com.rometools:rome:1.7.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.6.1/fb844f8d9b7c1e324d150ca6ebb791cfd9b33243/rome-1.6.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.1/b15f1e5277ffa19077672ad7f889a636e661d6ee/rome-1.7.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.6.1/d5c50cf970dde36378a5f7bf8b2273e82723535/rome-1.6.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.1/2bab37f921df082d245d440f96dc63a69cb5f67/rome-1.7.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-cli:commons-cli:1.3.1"> + <library name="Gradle: commons-cli:commons-cli:1.4"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/1303efbc4b181e5a58bf2e967dc156a3132b97c0/commons-cli-1.3.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.3.1/12ec02d8cb9fbb33bd05506109a4fc8bcc3578/commons-cli-1.3.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/40dfd9fdef125e19136135e68d54af6d9b0cfbb8/commons-cli-1.4-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: commons-codec:commons-codec:1.10"> @@ -386,13 +386,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/ecf26c7507d67782a3bbd148d170b31dfad001aa/commons-logging-1.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-net:commons-net:3.5"> + <library name="Gradle: commons-net:commons-net:3.6"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.5/342fc284019f590e1308056990fdb24a08f06318/commons-net-3.5.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.5/185dea3736a8583323da4d6ce647719ddf452ccf/commons-net-3.5-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/961dc27eabbe71bf32478baffe0e1be915ce7689/commons-net-3.6-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: log4j:log4j:1.2.17"> @@ -404,13 +404,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.objecthunter:exp4j:0.4.7"> + <library name="Gradle: net.objecthunter:exp4j:0.4.8"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.7/f910cbcc730e5bd5e7699d8c14905c07c6c12d47/exp4j-0.4.7.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.7/26e7ad8ecde8e7bb1e9d465e8a0b6ffe8dbca2ea/exp4j-0.4.7-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/8d86f148ff1f0d5b624eae9bb0882198ab5cd07/exp4j-0.4.8-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: net.sf.delicious-java:delicious:1.14"> @@ -458,22 +458,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20160212"> + <library name="Gradle: org.json:json:20160810"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160212/a742e3f85161835b95877478c5dd5b405cefaab9/json-20160212.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160810/aca5eb39e2a12fddd6c472b240afe9ebea3a6733/json-20160810.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160212/6b4ee9a02a4ed9eb92efe6104c424f70c822c8c8/json-20160212-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160810/92f4b89bc0bb4c7c3f7b52724568a901b7e0195b/json-20160810-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jsoup:jsoup:1.9.2"> + <library name="Gradle: org.jsoup:jsoup:1.10.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.9.2/5e3bda828a80c7a21dfbe2308d1755759c2fd7b4/jsoup-1.9.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.10.2/33ee82e324f4b1e40167f3dc5e01234a1c5cab61/jsoup-1.10.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.9.2/97441f4a0f6bb4c2d9f1e19321f4a45c9ffd0e2c/jsoup-1.9.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.10.2/705a50ce237c266feaa279797712cb6bb6c1f34d/jsoup-1.10.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.ostermiller:utils:1.07.00"> @@ -485,31 +485,31 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.slf4j:slf4j-api:1.7.21"> + <library name="Gradle: org.slf4j:slf4j-api:1.7.25"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.21/139535a69a4239db087de9bab0bee568bf8e0b70/slf4j-api-1.7.21.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.21/f285ac123f201fb4b028bac556928d7cf527ef48/slf4j-api-1.7.21-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/962153db4a9ea71b79d047dfd1b2a0d80d8f4739/slf4j-api-1.7.25-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.slf4j:slf4j-log4j12:1.7.21"> + <library name="Gradle: org.slf4j:slf4j-log4j12:1.7.25"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.21/7238b064d1aba20da2ac03217d700d91e02460fa/slf4j-log4j12-1.7.21.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.25/110cefe2df103412849d72ef7a67e4e91e4266b4/slf4j-log4j12-1.7.25.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.21/16b1333786ea93d16bff6fb0f5e3b82716d1b008/slf4j-log4j12-1.7.21-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.25/d6f907c4254a49f40fa46005b65bc97261ad9e46/slf4j-log4j12-1.7.25-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.twitter4j:twitter4j-core:4.0.4"> + <library name="Gradle: org.twitter4j:twitter4j-core:4.0.6"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/1f3c896c7b2f20c51103078ccf0bc2ea97ac012a/twitter4j-core-4.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/f3722af4568b96ee66739267e13211b8b66ac7d4/twitter4j-core-4.0.6.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.4/5fb9d7d5700c56e9a5202a95a2fd6c2159cb33b7/twitter4j-core-4.0.4-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/5a3a910793c3510a3e25cea3c51672bb88124527/twitter4j-core-4.0.6-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: oro:oro:2.0.8"> diff --git a/website/index.html b/website/index.html index 58ca775..1fdb583 100644 --- a/website/index.html +++ b/website/index.html @@ -43,7 +43,7 @@ <li><a href="http://commons.apache.org/proper/commons-logging/">Commons Logging</a></li> <li><a href="http://commons.apache.org/proper/commons-net/">Commons Net</a></li> <li><a href="https://github.com/czarneckid/delicious-java">delicious-java</a></li> - <li><a href="http://sourceforge.net/projects/jweather/">JWeather</a></li> + <li><a href="https://bitbucket.org/akapribot/owm-japis/">OWM JAPIs</a></li> <li><a href="http://www.objecthunter.net/exp4j/">exp4j</a></li> <li><a href="http://ostermiller.org/utils/">OstermillerUtils</a></li> <li><a href="http://rometools.github.io/rome/">Rome</a></li> @@ -73,7 +73,9 @@ <div><code>mobibot: google mobitopia on irc</code></div> </li> <li>Displaying weather information - <div><code>mobibot: weather KSFO</code></div> + <div><code>mobibot: weather san francisco</code></div> + <div><code>mobibot: weather 94123 </code></div> + <div><code>mobibot: weather tokyo, jp</code></div> </li> <li>Performing DNS lookups <div><code>mobibot: lookup www.apple.com</code></div> From 492bf96e02883e9e9987333b97b0990561fdf61b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 8 Apr 2017 18:50:26 -0700 Subject: [PATCH 073/842] Fixed description. --- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 17077f5..123fe87 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -102,7 +102,7 @@ public class Weather2 extends AbstractModule { } /** - * Fetches the weather data from a specific country. + * Fetches the weather data from a specific city. */ private void run(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { final OpenWeatherMap owm = new OpenWeatherMap(properties.get(OWM_API_KEY_PROP)); From 8a8de137d1fba7934b387cc86078befcafd54a5c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 22 Apr 2017 10:03:12 -0700 Subject: [PATCH 074/842] Switched to new semver mustache template. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 160 ++++++---------- .../net/thauvin/erik/mobibot/Mobibot.java | 6 +- version.mustache | 104 +++++++++++ version.vm | 173 ------------------ 4 files changed, 160 insertions(+), 283 deletions(-) create mode 100644 version.mustache delete mode 100644 version.vm diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 913e103..ea0b0ff 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,45 +13,20 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String buildmeta = "018"; - private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1491238266337L), ZoneId.systemDefault()); - private final static int major = 0; - private final static int minor = 7; - private final static int patch = 0; - private final static String prerelease = "beta"; - private final static String project = "mobibot"; + private final static String DEFAULT_PRERELEASE_PREFIX = "-"; + private final static String DEFAULT_BUILDMETA_PREFIX = "+"; + + public final static String project = "mobibot"; + public final static LocalDateTime buildDate = + LocalDateTime.ofInstant(Instant.ofEpochMilli(1492880264977L), ZoneId.systemDefault()); + public final static int major = 0; + public final static int minor = 7; + public final static int patch = 1; + public final static String preRelease = "beta"; + public final static String buildMeta = "018"; /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private ReleaseInfo() - throws UnsupportedOperationException { - throw new UnsupportedOperationException("Illegal constructor call."); - } - - /** - * Returns the build date. - * - * @return The build date. - */ - public static LocalDateTime getBuildDate() { - return date; - } - - /** - * Returns the project name. - * - * @return The project name, if any. - */ - public static String getProject() { - return project; - } - - /** - * Returns the full version string. + * The full version string. * <p> * Formatted as: * <blockquote> @@ -65,94 +40,65 @@ public final class ReleaseInfo { * <li><code>1.0.0+20160124144700</code></li> * <li><code>1.0.0-alpha+001</code></li> * </ul> - * - * @return The version string. */ - public static String getVersion() { - return Integer.toString(getMajor()) + '.' - + Integer.toString(getMinor()) + '.' - + Integer.toString(getPatch()) - + getPreRelease(true) + getBuildMetadata(true); + public final static String version = Integer.toString(major) + '.' + + Integer.toString(minor) + '.' + + Integer.toString(patch) + + preReleaseWithPrefix() + buildMetaWithPrefix(); + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private ReleaseInfo() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); } /** - * Returns the major version. + * Returns the build metadata with default prefix. * - * @return The major version. + * @return The build metadata, if any. */ - public static int getMajor() { - return major; - } - - /** - * Returns the minor version. - * - * @return The minor version. - */ - public static int getMinor() { - return minor; - } - - /** - * Returns the patch version. - * - * @return The patch version. - */ - public static int getPatch() { - return patch; - } - - /** - * Returns the pre-release version. - * - * @param isHyphen Prepend a hyphen, if <code>true</code>. - * @return The pre-release version, if any. - */ - public static String getPreRelease(final boolean isHyphen) { - if (prerelease.length() > 0) { - if (isHyphen) { - return '-' + prerelease; - } else { - return prerelease; - } - } - - return ""; - } - - /** - * Returns the pre-release version. - * - * @return The pre-release version, if any. - */ - public static String getPreRelease() { - return getPreRelease(false); + public static String buildMetaWithPrefix() { + return buildMetaWithPrefix(DEFAULT_PRERELEASE_PREFIX); } /** * Returns the build metadata. * - * @param isPlus Prepend a plus sign, if <code>true</code>. + * @param prefix Prefix to prepend. * @return The build metadata, if any. */ - public static String getBuildMetadata(final boolean isPlus) { - if (buildmeta.length() > 0) { - if (isPlus) { - return '+' + buildmeta; - } else { - return buildmeta; - } + public static String buildMetaWithPrefix(final String prefix) { + if (buildMeta.length() > 0 && prefix.length() > 0) { + return prefix + buildMeta; + } else { + return buildMeta; } - - return ""; } /** - * Returns the build metadata. + * Returns the pre-release version with default prefix. * - * @return The build metadata, if any. + * @return The pre-release version, if any. */ - public static String getBuildMetadata() { - return getBuildMetadata(false); + public static String preReleaseWithPrefix() { + return preReleaseWithPrefix(DEFAULT_PRERELEASE_PREFIX); + } + + /** + * Returns the pre-release version. + * + * @param prefix The prefix to prepend. + * @return The pre-release version, if any. + */ + public static String preReleaseWithPrefix(final String prefix) { + if (preRelease.length() > 0 && prefix.length() > 0) { + return prefix + preRelease; + } else { + return preRelease; + } } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c617572..6b80946 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -69,7 +69,7 @@ public class Mobibot extends PircBot { // The info strings. private static final String[] INFO_STRS = { - ReleaseInfo.getProject() + " v" + ReleaseInfo.getVersion() + " by Erik C. Thauvin (erik@thauvin.net)", + ReleaseInfo.project + " v" + ReleaseInfo.version + " by Erik C. Thauvin (erik@thauvin.net)", "http://www.mobitopia.org/mobibot/" }; @@ -100,9 +100,9 @@ public class Mobibot extends PircBot { // The version strings. private static final String[] VERSION_STRS = { "Version: " - + ReleaseInfo.getVersion() + + ReleaseInfo.version + " (" - + Utils.isoLocalDate(ReleaseInfo.getBuildDate()) + ')', + + Utils.isoLocalDate(ReleaseInfo.buildDate) + ')', "Platform: " + System.getProperty("os.name") + " (" diff --git a/version.mustache b/version.mustache new file mode 100644 index 0000000..6be70c2 --- /dev/null +++ b/version.mustache @@ -0,0 +1,104 @@ +/* + * This file is automatically generated. + * Do not modify! -- ALL CHANGES WILL BE ERASED! + */ +package {{packageName}}; + +import java.time.*; + +/** + * Provides semantic version information. + * + * @author <a href="https://github.com/ethauvin/semver">Semantic Version + * Annotation Processor</a> + */ +public final class {{className}} { + private final static String DEFAULT_PRERELEASE_PREFIX = "-"; + private final static String DEFAULT_BUILDMETA_PREFIX = "+"; + + public final static String project = "{{project}}"; + public final static LocalDateTime buildDate = + LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); + public final static int major = {{major}}; + public final static int minor = {{minor}}; + public final static int patch = {{patch}}; + public final static String preRelease = "{{preRelease}}"; + public final static String buildMeta = "{{buildMeta}}"; + + /** + * The full version string. + * <p> + * Formatted as: + * <blockquote> + * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> + * </blockquote> + * <p> + * For example: + * <ul> + * <li><code>1.0.0</code></li> + * <li><code>1.0.0-beta</code></li> + * <li><code>1.0.0+20160124144700</code></li> + * <li><code>1.0.0-alpha+001</code></li> + * </ul> + */ + public final static String version = Integer.toString(major) + '.' + + Integer.toString(minor) + '.' + + Integer.toString(patch) + + preReleaseWithPrefix() + buildMetaWithPrefix(); + + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private {{className}}() + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Returns the build metadata with default prefix. + * + * @return The build metadata, if any. + */ + public static String buildMetaWithPrefix() { + return buildMetaWithPrefix(DEFAULT_PRERELEASE_PREFIX); + } + + /** + * Returns the build metadata. + * + * @param prefix Prefix to prepend. + * @return The build metadata, if any. + */ + public static String buildMetaWithPrefix(final String prefix) { + if (buildMeta.length() > 0 && prefix.length() > 0) { + return prefix + buildMeta; + } else { + return buildMeta; + } + } + + /** + * Returns the pre-release version with default prefix. + * + * @return The pre-release version, if any. + */ + public static String preReleaseWithPrefix() { + return preReleaseWithPrefix(DEFAULT_PRERELEASE_PREFIX); + } + + /** + * Returns the pre-release version. + * + * @param prefix The prefix to prepend. + * @return The pre-release version, if any. + */ + public static String preReleaseWithPrefix(final String prefix) { + if (preRelease.length() > 0 && prefix.length() > 0) { + return prefix + preRelease; + } else { + return preRelease; + } + } +} \ No newline at end of file diff --git a/version.vm b/version.vm deleted file mode 100644 index 3921024..0000000 --- a/version.vm +++ /dev/null @@ -1,173 +0,0 @@ -############################################################################## -## -## Available variables: -## -## $packageName - The package name. (string) -## $className - The class name. (string) -## $project - The project name. (string) -## $epoch - The build epoch/unix time. (long) -## $major - The major version. (int) -## $minor - The minor version. (int) -## $patch - The patch version. (int) -## $prerelease - The pre-release version. (string) -## $buildmeta - The build metadata version. (string) -## -############################################################################## -/* - * This file is automatically generated. - * Do not modify! -- ALL CHANGES WILL BE ERASED! - */ -package ${packageName}; - -import java.time.*; - -/** - * Provides semantic version information. - * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version - * Annotation Processor</a> - */ -public final class ${className} { - private final static String buildmeta = "${buildmeta}"; - private final static LocalDateTime date = - LocalDateTime.ofInstant(Instant.ofEpochMilli(${epoch}L), ZoneId.systemDefault()); - private final static int major = ${major}; - private final static int minor = ${minor}; - private final static int patch = ${patch}; - private final static String prerelease = "${prerelease}"; - private final static String project = "${project}"; - - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private ${className}() - throws UnsupportedOperationException { - throw new UnsupportedOperationException("Illegal constructor call."); - } - - /** - * Returns the build date. - * - * @return The build date. - */ - public static LocalDateTime getBuildDate() { - return date; - } - - /** - * Returns the project name. - * - * @return The project name, if any. - */ - public static String getProject() { - return project; - } - - /** - * Returns the full version string. - * <p> - * Formatted as: - * <blockquote> - * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> - * </blockquote> - * <p> - * For example: - * <ul> - * <li><code>1.0.0</code></li> - * <li><code>1.0.0-beta</code></li> - * <li><code>1.0.0+20160124144700</code></li> - * <li><code>1.0.0-alpha+001</code></li> - * </ul> - * - * @return The version string. - */ - public static String getVersion() { - return Integer.toString(getMajor()) + '.' - + Integer.toString(getMinor()) + '.' - + Integer.toString(getPatch()) - + getPreRelease(true) + getBuildMetadata(true); - } - - /** - * Returns the major version. - * - * @return The major version. - */ - public static int getMajor() { - return major; - } - - /** - * Returns the minor version. - * - * @return The minor version. - */ - public static int getMinor() { - return minor; - } - - /** - * Returns the patch version. - * - * @return The patch version. - */ - public static int getPatch() { - return patch; - } - - /** - * Returns the pre-release version. - * - * @param isHyphen Prepend a hyphen, if <code>true</code>. - * @return The pre-release version, if any. - */ - public static String getPreRelease(final boolean isHyphen) { - if (prerelease.length() > 0) { - if (isHyphen) { - return '-' + prerelease; - } else { - return prerelease; - } - } - - return ""; - } - - /** - * Returns the pre-release version. - * - * @return The pre-release version, if any. - */ - public static String getPreRelease() { - return getPreRelease(false); - } - - /** - * Returns the build metadata. - * - * @param isPlus Prepend a plus sign, if <code>true</code>. - * @return The build metadata, if any. - */ - public static String getBuildMetadata(final boolean isPlus) { - if (buildmeta.length() > 0) { - if (isPlus) { - return '+' + buildmeta; - } else { - return buildmeta; - } - } - - return ""; - } - - /** - * Returns the build metadata. - * - * @return The build metadata, if any. - */ - public static String getBuildMetadata() { - return getBuildMetadata(false); - } -} \ No newline at end of file From bc985dbed47a7ca56e1b3c46050047c40ec50435 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 30 Apr 2017 23:12:45 -0700 Subject: [PATCH 075/842] Removed old jweather module. --- .../thauvin/erik/mobibot/modules/Weather.java | 178 ------------------ 1 file changed, 178 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Weather.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java deleted file mode 100644 index 72f82df..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Weather.java - * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules; - -import net.sf.jweather.metar.Metar; -import net.sf.jweather.metar.SkyCondition; -import net.sf.jweather.metar.WeatherCondition; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; - -import java.text.DecimalFormat; -import java.util.Date; - -/** - * The Weather module - * - * @author Erik C. Thauvin - * @created Feb 7, 2004 - * @since 1.0 - */ -final public class Weather extends AbstractModule { - /** - * The weather command. - */ - public static final String WEATHER_CMD = "weather"; - - // The decimal number format. - private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("0.##"); - - // The URL where the stations are listed. - private static final String STATIONS_URL = "http://www.rap.ucar.edu/weather/surface/stations.txt"; - - /** - * Creates a new {@link Weather} instance. - */ - public Weather() { - commands.add(WEATHER_CMD); - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); - } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To display weather information:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <station id>")); - bot.send(sender, "For a listing of the ICAO station IDs, please visit: " + STATIONS_URL); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isPrivateMsgEnabled() { - return true; - } - - /** - * Fetches the weather data from a specific station ID. - */ - private void run(final Mobibot bot, final String sender, final String station, final boolean isPrivate) { - if (station.length() == 4) { - final Metar metar = net.sf.jweather.Weather.getMetar(station); - - if (metar != null) { - Float result; - - bot.send(sender, "Station ID: " + metar.getStationID(), isPrivate); - - bot.send(sender, - "At: " - + Utils.utcDateTime(metar.getDate()) - + " UTC (" - + (((new Date()).getTime() - metar.getDate().getTime()) / 1000L / 60L) - + " minutes ago)", - isPrivate); - - result = metar.getWindSpeedInMPH(); - - if (result != null) { - bot.send(sender, - "Wind Speed: " - + result - + " mph, " - + metar.getWindSpeedInKnots() - + " knots, " - + metar.getWindSpeedInMPS() - + " m/s", - isPrivate); - } - - result = metar.getVisibility(); - - if (result != null) { - bot.send(sender, - "Visibility: " - + (metar.getVisibilityLessThan() ? "< " : "") - + NUMBER_FORMAT.format(result) - + " mi, " + metar.getVisibilityInKilometers() + " km", - isPrivate); - } - - result = metar.getPressure(); - - if (result != null) { - bot.send(sender, - "Pressure: " + result + " Hg, " + metar.getPressureInHectoPascals() + " hPa", - isPrivate); - } - - result = metar.getTemperatureInCelsius(); - - if (result != null) { - bot.send(sender, - "Temperature: " + result + " \u00B0C, " + metar.getTemperatureInFahrenheit() + " \u00B0F", - isPrivate); - } - - if (metar.getWeatherConditions() != null) { - for (final Object weatherCondition : metar.getWeatherConditions()) { - bot.send(sender, ((WeatherCondition) weatherCondition).getNaturalLanguageString(), isPrivate); - } - } - - if (metar.getSkyConditions() != null) { - for (final Object skyCondition : metar.getSkyConditions()) { - bot.send(sender, ((SkyCondition) skyCondition).getNaturalLanguageString(), isPrivate); - } - } - - return; - } else { - bot.send(sender, "Invalid Station ID. Please try again.", isPrivate); - - return; - } - } - - helpResponse(bot, sender, station, isPrivate); - } -} From a45a5145421fe27c84ea30c57ec9dd9565bc5e3a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 30 Apr 2017 23:14:03 -0700 Subject: [PATCH 076/842] Updated to new semver template format. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 48 +++++++++---------- .../net/thauvin/erik/mobibot/Mobibot.java | 6 +-- version.mustache | 46 +++++++++--------- version.properties | 6 +-- 4 files changed, 53 insertions(+), 53 deletions(-) rename src/generated/java/{ => sources}/net/thauvin/erik/mobibot/ReleaseInfo.java (60%) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/sources/net/thauvin/erik/mobibot/ReleaseInfo.java similarity index 60% rename from src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java rename to src/generated/java/sources/net/thauvin/erik/mobibot/ReleaseInfo.java index ea0b0ff..3a5ec39 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/sources/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,19 +13,19 @@ import java.time.*; * Annotation Processor</a> */ public final class ReleaseInfo { - private final static String DEFAULT_PRERELEASE_PREFIX = "-"; - private final static String DEFAULT_BUILDMETA_PREFIX = "+"; + public final static String PRERELEASE_PREFIX = "-"; + public final static String BUILDMETA_PREFIX = "+"; - public final static String project = "mobibot"; - public final static LocalDateTime buildDate = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1492880264977L), ZoneId.systemDefault()); - public final static int major = 0; - public final static int minor = 7; - public final static int patch = 1; - public final static String preRelease = "beta"; - public final static String buildMeta = "018"; + public final static String PROJECT = "mobibot"; + public final static LocalDateTime BUILDDATE = + LocalDateTime.ofInstant(Instant.ofEpochMilli(1493616818868L), ZoneId.systemDefault()); + public final static int MAJOR = 0; + public final static int MINOR = 7; + public final static int PATCH = 1; + public final static String PRERELEASE = "beta"; + public final static String BUILDMETA = "018"; - /** + /** * The full version string. * <p> * Formatted as: @@ -41,9 +41,9 @@ public final class ReleaseInfo { * <li><code>1.0.0-alpha+001</code></li> * </ul> */ - public final static String version = Integer.toString(major) + '.' - + Integer.toString(minor) + '.' - + Integer.toString(patch) + public final static String VERSION = Integer.toString(MAJOR) + '.' + + Integer.toString(MINOR) + '.' + + Integer.toString(PATCH) + preReleaseWithPrefix() + buildMetaWithPrefix(); /** @@ -57,12 +57,12 @@ public final class ReleaseInfo { } /** - * Returns the build metadata with default prefix. + * Returns the build metadata with {@value #BUILDMETA_PREFIX} prefix. * * @return The build metadata, if any. */ public static String buildMetaWithPrefix() { - return buildMetaWithPrefix(DEFAULT_PRERELEASE_PREFIX); + return buildMetaWithPrefix(BUILDMETA_PREFIX); } /** @@ -72,20 +72,20 @@ public final class ReleaseInfo { * @return The build metadata, if any. */ public static String buildMetaWithPrefix(final String prefix) { - if (buildMeta.length() > 0 && prefix.length() > 0) { - return prefix + buildMeta; + if (BUILDMETA.length() > 0 && prefix.length() > 0) { + return prefix + BUILDMETA; } else { - return buildMeta; + return BUILDMETA; } } /** - * Returns the pre-release version with default prefix. + * Returns the pre-release version with {@value #PRERELEASE_PREFIX} prefix. * * @return The pre-release version, if any. */ public static String preReleaseWithPrefix() { - return preReleaseWithPrefix(DEFAULT_PRERELEASE_PREFIX); + return preReleaseWithPrefix(PRERELEASE_PREFIX); } /** @@ -95,10 +95,10 @@ public final class ReleaseInfo { * @return The pre-release version, if any. */ public static String preReleaseWithPrefix(final String prefix) { - if (preRelease.length() > 0 && prefix.length() > 0) { - return prefix + preRelease; + if (PRERELEASE.length() > 0 && prefix.length() > 0) { + return prefix + PRERELEASE; } else { - return preRelease; + return PRERELEASE; } } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 6b80946..801720f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -69,7 +69,7 @@ public class Mobibot extends PircBot { // The info strings. private static final String[] INFO_STRS = { - ReleaseInfo.project + " v" + ReleaseInfo.version + " by Erik C. Thauvin (erik@thauvin.net)", + ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", "http://www.mobitopia.org/mobibot/" }; @@ -100,9 +100,9 @@ public class Mobibot extends PircBot { // The version strings. private static final String[] VERSION_STRS = { "Version: " - + ReleaseInfo.version + + ReleaseInfo.VERSION + " (" - + Utils.isoLocalDate(ReleaseInfo.buildDate) + ')', + + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', "Platform: " + System.getProperty("os.name") + " (" diff --git a/version.mustache b/version.mustache index 6be70c2..8cb1997 100644 --- a/version.mustache +++ b/version.mustache @@ -13,19 +13,19 @@ import java.time.*; * Annotation Processor</a> */ public final class {{className}} { - private final static String DEFAULT_PRERELEASE_PREFIX = "-"; - private final static String DEFAULT_BUILDMETA_PREFIX = "+"; + public final static String PRERELEASE_PREFIX = "-"; + public final static String BUILDMETA_PREFIX = "+"; - public final static String project = "{{project}}"; - public final static LocalDateTime buildDate = + public final static String PROJECT = "{{project}}"; + public final static LocalDateTime BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); - public final static int major = {{major}}; - public final static int minor = {{minor}}; - public final static int patch = {{patch}}; - public final static String preRelease = "{{preRelease}}"; - public final static String buildMeta = "{{buildMeta}}"; + public final static int MAJOR = {{major}}; + public final static int MINOR = {{minor}}; + public final static int PATCH = {{patch}}; + public final static String PRERELEASE = "{{preRelease}}"; + public final static String BUILDMETA = "{{buildMeta}}"; - /** + /** * The full version string. * <p> * Formatted as: @@ -41,9 +41,9 @@ public final class {{className}} { * <li><code>1.0.0-alpha+001</code></li> * </ul> */ - public final static String version = Integer.toString(major) + '.' - + Integer.toString(minor) + '.' - + Integer.toString(patch) + public final static String VERSION = Integer.toString(MAJOR) + '.' + + Integer.toString(MINOR) + '.' + + Integer.toString(PATCH) + preReleaseWithPrefix() + buildMetaWithPrefix(); /** @@ -57,12 +57,12 @@ public final class {{className}} { } /** - * Returns the build metadata with default prefix. + * Returns the build metadata with {@value #BUILDMETA_PREFIX} prefix. * * @return The build metadata, if any. */ public static String buildMetaWithPrefix() { - return buildMetaWithPrefix(DEFAULT_PRERELEASE_PREFIX); + return buildMetaWithPrefix(BUILDMETA_PREFIX); } /** @@ -72,20 +72,20 @@ public final class {{className}} { * @return The build metadata, if any. */ public static String buildMetaWithPrefix(final String prefix) { - if (buildMeta.length() > 0 && prefix.length() > 0) { - return prefix + buildMeta; + if (BUILDMETA.length() > 0 && prefix.length() > 0) { + return prefix + BUILDMETA; } else { - return buildMeta; + return BUILDMETA; } } /** - * Returns the pre-release version with default prefix. + * Returns the pre-release version with {@value #PRERELEASE_PREFIX} prefix. * * @return The pre-release version, if any. */ public static String preReleaseWithPrefix() { - return preReleaseWithPrefix(DEFAULT_PRERELEASE_PREFIX); + return preReleaseWithPrefix(PRERELEASE_PREFIX); } /** @@ -95,10 +95,10 @@ public final class {{className}} { * @return The pre-release version, if any. */ public static String preReleaseWithPrefix(final String prefix) { - if (preRelease.length() > 0 && prefix.length() > 0) { - return prefix + preRelease; + if (PRERELEASE.length() > 0 && prefix.length() > 0) { + return prefix + PRERELEASE; } else { - return preRelease; + return PRERELEASE; } } } \ No newline at end of file diff --git a/version.properties b/version.properties index 75c612b..b414f50 100644 --- a/version.properties +++ b/version.properties @@ -1,8 +1,8 @@ -#Mon, 25 Jan 2016 16:36:57 -0800 +#Mon, 17 Apr 2017 22:49:41 -0700 #Mon Dec 07 01:31:00 PST 2015 version.project=mobibot version.major=0 version.minor=7 -version.patch=0 +version.patch=1 version.prerelease=beta -version.buildmeta=018 +version.buildmeta=018 \ No newline at end of file From 7076c928a10f65d31a14e8e1e33f01d03fded8bc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 30 Apr 2017 23:16:35 -0700 Subject: [PATCH 077/842] Removed jweather dependency. --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 69a9de8..d8a6857 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ defaultTasks 'deploy' def packageName = 'net.thauvin.erik.mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames +def semverJar = 'net.thauvin.erik:semver:0.9.7' def getVersion(isIncrement = false) { def propsFile = 'version.properties' @@ -66,7 +67,6 @@ dependencies { compile 'org.json:json:20160810' compile 'org.ostermiller:utils:1.07.00' - compile 'net.sourceforge.jweather:jweather:0.3.0@jar' compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.6' @@ -74,12 +74,12 @@ dependencies { compile files('lib/owm-japis-2.5.0.5.jar') - compileOnly 'net.thauvin.erik:semver:0.9.6-beta' + compileOnly semverJar } annotationProcessor { project.version = getVersion(isRelease) - library 'net.thauvin.erik:semver:0.9.6-beta' + library semverJar processor 'net.thauvin.erik.semver.VersionProcessor' } @@ -121,7 +121,7 @@ task copyToDeploy(type: Copy) { } task copyToDeployLib(type: Copy) { - from configurations.runtime + from configurations.compile into deployDir + '/lib' } From 940f10bf60acb1e86b4937ddd96e003dadc6a098 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 30 Apr 2017 23:17:04 -0700 Subject: [PATCH 078/842] Added deploy task. --- kobalt/src/Build.kt | 88 ++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 129bbde..31a977b 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -1,17 +1,23 @@ import com.beust.kobalt.* -import com.beust.kobalt.plugin.apt.* -import com.beust.kobalt.plugin.packaging.* -import com.beust.kobalt.plugin.application.* -import com.beust.kobalt.plugin.java.* +import com.beust.kobalt.api.Project +import com.beust.kobalt.api.annotation.Task +import com.beust.kobalt.misc.KobaltLogger +import com.beust.kobalt.misc.log +import com.beust.kobalt.plugin.application.application +import com.beust.kobalt.plugin.apt.apt +import com.beust.kobalt.plugin.packaging.assemble +import com.beust.kobalt.plugin.packaging.install +import com.beust.kobalt.plugin.publish.autoGitTag import java.io.File import java.io.FileInputStream import java.util.* val bs = buildScript { - repos() + repos(file("K:/maven/repository")) } val mainClassName = "net.thauvin.erik.mobibot.Mobibot" +val deploy = "deploy" fun StringBuilder.prepend(s: String): StringBuilder { if (this.isNotEmpty()) { @@ -41,49 +47,59 @@ val p = project { version = versionFor() - val processorJar = "net.thauvin.erik:semver:" + val processorJar = "net.thauvin.erik:semver:0.9.7" + val lib = "lib" dependencies { - compile("log4j:log4j:jar:1.2.17") + compile("log4j:log4j:jar:1.2.17") - compile("pircbot:pircbot:1.5.0") + compile("pircbot:pircbot:1.5.0") - compile("commons-codec:commons-codec:1.10") - compile("commons-logging:commons-logging:1.2") - compile("commons-net:commons-net:3.6") - compile("commons-cli:commons-cli:1.4") - compile("commons-httpclient:commons-httpclient:3.1") + compile("commons-codec:commons-codec:1.10") + compile("commons-logging:commons-logging:1.2") + compile("commons-net:commons-net:3.6") + compile("commons-cli:commons-cli:1.4") + compile("commons-httpclient:commons-httpclient:3.1") - compile("oro:oro:2.0.8") + compile("oro:oro:2.0.8") - compile("org.jsoup:jsoup:1.10.2") - compile("com.rometools:rome:1.7.1") - compile("org.slf4j:slf4j-log4j12:1.7.25") - compile("org.json:json:20160810") - compile("org.ostermiller:utils:1.07.00") + compile("org.jsoup:jsoup:1.10.2") + compile("com.rometools:rome:1.7.1") + compile("org.slf4j:slf4j-log4j12:1.7.25") + compile("org.json:json:20160810") + compile("org.ostermiller:utils:1.07.00") - compile("net.sourceforge.jweather:jweather:jar:0.3.0") - compile("net.objecthunter:exp4j:0.4.8") + compile("net.objecthunter:exp4j:0.4.8") - compile("org.twitter4j:twitter4j-core:4.0.6") - compile("net.sf.delicious-java:delicious:1.14") + compile("org.twitter4j:twitter4j-core:4.0.6") + compile("net.sf.delicious-java:delicious:1.14") - compile(file("lib/owm-japis-2.5.0.5.jar")) + compile(file("lib/owm-japis-2.5.0.5.jar")) - apt(processorJar) - compile(processorJar) + apt(processorJar) + compileOnly(processorJar) } apt { - outputDir = "/src/generated/java/" + outputDir = "../src/generated/java/" + } + + autoGitTag { + enabled = true + message = "Version $version" + annotated = true } assemble { jar { name = "${project.name}.jar" - fatJar = true manifest { attributes("Main-Class", mainClassName) + attributes("Class-Path", + collect(compileDependencies) + .filter { !it.file.name.startsWith("junit") } + .map { it.file.name } + .joinToString(" ./$lib/", prefix = ". ./$lib/")) } } } @@ -94,6 +110,20 @@ val p = project { } install { - libDir = "deploy" + target = deploy + include(from("kobaltBuild/libs"), to(target), glob("**/*")) + include(from("properties"), to(target), glob("**/*.properties")) + collect(compileDependencies) + .filter { !it.file.name.startsWith("junit") } + .forEach { + copy(from(it.file.absolutePath), to("$target/$lib")) + } } } + +@Task(name = "deploy", dependsOn = arrayOf("assemble", "install"), description = "Deploy application") +fun deploy(project: Project): TaskResult { + File("$deploy/logs").mkdir() + KobaltLogger.log(1, " Deployed to " + File(deploy).absolutePath) + return TaskResult() +} From 20af9efc177a7f485e713e54874e46c2d819fd21 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 May 2017 12:17:45 -0700 Subject: [PATCH 079/842] Added sublime project. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 6549aef..49d96ef 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ **/.idea/tasks.xml **/.idea/workspace.xml *.iws +*.sublime-* .classpath .DS_Store .gradle @@ -31,4 +32,5 @@ CVS ehthumbs.db kobaltBuild +kobaltw-test Thumbs.db \ No newline at end of file From a9364be3e3a555ca4a16e7f4d5b8b91defd2bb27 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 May 2017 12:18:31 -0700 Subject: [PATCH 080/842] Updated copyright. --- src/main/java/net/thauvin/erik/mobibot/Commands.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/EntryComment.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/EntryLink.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/FeedReader.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Tell.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/TellMessage.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Utils.java | 4 ++-- .../net/thauvin/erik/mobibot/modules/AbstractModule.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Calc.java | 4 ++-- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Dice.java | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/GoogleSearch.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Joke.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Ping.java | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/StockQuote.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/War.java | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/Weather2.java | 6 +++--- .../java/net/thauvin/erik/mobibot/modules/WorldTime.java | 4 ++-- 25 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 5a41e43..fd108cd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -1,7 +1,7 @@ /* * Commands.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot; /** * The <code>commands</code>, <code>keywords</code> and <code>arguments</code>. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 401b049..7733f90 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -1,7 +1,7 @@ /* * DeliciousPoster.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ import javax.swing.*; /** * The class to handle posts to del.icio.us. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Mar 5, 2005 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index ad068ca..ca7d8a8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -1,7 +1,7 @@ /* * EntriesMgr.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,7 +44,7 @@ import java.util.List; /** * Manages the feed entries. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index 4adf0d0..5c1ea0a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -1,7 +1,7 @@ /* * EntryComment.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,7 +37,7 @@ import java.time.LocalDateTime; /** * The class used to store comments associated to a specific entry. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 8dcd822..555c2ae 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -1,7 +1,7 @@ /* * EntryLink.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * The class used to store link entries. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 92808e4..cb82e6a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -1,7 +1,7 @@ /* * FeedReader.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,7 +44,7 @@ import java.util.List; /** * Reads a RSS feed. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Feb 1, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 801720f..bb9f361 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -50,7 +50,7 @@ import java.util.*; /** * Implements the #mobitopia bot. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index b34a47a..89b149c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -1,7 +1,7 @@ /* * Tell.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,7 +37,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * The <code>Tell</code> command. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2016-07-02 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index db54230..56d778e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -1,7 +1,7 @@ /* * TellMessage.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,7 +39,7 @@ import java.time.format.DateTimeFormatter; /** * The <code>TellMessage</code> class. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-24 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index d170e37..f2b3520 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -1,7 +1,7 @@ /* * TellMessagesMgr.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,7 +42,7 @@ import java.util.List; /** * The Tell Messages Manager. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index dcaa667..954e45c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -21,7 +21,7 @@ import java.io.InputStreamReader; * </p> * and follow the prompts/instructions. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @author <a href="http://twitter4j.org/en/code-examples.html#oauth" target="_blank">Twitter4J</a> * @created Sep 13, 2010 * @since 1.0 diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 903dec7..a20f18c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -1,7 +1,7 @@ /* * Utils.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,7 +42,7 @@ import java.util.Date; /** * Miscellaneous utilities class. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 46b73d2..a907b91 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -1,7 +1,7 @@ /* * AbstractModule.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,7 +39,7 @@ import java.util.*; /** * The <code>Module</code> abstract class. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2016-07-01 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index f9a0b30..c2ab162 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -1,7 +1,7 @@ /* * Calc.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,7 +41,7 @@ import java.text.DecimalFormat; /** * The Calc module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2016-07-01 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 2a1f3de..76c065d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -1,7 +1,7 @@ /* * CurrencyConverter.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,7 +50,7 @@ import java.util.TreeMap; /** * The CurrentConverter module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Feb 11, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 8c300da..1a0316f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -1,7 +1,7 @@ /* * Dice.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,7 +39,7 @@ import java.util.Random; /** * The Dice module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 4772b3d..73d76eb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -1,7 +1,7 @@ /* * GoogleSearch.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,7 +45,7 @@ import java.net.URLEncoder; /** * The GoogleSearch module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Feb 7, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 301f06f..7d6a26e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -1,7 +1,7 @@ /* * Joke.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ import java.net.URLConnection; /** * The Joke module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-20 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 64460d8..7fbfa08 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -1,7 +1,7 @@ /* * Lookup.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,7 +41,7 @@ import java.net.UnknownHostException; /** * The Lookup module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index dd7d76d..dacdd23 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -1,7 +1,7 @@ /* * Ping.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,7 +40,7 @@ import java.util.Random; /** * The Ping module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2016-07-02 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index ad716b3..7545852 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -1,7 +1,7 @@ /* * StockQuote.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,7 +41,7 @@ import java.io.IOException; /** * The StockQuote module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Feb 7, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 41dbdcb..714a388 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -1,7 +1,7 @@ /* * Twitter.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,7 +39,7 @@ import twitter4j.conf.ConfigurationBuilder; /** * The Twitter module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created Sept 10, 2008 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index b0d7776..dd183a4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,7 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,7 +39,7 @@ import java.util.Random; /** * The War module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 123fe87..db91e46 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -39,9 +39,9 @@ import net.thauvin.erik.mobibot.Utils; import java.io.IOException; /** - * The <code>Weather2</code> class. + * The <code>Weather2</code> module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2017-04-02 * @since 1.0 */ @@ -56,7 +56,7 @@ public class Weather2 extends AbstractModule { private static final String OWM_API_KEY_PROP = "owm-api-key"; /** - * Creates a new {@link Weather} instance. + * Creates a new {@link Weather2} instance. */ public Weather2() { commands.add(WEATHER_CMD); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 142537d..9a72d7f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -1,7 +1,7 @@ /* * WorldTime.java * - * Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ import java.util.TreeMap; /** * The WorldTime module. * - * @author <a href="mailto:erik@thauvin.net" target="_blank">Erik C. Thauvin</a> + * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2014-04-27 * @since 1.0 */ From 090746abb496cf8eb28f43aca064af11bafcb1fb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 May 2017 12:19:04 -0700 Subject: [PATCH 081/842] SemVer 1.0.0 update. --- build.gradle | 2 +- kobalt/src/Build.kt | 20 ++++++++++++------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) rename src/generated/java/{sources => }/net/thauvin/erik/mobibot/ReleaseInfo.java (96%) diff --git a/build.gradle b/build.gradle index d8a6857..6572b46 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ defaultTasks 'deploy' def packageName = 'net.thauvin.erik.mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -def semverJar = 'net.thauvin.erik:semver:0.9.7' +def semverJar = 'net.thauvin.erik:semver:1.0.0' def getVersion(isIncrement = false) { def propsFile = 'version.properties' diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 31a977b..d1366b1 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -5,6 +5,7 @@ import com.beust.kobalt.misc.KobaltLogger import com.beust.kobalt.misc.log import com.beust.kobalt.plugin.application.application import com.beust.kobalt.plugin.apt.apt +import com.beust.kobalt.plugin.java.javadoc import com.beust.kobalt.plugin.packaging.assemble import com.beust.kobalt.plugin.packaging.install import com.beust.kobalt.plugin.publish.autoGitTag @@ -13,7 +14,6 @@ import java.io.FileInputStream import java.util.* val bs = buildScript { - repos(file("K:/maven/repository")) } val mainClassName = "net.thauvin.erik.mobibot.Mobibot" @@ -47,7 +47,7 @@ val p = project { version = versionFor() - val processorJar = "net.thauvin.erik:semver:0.9.7" + val processorJar = "net.thauvin.erik:semver:1.0.0" val lib = "lib" dependencies { @@ -110,14 +110,20 @@ val p = project { } install { - target = deploy include(from("kobaltBuild/libs"), to(target), glob("**/*")) include(from("properties"), to(target), glob("**/*.properties")) collect(compileDependencies) - .filter { !it.file.name.startsWith("junit") } - .forEach { - copy(from(it.file.absolutePath), to("$target/$lib")) - } + .filter { !it.file.name.startsWith("junit") } + .forEach { + copy(from(it.file.absolutePath), to("$target/$lib")) + } + } + + javadoc { + title = project.name + ' ' + project.version + tags("created") + author = true + links("http://www.jibble.org/javadocs/pircbot/", "http://docs.oracle.com/javase/8/docs/api/") } } diff --git a/src/generated/java/sources/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java similarity index 96% rename from src/generated/java/sources/net/thauvin/erik/mobibot/ReleaseInfo.java rename to src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 3a5ec39..df4e122 100644 --- a/src/generated/java/sources/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,7 +18,7 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1493616818868L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1493879846880L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 1; From b58121d9e6b3df2f1435655b0bf2fcad45b36cc4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 May 2017 12:20:03 -0700 Subject: [PATCH 082/842] Gradle 3.5 update. --- gradle/wrapper/gradle-wrapper.jar | Bin 54212 -> 54783 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- kobalt/wrapper/kobalt-wrapper.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 60bd3bc497ba831b732a27ae2cf9ccec20be152a..9785bdb0326373c0795fde9eb5e8e80ecdb79d99 100644 GIT binary patch delta 6729 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 delta 6082 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c07e7f6..cbb2c54 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Apr 02 19:18:15 PDT 2017 +#Mon Apr 17 22:31:51 PDT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index d12a84e..cdcb2d1 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.46 \ No newline at end of file +kobalt.version=1.0.86 \ No newline at end of file From 80d7f7c9dbd887b96c5d1958e4206fab852bfc67 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 May 2017 12:20:21 -0700 Subject: [PATCH 083/842] IDEA files update. --- .idea/modules/mobibot.iml | 242 ++++++++++++++++++++++++++------------ kobalt/Build.kt.iml | 24 ++++ mobibot.ipr | 71 +++++------ 3 files changed, 219 insertions(+), 118 deletions(-) create mode 100644 kobalt/Build.kt.iml diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 2e3e406..5483171 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,87 +1,58 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.0-beta+018" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="KOBALT" external.system.module.group="" external.system.module.version="0.7.1-beta+018" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> - <output url="file://$MODULE_DIR$/../../build/classes/main" /> - <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> + <output url="file://$MODULE_DIR$/../../kobaltBuild/classes" /> + <output-test url="file://$MODULE_DIR$/../../kobaltBuild/test-classes" /> <exclude-output /> <content url="file://$MODULE_DIR$/../.."> - <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/annotationProcessor/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> <excludeFolder url="file://$MODULE_DIR$/../../build" /> + <excludeFolder url="file://$MODULE_DIR$/../../kobaltBuild" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:0.9.6-beta" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160810" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160810" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160810" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sourceforge.jweather:jweather:0.3.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.velocity:velocity:1.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-collections:commons-collections:3.2.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-lang:commons-lang:2.4" level="project" /> - <orderEntry type="module-library" scope="PROVIDED"> - <library name="Gradle: owm-japis-2.5.0.5"> + <orderEntry type="module-library"> + <library name="Kobalt: log4j:log4j:jar:1.2.17"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/log4j/log4j/1.2.17/log4j-1.2.17.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: org.slf4j:slf4j-api:jar:1.7.25"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: org.jdom:jdom2:jar:2.0.6"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: com.rometools:rome-utils:jar:1.7.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/com/rometools/rome-utils/1.7.1/rome-utils-1.7.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: file://.\lib\owm-japis-2.5.0.5.jar"> <CLASSES> <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> </CLASSES> @@ -89,19 +60,136 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="module-library" scope="RUNTIME"> - <library name="Gradle: owm-japis-2.5.0.5"> + <orderEntry type="module-library"> + <library name="Kobalt: net.sf.delicious-java:delicious:jar:1.14"> <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.kobalt/cache/net/sf/delicious-java/delicious/1.14/delicious-1.14.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> </library> </orderEntry> - <orderEntry type="module-library" scope="TEST"> - <library name="Gradle: owm-japis-2.5.0.5"> + <orderEntry type="module-library"> + <library name="Kobalt: org.twitter4j:twitter4j-core:jar:4.0.6"> <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.kobalt/cache/org/twitter4j/twitter4j-core/4.0.6/twitter4j-core-4.0.6.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: net.objecthunter:exp4j:jar:0.4.8"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/net/objecthunter/exp4j/0.4.8/exp4j-0.4.8.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: org.ostermiller:utils:jar:1.07.00"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/org/ostermiller/utils/1.07.00/utils-1.07.00.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: org.json:json:jar:20160810"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/org/json/json/20160810/json-20160810.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: org.slf4j:slf4j-log4j12:jar:1.7.25"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: com.rometools:rome:jar:1.7.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/com/rometools/rome/1.7.1/rome-1.7.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: org.jsoup:jsoup:jar:1.10.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/org/jsoup/jsoup/1.10.2/jsoup-1.10.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: oro:oro:jar:2.0.8"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/oro/oro/2.0.8/oro-2.0.8.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: commons-httpclient:commons-httpclient:jar:3.1"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/commons-httpclient/commons-httpclient/3.1/commons-httpclient-3.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: commons-cli:commons-cli:jar:1.4"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/commons-cli/commons-cli/1.4/commons-cli-1.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: commons-net:commons-net:jar:3.6"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/commons-net/commons-net/3.6/commons-net-3.6.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: commons-logging:commons-logging:jar:1.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/commons-logging/commons-logging/1.2/commons-logging-1.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: commons-codec:commons-codec:jar:1.10"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/commons-codec/commons-codec/1.10/commons-codec-1.10.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: pircbot:pircbot:jar:1.5.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/cache/pircbot/pircbot/1.5.0/pircbot-1.5.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> diff --git a/kobalt/Build.kt.iml b/kobalt/Build.kt.iml new file mode 100644 index 0000000..7cf6bc1 --- /dev/null +++ b/kobalt/Build.kt.iml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="Build.kt" external.linked.project.path="$MODULE_DIR$/.." external.root.project.path="$MODULE_DIR$/.." external.system.id="KOBALT" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> + <output url="file://$MODULE_DIR$/out/classes" /> + <output-test url="file://$MODULE_DIR$/out/test-classes" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library"> + <library name="Kobalt: com.beust.kobalt:kobalt:jar:1.0.83"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.83/kobalt/wrapper/kobalt-1.0.83.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.83/kobalt/wrapper/kobalt-1.0.83-sources.jar!/" /> + </SOURCES> + </library> + </orderEntry> + </component> +</module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index ae1f521..9c1aed3 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -157,9 +157,24 @@ <option name="USE_PROJECT_PROFILE" value="false" /> <version value="1.0" /> </component> + <component name="KobaltSettings"> + <option name="linkedExternalProjectsSettings"> + <KobaltProjectSettings> + <option name="autoDownloadKobalt" value="true" /> + <option name="downloadSources" value="false" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.83" /> + <option name="modules"> + <set> + <option value="$PROJECT_DIR$" /> + </set> + </option> + <option name="profiles" value="" /> + </KobaltProjectSettings> + </option> + </component> <component name="KotlinCommonCompilerArguments"> - <option name="languageVersion" value="1.1" /> - <option name="apiVersion" value="1.1" /> + <option name="coroutinesWarn" value="false" /> </component> <component name="MavenImportPreferences"> <option name="generalSettings"> @@ -206,6 +221,7 @@ </component> <component name="ProjectModuleManager"> <modules> + <module fileurl="file://$PROJECT_DIR$/kobalt/Build.kt.iml" filepath="$PROJECT_DIR$/kobalt/Build.kt.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" /> </modules> </component> @@ -314,6 +330,15 @@ <mapping directory="$PROJECT_DIR$" vcs="Git" /> </component> <component name="libraryTable"> + <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/1d1fab03f31a75018fc30ec248859a5c89cd342f/compiler-0.9.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/599674a480f9940ff5e25b375c6f59d14d8a4bfa/compiler-0.9.4-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: com.rometools:rome-utils:1.7.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.1/36592957a19bbfdb6f1182ea3e6036bccaec0bdc/rome-utils-1.7.1.jar!/" /> @@ -350,15 +375,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/11fb3d88ae7e3b757d70237064210ceb954a5a04/commons-codec-1.10-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-collections:commons-collections:3.2.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/761ea405b9b37ced573d2df0d1e3a4e0f9edc668/commons-collections-3.2.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.1/fa095ef874374e5b2a11f8b06c26a5d68c7cb3a4/commons-collections-3.2.1-sources.jar!/" /> - </SOURCES> - </library> <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar!/" /> @@ -368,15 +384,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-lang:commons-lang:2.4"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/16313e02a793435009f1e458fa4af5d879f6fb11/commons-lang-2.4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.4/2b8c4b3035e45520ef42033e823c7d33e4b4402c/commons-lang-2.4-sources.jar!/" /> - </SOURCES> - </library> <library name="Gradle: commons-logging:commons-logging:1.2"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar!/" /> @@ -422,31 +429,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.sourceforge.jweather:jweather:0.3.0"> + <library name="Gradle: net.thauvin.erik:semver:0.9.7"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/8bb010d54c64e66b1738524513b50ed263c35290/jweather-0.3.0.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.7/semver-0.9.7.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sourceforge.jweather/jweather/0.3.0/385ecc27f62f9d7c31de57cee468a8df58cb415e/jweather-0.3.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.thauvin.erik:semver:0.9.6-beta"> - <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.6-beta/semver-0.9.6-beta.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.6-beta/semver-0.9.6-beta-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.apache.velocity:velocity:1.7"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/2ceb567b8f3f21118ecdec129fe1271dbc09aa7a/velocity-1.7.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.velocity/velocity/1.7/eb11eb70171ed64842b2e5216d5904e21ed162ac/velocity-1.7-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.7/semver-0.9.7-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> From 16953c8f986a4cb54ef6141497500f5b3da24ea7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 15 May 2017 21:45:26 -0700 Subject: [PATCH 084/842] Added API end point for Delicious. --- .idea/modules/mobibot.iml | 236 ++++++------------ kobalt/src/Build.kt | 1 + kobaltw | 2 +- mobibot.ipr | 8 +- properties/mobibot.properties | 2 + .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../thauvin/erik/mobibot/DeliciousPoster.java | 5 +- .../net/thauvin/erik/mobibot/Mobibot.java | 11 +- version.properties | 2 +- 9 files changed, 93 insertions(+), 178 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 5483171..15c67f6 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,12 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="KOBALT" external.system.module.group="" external.system.module.version="0.7.1-beta+018" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.1-beta+018" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> - <output url="file://$MODULE_DIR$/../../kobaltBuild/classes" /> - <output-test url="file://$MODULE_DIR$/../../kobaltBuild/test-classes" /> + <output url="file://$MODULE_DIR$/../../build/classes/main" /> + <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> <exclude-output /> <content url="file://$MODULE_DIR$/../.."> + <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/annotationProcessor/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> @@ -15,44 +17,9 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module-library"> - <library name="Kobalt: log4j:log4j:jar:1.2.17"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/log4j/log4j/1.2.17/log4j-1.2.17.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: org.slf4j:slf4j-api:jar:1.7.25"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: org.jdom:jdom2:jar:2.0.6"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: com.rometools:rome-utils:jar:1.7.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/com/rometools/rome-utils/1.7.1/rome-utils-1.7.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: file://.\lib\owm-japis-2.5.0.5.jar"> + <orderEntry type="library" scope="PROVIDED" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="module-library" scope="TEST"> + <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> </CLASSES> @@ -60,140 +27,81 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: net.sf.delicious-java:delicious:jar:1.14"> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="module-library" scope="RUNTIME"> + <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/net/sf/delicious-java/delicious/1.14/delicious-1.14.jar!/" /> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> </library> </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: org.twitter4j:twitter4j-core:jar:4.0.6"> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="module-library" scope="PROVIDED"> + <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/org/twitter4j/twitter4j-core/4.0.6/twitter4j-core-4.0.6.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: net.objecthunter:exp4j:jar:0.4.8"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/net/objecthunter/exp4j/0.4.8/exp4j-0.4.8.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: org.ostermiller:utils:jar:1.07.00"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/org/ostermiller/utils/1.07.00/utils-1.07.00.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: org.json:json:jar:20160810"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/org/json/json/20160810/json-20160810.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: org.slf4j:slf4j-log4j12:jar:1.7.25"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: com.rometools:rome:jar:1.7.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/com/rometools/rome/1.7.1/rome-1.7.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: org.jsoup:jsoup:jar:1.10.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/org/jsoup/jsoup/1.10.2/jsoup-1.10.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: oro:oro:jar:2.0.8"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/oro/oro/2.0.8/oro-2.0.8.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: commons-httpclient:commons-httpclient:jar:3.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/commons-httpclient/commons-httpclient/3.1/commons-httpclient-3.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: commons-cli:commons-cli:jar:1.4"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/commons-cli/commons-cli/1.4/commons-cli-1.4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: commons-net:commons-net:jar:3.6"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/commons-net/commons-net/3.6/commons-net-3.6.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: commons-logging:commons-logging:jar:1.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/commons-logging/commons-logging/1.2/commons-logging-1.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: commons-codec:commons-codec:jar:1.10"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/commons-codec/commons-codec/1.10/commons-codec-1.10.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: pircbot:pircbot:jar:1.5.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/pircbot/pircbot/1.5.0/pircbot-1.5.0.jar!/" /> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> </library> </orderEntry> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> </component> </module> \ No newline at end of file diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index d1366b1..7980dba 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -110,6 +110,7 @@ val p = project { } install { + target = deploy include(from("kobaltBuild/libs"), to(target), glob("**/*")) include(from("properties"), to(target), glob("**/*.properties")) collect(compileDependencies) diff --git a/kobaltw b/kobaltw index 287d453..c5186d5 100755 --- a/kobaltw +++ b/kobaltw @@ -1,2 +1,2 @@ -#!/bin/env sh +#!/usr/bin/env sh java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $* diff --git a/mobibot.ipr b/mobibot.ipr index 9c1aed3..a59e8d0 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -163,7 +163,7 @@ <option name="autoDownloadKobalt" value="true" /> <option name="downloadSources" value="false" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.83" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.86" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> @@ -429,13 +429,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:semver:0.9.7"> + <library name="Gradle: net.thauvin.erik:semver:1.0.0"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.7/semver-0.9.7.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.0/semver-1.0.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/0.9.7/semver-0.9.7-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.0/semver-1.0.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index cbb39c4..9c007bc 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -25,6 +25,8 @@ tell-max-size=50 # #delicious-user= #delicious-pwd= +#declicious-api-endpoint=https://api.pinboard.in/v1/ + # # Configure app at: https://apps.twitter.com/ diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index df4e122..52cb9a2 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1493879846880L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1494906988487L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 1; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "018"; + public final static String BUILDMETA = "020"; /** * The full version string. diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java index 7733f90..2d14f71 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java @@ -49,12 +49,13 @@ class DeliciousPoster { /** * Creates a new {@link DeliciousPoster} instance. * + * @param apiEndPoint The API end point. * @param username The del.icio.us user name. * @param password The del.icio.us password. * @param ircServer The IRC server. */ - public DeliciousPoster(final String username, final String password, final String ircServer) { - delicious = new Delicious(username, password); + public DeliciousPoster(final String apiEndPoint, final String username, final String password, final String ircServer) { + delicious = new Delicious(username, password, apiEndPoint); this.ircServer = ircServer; } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index bb9f361..b844841 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot; import com.rometools.rome.io.FeedException; +import del.icio.us.DeliciousConstants; import net.thauvin.erik.mobibot.modules.*; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; @@ -70,7 +71,7 @@ public class Mobibot extends PircBot { // The info strings. private static final String[] INFO_STRS = { ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", - "http://www.mobitopia.org/mobibot/" + "https://www.mobitopia.org/mobibot/" }; // The link match string. @@ -358,6 +359,7 @@ public class Mobibot extends PircBot { // Get the del.icio.us properties final String dname = p.getProperty("delicious-user"); final String dpwd = p.getProperty("delicious-pwd"); + final String dapi = p.getProperty("declicious-api-endpoint", DeliciousConstants.API_ENDPOINT); // Create the bot final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir); @@ -379,7 +381,7 @@ public class Mobibot extends PircBot { bot.setBacklogsUrl(backlogsURL); // Set the del.icio.us authentication - bot.setDeliciousAuth(dname, dpwd); + bot.setDeliciousAuth(dapi, dname, dpwd); // Load the modules properties MODULES.stream().filter(AbstractModule::hasProperties).forEach( @@ -1492,12 +1494,13 @@ public class Mobibot extends PircBot { /** * Sets the del.icio.us authentication. * + * @param apiEndPoint The API end point. * @param username The del.icio.us user name. * @param password The del.icio.us password. */ - private void setDeliciousAuth(final String username, final String password) { + private void setDeliciousAuth(final String apiEndPoint, final String username, final String password) { if (Utils.isValidString(username) && Utils.isValidString(password)) { - delicious = new DeliciousPoster(username, password, ircServer); + delicious = new DeliciousPoster(apiEndPoint, username, password, ircServer); } } diff --git a/version.properties b/version.properties index b414f50..de1e7c5 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=1 version.prerelease=beta -version.buildmeta=018 \ No newline at end of file +version.buildmeta=020 \ No newline at end of file From 73dbd6c0d85288e2f8bace1e25284fc77929876f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 17 May 2017 17:38:14 -0700 Subject: [PATCH 085/842] Implemented Pinboard.in integration. --- .idea/modules/mobibot.iml | 166 ++++++++++-------- build.gradle | 2 +- kobalt/src/Build.kt | 3 +- mobibot.ipr | 42 ++++- properties/log4j.properties | 3 - properties/mobibot.properties | 6 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../net/thauvin/erik/mobibot/EntryLink.java | 4 +- .../net/thauvin/erik/mobibot/Mobibot.java | 67 +++---- .../{DeliciousPoster.java => Pinboard.java} | 70 +++++--- .../java/net/thauvin/erik/mobibot/Utils.java | 2 +- version.properties | 2 +- 12 files changed, 214 insertions(+), 157 deletions(-) rename src/main/java/net/thauvin/erik/mobibot/{DeliciousPoster.java => Pinboard.java} (67%) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 15c67f6..0856423 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.1-beta+018" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.1-beta+020" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/main" /> <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> @@ -18,62 +18,40 @@ <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="module-library" scope="TEST"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160810" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="module-library" scope="RUNTIME"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160810" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="module-library" scope="PROVIDED"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> @@ -83,25 +61,59 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.sf.delicious-java:delicious:1.14" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160810" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="module-library" scope="RUNTIME"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="module-library" scope="TEST"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> </component> </module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6572b46..20ce201 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.6' - compile 'net.sf.delicious-java:delicious:1.14' + compile 'net.thauvin.erik:pinboard-poster:0.9.1' compile files('lib/owm-japis-2.5.0.5.jar') diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 7980dba..3362210 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -14,6 +14,7 @@ import java.io.FileInputStream import java.util.* val bs = buildScript { + repos(localMaven()) } val mainClassName = "net.thauvin.erik.mobibot.Mobibot" @@ -72,7 +73,7 @@ val p = project { compile("net.objecthunter:exp4j:0.4.8") compile("org.twitter4j:twitter4j-core:4.0.6") - compile("net.sf.delicious-java:delicious:1.14") + compile("net.thauvin.erik:pinboard-poster:0.9.1") compile(file("lib/owm-japis-2.5.0.5.jar")) diff --git a/mobibot.ipr b/mobibot.ipr index a59e8d0..df5896c 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -357,6 +357,24 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.1/2bab37f921df082d245d440f96dc63a69cb5f67/rome-1.7.1-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: com.squareup.okhttp3:okhttp:3.8.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.8.0/5a11f020cce2d11eb71ba916700600e18c4547e7/okhttp-3.8.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.8.0/db21293949e200f08d5325e8a8eefdcc9134b752/okhttp-3.8.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: com.squareup.okio:okio:1.13.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.13.0/a9283170b7305c8d92d25aff02a6ab7e45d06cbe/okio-1.13.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.13.0/f4c91e12121af963e3ef76e81c82aa75ba6e8533/okio-1.13.0-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: commons-cli:commons-cli:1.4"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar!/" /> @@ -420,13 +438,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/8d86f148ff1f0d5b624eae9bb0882198ab5cd07/exp4j-0.4.8-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.sf.delicious-java:delicious:1.14"> + <library name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/6fdcbf3ef291e2a2352fc4c27fe033f02206ee1a/delicious-1.14.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.0/pinboard-poster-0.9.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.delicious-java/delicious/1.14/af3389b4f23bb9ac23552bff5ae6ed917df36192/delicious-1.14-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.0/pinboard-poster-0.9.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: net.thauvin.erik:semver:1.0.0"> @@ -447,6 +465,24 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.2-3/552a40eb47669b78f0f194d526cb21b3aa1f8319/kotlin-stdlib-1.1.2-3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.2-3/7502cd343c8990a77aabb7cf41bd7e9f186c06cc/kotlin-stdlib-1.1.2-3-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.jetbrains:annotations:13.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.json:json:20160810"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160810/aca5eb39e2a12fddd6c472b240afe9ebea3a6733/json-20160810.jar!/" /> diff --git a/properties/log4j.properties b/properties/log4j.properties index 201ab08..78b2948 100644 --- a/properties/log4j.properties +++ b/properties/log4j.properties @@ -11,8 +11,5 @@ log4j.appender.stderr.target=System.err log4j.logger.org.apache.commons.httpclient=FATAL log4j.logger.httpclient.wire=FATAL -# Print only messages of priority FATAL or above in the package net.sf.jweather -log4j.category.net.sf.jweather=FATAL - # Print only messages of priority FATAL or above in the package net.thauvin.erik.mobibot log4j.category.net.thauvin.erik.mobibot=FATAL diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 9c007bc..130b9f1 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -21,11 +21,9 @@ tell-max-days=5 tell-max-size=50 # -# Credentials for: http://del.icio.us/ +# Credentials for: http://pinboard.in/ # -#delicious-user= -#delicious-pwd= -#declicious-api-endpoint=https://api.pinboard.in/v1/ +#pinboard-api-token=user\:TOKEN # diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 52cb9a2..7115878 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1494906988487L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1495067438992L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 1; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "020"; + public final static String BUILDMETA = "021"; /** * The full version string. diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 555c2ae..d8bf472 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -206,11 +206,11 @@ public class EntryLink implements Serializable { } /** - * Returns the tags formatted for del.icio.us. + * Returns the tags formatted for pinboard.in * * @return The tags as a comma-delimited string. */ - public final String getDeliciousTags() { + public final String getPinboardTags() { final StringBuilder tags = new StringBuilder(nick); for (final Object tag : this.tags) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index b844841..7227054 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot; import com.rometools.rome.io.FeedException; -import del.icio.us.DeliciousConstants; import net.thauvin.erik.mobibot.modules.*; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; @@ -167,22 +166,16 @@ public class Mobibot extends PircBot { // The default tags/categories. private String defaultTags = ""; - - // The del.icio.us posts handler. - private DeliciousPoster delicious = null; - // The feed URL. private String feedURL = ""; - // The ident message. private String identMsg = ""; - // The ident nick. private String identNick = ""; - // The NickServ ident password. private String identPwd = ""; - + // The pinboard posts handler. + private Pinboard pinboard = null; // Today's date. private String today = Utils.today(); @@ -356,10 +349,8 @@ public class Mobibot extends PircBot { final String identPwd = p.getProperty("ident", ""); final String tags = p.getProperty("tags", ""); - // Get the del.icio.us properties - final String dname = p.getProperty("delicious-user"); - final String dpwd = p.getProperty("delicious-pwd"); - final String dapi = p.getProperty("declicious-api-endpoint", DeliciousConstants.API_ENDPOINT); + // Get the pinboard properties + final String pinApiToken = p.getProperty("pinboard-api-token"); // Create the bot final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir); @@ -380,8 +371,8 @@ public class Mobibot extends PircBot { bot.setFeedURL(feedURL); bot.setBacklogsUrl(backlogsURL); - // Set the del.icio.us authentication - bot.setDeliciousAuth(dapi, dname, dpwd); + // Set the pinboard authentication + bot.setPinboardAuth(pinApiToken); // Load the modules properties MODULES.stream().filter(AbstractModule::hasProperties).forEach( @@ -1044,8 +1035,8 @@ public class Mobibot extends PircBot { final EntryLink entry = entries.get(index); send(channel, Utils.buildLink(index, entry)); - if (delicious != null) { - delicious.addPost(entry); + if (pinboard != null) { + pinboard.addPost(entry); } saveEntries(isBackup); @@ -1152,8 +1143,8 @@ public class Mobibot extends PircBot { final EntryLink entry = entries.get(index); if (entry.getLogin().equals(login) || isOp(sender)) { - if (delicious != null) { - delicious.deletePost(entry); + if (pinboard != null) { + pinboard.deletePost(entry); } entries.remove(index); @@ -1169,8 +1160,8 @@ public class Mobibot extends PircBot { final EntryLink entry = entries.get(index); entry.setTitle(cmd.substring(1).trim()); - if (delicious != null) { - delicious.updatePost(entry.getLink(), entry); + if (pinboard != null) { + pinboard.updatePost(entry.getLink(), entry); } send(channel, Utils.buildLink(index, entry)); @@ -1189,8 +1180,8 @@ public class Mobibot extends PircBot { entry.setLink(link); - if (delicious != null) { - delicious.updatePost(oldLink, entry); + if (pinboard != null) { + pinboard.updatePost(oldLink, entry); } send(channel, Utils.buildLink(index, entry)); @@ -1239,8 +1230,8 @@ public class Mobibot extends PircBot { if (entry.getLogin().equals(login) || isOp(sender)) { entry.setTags(cmd); - if (delicious != null) { - delicious.updatePost(entry.getLink(), entry); + if (pinboard != null) { + pinboard.updatePost(entry.getLink(), entry); } send(channel, Utils.buildTags(index, entry)); @@ -1439,7 +1430,7 @@ public class Mobibot extends PircBot { private void recapResponse(final String sender, final boolean isPrivate) { if (this.recap.size() > 0) { for (final String recap : this.recap) { - send(sender, recap, isPrivate); + send(sender, recap, isPrivate); } } else { send(sender, "Sorry, nothing to recap.", true); @@ -1491,19 +1482,6 @@ public class Mobibot extends PircBot { send(sender, message, false); } - /** - * Sets the del.icio.us authentication. - * - * @param apiEndPoint The API end point. - * @param username The del.icio.us user name. - * @param password The del.icio.us password. - */ - private void setDeliciousAuth(final String apiEndPoint, final String username, final String password) { - if (Utils.isValidString(username) && Utils.isValidString(password)) { - delicious = new DeliciousPoster(apiEndPoint, username, password, ircServer); - } - } - /** * Sets the feed URL. * @@ -1541,6 +1519,17 @@ public class Mobibot extends PircBot { } } + /** + * Sets the pinboard authentication. + * + * @param apiToken The API token + */ + private void setPinboardAuth(final String apiToken) { + if (Utils.isValidString(apiToken)) { + pinboard = new Pinboard(this, apiToken, ircServer); + } + } + /** * Sets the default tags/categories. * diff --git a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java similarity index 67% rename from src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java rename to src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 2d14f71..ff1638d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/DeliciousPoster.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -1,5 +1,5 @@ /* - * DeliciousPoster.java + * Pinboard.java * * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -31,32 +31,46 @@ */ package net.thauvin.erik.mobibot; -import del.icio.us.Delicious; +import net.thauvin.erik.pinboard.PinboardPoster; import javax.swing.*; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; /** - * The class to handle posts to del.icio.us. + * The class to handle posts to pinbard.in. * * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created Mar 5, 2005 + * @created 2017-05-17 * @since 1.0 */ -class DeliciousPoster { - private final Delicious delicious; +class Pinboard { private final String ircServer; + private final PinboardPoster pinboard; /** - * Creates a new {@link DeliciousPoster} instance. + * Creates a new {@link Pinboard} instance. * - * @param apiEndPoint The API end point. - * @param username The del.icio.us user name. - * @param password The del.icio.us password. + * @param bot The bot's instance. + * @param apiToken The API end point. * @param ircServer The IRC server. */ - public DeliciousPoster(final String apiEndPoint, final String username, final String password, final String ircServer) { - delicious = new Delicious(username, password, apiEndPoint); + public Pinboard(final Mobibot bot, final String apiToken, final String ircServer) { + pinboard = new PinboardPoster(apiToken); this.ircServer = ircServer; + + if (bot.getLogger().isDebugEnabled()) { + final ConsoleHandler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.FINE); + final Logger logger = pinboard.getLogger(); + logger.addHandler(consoleHandler); + logger.setLevel(Level.FINE); + } } /** @@ -69,11 +83,11 @@ class DeliciousPoster { @Override protected Boolean doInBackground() throws Exception { - return delicious.addPost(entry.getLink(), + return pinboard.addPin(entry.getLink(), entry.getTitle(), postedBy(entry), - entry.getDeliciousTags(), - entry.getDate()); + entry.getPinboardTags(), + formatDate(entry.getDate())); } }; @@ -92,13 +106,23 @@ class DeliciousPoster { @Override protected Boolean doInBackground() throws Exception { - return delicious.deletePost(link); + return pinboard.deletePin(link); } }; worker.execute(); } + /** + * Format a date to a UTC timestamp. + * + * @param date The date. + * @return The date in {@link DateTimeFormatter#ISO_INSTANT} format. + */ + private String formatDate(final Date date) { + return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT); + } + /** * Returns he del.icio.us extended attribution line. * @@ -121,19 +145,19 @@ class DeliciousPoster { protected Boolean doInBackground() throws Exception { if (!oldUrl.equals(entry.getLink())) { - delicious.deletePost(oldUrl); + pinboard.deletePin(oldUrl); - return delicious.addPost(entry.getLink(), + return pinboard.addPin(entry.getLink(), entry.getTitle(), postedBy(entry), - entry.getDeliciousTags(), - entry.getDate()); + entry.getPinboardTags(), + formatDate(entry.getDate())); } else { - return delicious.addPost(entry.getLink(), + return pinboard.addPin(entry.getLink(), entry.getTitle(), postedBy(entry), - entry.getDeliciousTags(), - entry.getDate(), + entry.getPinboardTags(), + formatDate(entry.getDate()), true, true); } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index a20f18c..2610f20 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -140,7 +140,7 @@ final public class Utils { * @return The entry's tags. */ static String buildTags(final int entryIndex, final EntryLink entry) { - return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getDeliciousTags().replaceAll(",", ", ")); + return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replaceAll(",", ", ")); } /** diff --git a/version.properties b/version.properties index de1e7c5..f372b5b 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=1 version.prerelease=beta -version.buildmeta=020 \ No newline at end of file +version.buildmeta=021 \ No newline at end of file From 06789a80a15a3097ed51db14f5a666589c8cce01 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 18 May 2017 20:16:56 -0700 Subject: [PATCH 086/842] Switched to OkHttp. --- build.gradle | 8 ++++---- kobalt/src/Build.kt | 8 ++++---- .../erik/mobibot/modules/StockQuote.java | 17 ++++++++--------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index 20ce201..502bc00 100644 --- a/build.gradle +++ b/build.gradle @@ -53,18 +53,18 @@ dependencies { compile 'pircbot:pircbot:1.5.0' - compile 'commons-codec:commons-codec:1.10' + //compile 'commons-codec:commons-codec:1.10' compile 'commons-logging:commons-logging:1.2' compile 'commons-net:commons-net:3.6' compile 'commons-cli:commons-cli:1.4' - compile 'commons-httpclient:commons-httpclient:3.1' + compile 'com.squareup.okhttp3:okhttp:3.8.0' compile 'oro:oro:2.0.8' compile 'org.jsoup:jsoup:1.10.2' - compile 'com.rometools:rome:1.7.1' + compile 'com.rometools:rome:1.7.3' compile 'org.slf4j:slf4j-log4j12:1.7.25' - compile 'org.json:json:20160810' + compile 'org.json:json:20170516' compile 'org.ostermiller:utils:1.07.00' compile 'net.objecthunter:exp4j:0.4.8' diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 3362210..4eed577 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -56,18 +56,18 @@ val p = project { compile("pircbot:pircbot:1.5.0") - compile("commons-codec:commons-codec:1.10") + //compile("commons-codec:commons-codec:1.10") compile("commons-logging:commons-logging:1.2") compile("commons-net:commons-net:3.6") compile("commons-cli:commons-cli:1.4") - compile("commons-httpclient:commons-httpclient:3.1") + compile("com.squareup.okhttp3:okhttp:3.8.0") compile("oro:oro:2.0.8") compile("org.jsoup:jsoup:1.10.2") - compile("com.rometools:rome:1.7.1") + compile("com.rometools:rome:1.7.3") compile("org.slf4j:slf4j-log4j12:1.7.25") - compile("org.json:json:20160810") + compile("org.json:json:20170516") compile("org.ostermiller:utils:1.07.00") compile("net.objecthunter:exp4j:0.4.8") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 7545852..6561504 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -33,8 +33,9 @@ package net.thauvin.erik.mobibot.modules; import com.Ostermiller.util.CSVParser; import net.thauvin.erik.mobibot.Mobibot; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.methods.GetMethod; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; import java.io.IOException; @@ -87,14 +88,12 @@ final public class StockQuote extends AbstractModule { */ private void run(final Mobibot bot, final String sender, final String symbol) { try { - final HttpClient client = new HttpClient(); - client.getHttpConnectionManager().getParams().setConnectionTimeout(Mobibot.CONNECT_TIMEOUT); - client.getHttpConnectionManager().getParams().setSoTimeout(Mobibot.CONNECT_TIMEOUT); - final GetMethod getMethod = new GetMethod(YAHOO_URL + symbol.toUpperCase()); - client.executeMethod(getMethod); + final OkHttpClient client = new OkHttpClient(); + final Request request = new Request.Builder().url(YAHOO_URL + symbol.toUpperCase()).build(); + final Response response = client.newCall(request).execute(); - final String[][] lines = CSVParser.parse(getMethod.getResponseBodyAsString()); + final String[][] lines = CSVParser.parse(response.body().string()); if (lines.length > 0) { final String[] quote = lines[0]; @@ -133,7 +132,7 @@ final public class StockQuote extends AbstractModule { } else { bot.send(sender, "No data returned."); } - } catch (IOException e) { + } catch (NullPointerException | IOException e) { bot.getLogger().debug("Unable to retrieve stock quote for: " + symbol, e); bot.send(sender, "An error has occurred retrieving a stock quote: " + e.getMessage()); } From 32f0585a3c3881d49893f50539b444bc9aa95223 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 18 May 2017 20:18:01 -0700 Subject: [PATCH 087/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/Pinboard.java | 8 ++++---- .../java/net/thauvin/erik/mobibot/TellMessagesMgr.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index ff1638d..ee6d45b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -74,7 +74,7 @@ class Pinboard { } /** - * Adds a post to del.icio.us. + * Adds a post to pinboard.in. * * @param entry The entry to add. */ @@ -95,7 +95,7 @@ class Pinboard { } /** - * Deletes a post to del.icio.us. + * Deletes a post to pinboard.in. * * @param entry The entry to delete. */ @@ -124,7 +124,7 @@ class Pinboard { } /** - * Returns he del.icio.us extended attribution line. + * Returns he pinboard.in extended attribution line. * * @param entry The entry. * @return The extended attribution line. @@ -134,7 +134,7 @@ class Pinboard { } /** - * Updates a post to del.icio.us. + * Updates a post to pinboard.in. * * @param oldUrl The old post URL. * @param entry The entry to add. diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index f2b3520..c3f8914 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -102,7 +102,7 @@ final class TellMessagesMgr { } catch (IOException e) { logger.error("An IO error occurred loading the messages queue.", e); } catch (Exception e) { - logger.getLogger().error("An error occurred loading the messages queue.", e); + logger.error("An error occurred loading the messages queue.", e); } return new ArrayList<>(); From 30c29f1a530552f0493fe11ba82f94d6d03fde1c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 May 2017 09:43:16 -0700 Subject: [PATCH 088/842] Moved to Log4J 2. --- .gitignore | 2 +- .idea/modules/mobibot.iml | 83 ++++++------- README.md | 2 +- build.gradle | 25 ++-- kobalt/Build.kt.iml | 15 ++- kobalt/src/Build.kt | 23 ++-- mobibot.ipr | 111 +++++++----------- properties/log4j.properties | 15 --- properties/log4j2.xml | 16 +++ .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../net/thauvin/erik/mobibot/Mobibot.java | 17 +-- .../thauvin/erik/mobibot/TellMessagesMgr.java | 6 +- version.properties | 6 +- 13 files changed, 149 insertions(+), 178 deletions(-) delete mode 100644 properties/log4j.properties create mode 100644 properties/log4j2.xml diff --git a/.gitignore b/.gitignore index 49d96ef..c6fa7ef 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ /gen /gradle.properties /local.properties -/log4j.properties +/log4j2.xml /logs /mobibot.properties /out diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 0856423..3014ce4 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.1-beta+020" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.1-beta+022" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/main" /> <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> @@ -17,41 +17,28 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: log4j:log4j:1.2.17" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20170516" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> <orderEntry type="module-library" scope="PROVIDED"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> @@ -61,24 +48,26 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="library" scope="RUNTIME" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.3" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20170516" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> <orderEntry type="module-library" scope="RUNTIME"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> @@ -88,24 +77,26 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="library" scope="TEST" name="Gradle: log4j:log4j:1.2.17" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-logging:commons-logging:1.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-httpclient:commons-httpclient:3.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: oro:oro:2.0.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-log4j12:1.7.25" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20160810" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20170516" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> <orderEntry type="module-library" scope="TEST"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> diff --git a/README.md b/README.md index 989b506..5dedce8 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Some very basic instructions: cd deploy { configure the properties } - vi *.properties + vi *.properties *.xml { help } java -jar mobibot.jar -h diff --git a/build.gradle b/build.gradle index 502bc00..edacab1 100644 --- a/build.gradle +++ b/build.gradle @@ -49,29 +49,28 @@ repositories { } dependencies { - compile 'log4j:log4j:1.2.17@jar' - compile 'pircbot:pircbot:1.5.0' + + compile 'org.apache.logging.log4j:log4j-api:2.8.2' + compile 'org.apache.logging.log4j:log4j-core:2.8.2' - //compile 'commons-codec:commons-codec:1.10' - compile 'commons-logging:commons-logging:1.2' - compile 'commons-net:commons-net:3.6' compile 'commons-cli:commons-cli:1.4' + + compile 'commons-net:commons-net:3.6' compile 'com.squareup.okhttp3:okhttp:3.8.0' - - compile 'oro:oro:2.0.8' - - compile 'org.jsoup:jsoup:1.10.2' + compile 'com.rometools:rome:1.7.3' - compile 'org.slf4j:slf4j-log4j12:1.7.25' + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.8.2' + compile 'org.json:json:20170516' compile 'org.ostermiller:utils:1.07.00' - + compile 'org.jsoup:jsoup:1.10.2' compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.6' compile 'net.thauvin.erik:pinboard-poster:0.9.1' + // https://bitbucket.org/akapribot/owm-japis compile files('lib/owm-japis-2.5.0.5.jar') compileOnly semverJar @@ -113,9 +112,7 @@ task wrapper(type: Wrapper) { } task copyToDeploy(type: Copy) { - from('properties') { - include '*.properties' - } + from('properties') from jar into deployDir } diff --git a/kobalt/Build.kt.iml b/kobalt/Build.kt.iml index 7cf6bc1..580d86e 100644 --- a/kobalt/Build.kt.iml +++ b/kobalt/Build.kt.iml @@ -10,13 +10,22 @@ <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module-library"> - <library name="Kobalt: com.beust.kobalt:kobalt:jar:1.0.83"> + <library name="Kobalt: kobalt-versioneye-0.4.5.jar"> <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.83/kobalt/wrapper/kobalt-1.0.83.jar!/" /> + <root url="jar://$USER_HOME$/.kobalt/cache/net/thauvin/erik/kobalt-versioneye/0.4.5/kobalt-versioneye-0.4.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="Kobalt: com.beust.kobalt:kobalt:jar:1.0.86"> + <CLASSES> + <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.86/kobalt/wrapper/kobalt-1.0.86.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.83/kobalt/wrapper/kobalt-1.0.83-sources.jar!/" /> + <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.86/kobalt/wrapper/kobalt-1.0.86-sources.jar!/" /> </SOURCES> </library> </orderEntry> diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 4eed577..14b107e 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -15,6 +15,7 @@ import java.util.* val bs = buildScript { repos(localMaven()) + plugins("net.thauvin.erik:kobalt-versioneye:") } val mainClassName = "net.thauvin.erik.mobibot.Mobibot" @@ -52,29 +53,27 @@ val p = project { val lib = "lib" dependencies { - compile("log4j:log4j:jar:1.2.17") - compile("pircbot:pircbot:1.5.0") - //compile("commons-codec:commons-codec:1.10") - compile("commons-logging:commons-logging:1.2") - compile("commons-net:commons-net:3.6") + compile("org.apache.logging.log4j:log4j-api:2.8.2") + compile("org.apache.logging.log4j:log4j-core:2.8.2") + compile("commons-cli:commons-cli:1.4") + + compile("commons-net:commons-net:3.6") compile("com.squareup.okhttp3:okhttp:3.8.0") - compile("oro:oro:2.0.8") + compile("com.rometools:rome:1.7.3", "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.8.2") - compile("org.jsoup:jsoup:1.10.2") - compile("com.rometools:rome:1.7.3") - compile("org.slf4j:slf4j-log4j12:1.7.25") compile("org.json:json:20170516") compile("org.ostermiller:utils:1.07.00") - + compile("org.jsoup:jsoup:1.10.2") compile("net.objecthunter:exp4j:0.4.8") compile("org.twitter4j:twitter4j-core:4.0.6") compile("net.thauvin.erik:pinboard-poster:0.9.1") + // https://bitbucket.org/akapribot/owm-japis/ compile(file("lib/owm-japis-2.5.0.5.jar")) apt(processorJar) @@ -98,7 +97,6 @@ val p = project { attributes("Main-Class", mainClassName) attributes("Class-Path", collect(compileDependencies) - .filter { !it.file.name.startsWith("junit") } .map { it.file.name } .joinToString(" ./$lib/", prefix = ". ./$lib/")) } @@ -113,9 +111,8 @@ val p = project { install { target = deploy include(from("kobaltBuild/libs"), to(target), glob("**/*")) - include(from("properties"), to(target), glob("**/*.properties")) + include(from("properties"), to(target), glob("**/*")) collect(compileDependencies) - .filter { !it.file.name.startsWith("junit") } .forEach { copy(from(it.file.absolutePath), to("$target/$lib")) } diff --git a/mobibot.ipr b/mobibot.ipr index df5896c..6196e9a 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -339,22 +339,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/599674a480f9940ff5e25b375c6f59d14d8a4bfa/compiler-0.9.4-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome-utils:1.7.1"> + <library name="Gradle: com.rometools:rome-utils:1.7.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.1/36592957a19bbfdb6f1182ea3e6036bccaec0bdc/rome-utils-1.7.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.3/53e57b41a8f3440ca808bd5f246cb70ba6dcc67a/rome-utils-1.7.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.1/3fa41b350dc73b3338322471294a83b5bbb1bbae/rome-utils-1.7.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.3/451e50a658f7ca5d59faa68715428093397751f5/rome-utils-1.7.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.7.1"> + <library name="Gradle: com.rometools:rome:1.7.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.1/b15f1e5277ffa19077672ad7f889a636e661d6ee/rome-1.7.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.3/18d04e4c24025077bacc5e4f2ca0fd48587f56e8/rome-1.7.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.1/2bab37f921df082d245d440f96dc63a69cb5f67/rome-1.7.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.3/d30dbef2c5e08f388157dc4cf7d9c55faa37e929/rome-1.7.3-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okhttp3:okhttp:3.8.0"> @@ -384,33 +384,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/40dfd9fdef125e19136135e68d54af6d9b0cfbb8/commons-cli-1.4-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: commons-codec:commons-codec:1.10"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/4b95f4897fa13f2cd904aee711aeafc0c5295cd8/commons-codec-1.10.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.10/11fb3d88ae7e3b757d70237064210ceb954a5a04/commons-codec-1.10-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-httpclient:commons-httpclient:3.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/964cd74171f427720480efdec40a7c7f6e58426a/commons-httpclient-3.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-httpclient/commons-httpclient/3.1/c6d6ea83d0cf16d3ed9c1b7e600fa0f60b9d3159/commons-httpclient-3.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-logging:commons-logging:1.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/ecf26c7507d67782a3bbd148d170b31dfad001aa/commons-logging-1.2-sources.jar!/" /> - </SOURCES> - </library> <library name="Gradle: commons-net:commons-net:3.6"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar!/" /> @@ -420,15 +393,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/961dc27eabbe71bf32478baffe0e1be915ce7689/commons-net-3.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: log4j:log4j:1.2.17"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/5af35056b4d257e4b64b9e8069c0746e8b08629f/log4j-1.2.17.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/log4j/log4j/1.2.17/677abe279b68c5e7490d6d50c6951376238d7d3e/log4j-1.2.17-sources.jar!/" /> - </SOURCES> - </library> <library name="Gradle: net.objecthunter:exp4j:0.4.8"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar!/" /> @@ -438,13 +402,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/8d86f148ff1f0d5b624eae9bb0882198ab5cd07/exp4j-0.4.8-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:pinboard-poster:0.9.0"> + <library name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.0/pinboard-poster-0.9.0.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.1/pinboard-poster-0.9.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.0/pinboard-poster-0.9.0-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.1/pinboard-poster-0.9.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: net.thauvin.erik:semver:1.0.0"> @@ -456,6 +420,33 @@ <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.0/semver-1.0.0-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.8.2/e590eeb783348ce8ddef205b82127f9084d82bf3/log4j-api-2.8.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.8.2/459702eacc545a5478627fe879cec6e004c00f3a/log4j-api-2.8.2-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.8.2/979fc0cf8460302e4ffbfe38c1b66a99450b0bb7/log4j-core-2.8.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.8.2/4693b08b8d9abe152417b9564bb7a68d82630180/log4j-core-2.8.2-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.8.2/36bc2a99b86be26ccdc51fe288458dc712d280c1/log4j-slf4j-impl-2.8.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.8.2/c5f494286f220ba330c6eabccf10740458ef1fab/log4j-slf4j-impl-2.8.2-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar!/" /> @@ -483,13 +474,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20160810"> + <library name="Gradle: org.json:json:20170516"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160810/aca5eb39e2a12fddd6c472b240afe9ebea3a6733/json-20160810.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20170516/949abe1460757b8dc9902c562f83e49675444572/json-20170516.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20160810/92f4b89bc0bb4c7c3f7b52724568a901b7e0195b/json-20160810-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20170516/6d919271d0ac012fb5706abd62fd3aff0f78ed95/json-20170516-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jsoup:jsoup:1.10.2"> @@ -510,22 +501,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.slf4j:slf4j-api:1.7.25"> + <library name="Gradle: org.slf4j:slf4j-api:1.7.24"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.24/3f6b4bd4f8dbe8d4bea06d107a3826469b85c3e9/slf4j-api-1.7.24.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/962153db4a9ea71b79d047dfd1b2a0d80d8f4739/slf4j-api-1.7.25-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.slf4j:slf4j-log4j12:1.7.25"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.25/110cefe2df103412849d72ef7a67e4e91e4266b4/slf4j-log4j12-1.7.25.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.25/d6f907c4254a49f40fa46005b65bc97261ad9e46/slf4j-log4j12-1.7.25-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.24/9f1cecd7c757ad5dde29453d43315228bb0a7c7e/slf4j-api-1.7.24-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.twitter4j:twitter4j-core:4.0.6"> @@ -537,15 +519,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/5a3a910793c3510a3e25cea3c51672bb88124527/twitter4j-core-4.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: oro:oro:2.0.8"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/5592374f834645c4ae250f4c9fbb314c9369d698/oro-2.0.8.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/oro/oro/2.0.8/3598e790ecd76ff7eb249853d4d00822ae1a5e71/oro-2.0.8-sources.jar!/" /> - </SOURCES> - </library> <library name="Gradle: pircbot:pircbot:1.5.0"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> diff --git a/properties/log4j.properties b/properties/log4j.properties deleted file mode 100644 index 78b2948..0000000 --- a/properties/log4j.properties +++ /dev/null @@ -1,15 +0,0 @@ -log4j.rootLogger=FATAL, stderr - -# Standard Error Console Appender -log4j.appender.stderr=org.apache.log4j.ConsoleAppender -log4j.appender.stderr.layout=org.apache.log4j.PatternLayout -#log4j.appender.stderr.layout.ConversionPattern=%d [%t] %-5p %c - %m%n -log4j.appender.stderr.layout.ConversionPattern=%d [%p] %c %x %m%n -log4j.appender.stderr.target=System.err - -# Print only messages of priority FATAL or above in the package org.apache.commons.httpclient -log4j.logger.org.apache.commons.httpclient=FATAL -log4j.logger.httpclient.wire=FATAL - -# Print only messages of priority FATAL or above in the package net.thauvin.erik.mobibot -log4j.category.net.thauvin.erik.mobibot=FATAL diff --git a/properties/log4j2.xml b/properties/log4j2.xml new file mode 100644 index 0000000..6c658d9 --- /dev/null +++ b/properties/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration status="WARN"> + <Appenders> + <Console name="stderr" target="SYSTEM_ERR"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> + </Console> + </Appenders> + <Loggers> + <Logger name="net.thauvin.erik.mobibot" level="warn" additivity="false"> + <AppenderRef ref="stderr"/> + </Logger> + <Root level="error"> + <AppenderRef ref="stderr"/> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 7115878..5e7dd9d 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1495067438992L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1495210914271L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; - public final static int PATCH = 1; + public final static int PATCH = 2; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "021"; + public final static String BUILDMETA = "022"; /** * The full version string. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 7227054..74060b5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -35,8 +35,10 @@ import com.rometools.rome.io.FeedException; import net.thauvin.erik.mobibot.modules.*; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; -import org.apache.commons.logging.impl.Log4JLogger; -import org.apache.log4j.Level; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; import org.jibble.pircbot.PircBot; import org.jibble.pircbot.User; import org.jsoup.Jsoup; @@ -150,7 +152,7 @@ public class Mobibot extends PircBot { private final String ircServer; // The logger. - private final Log4JLogger logger = new Log4JLogger(Mobibot.class.getPackage().getName()); + private final Logger logger = LogManager.getLogger(Mobibot.class); // The logger default level. private final Level loggerLevel; @@ -208,7 +210,7 @@ public class Mobibot extends PircBot { this.logsDir = logsDir; // Set the logger level - loggerLevel = logger.getLogger().getLevel(); + loggerLevel = logger.getLevel(); // Load the current entries, if any. try { @@ -527,7 +529,7 @@ public class Mobibot extends PircBot { * * @return The bot's logger. */ - public final Log4JLogger getLogger() { + public final Logger getLogger() { return logger; } @@ -1399,9 +1401,10 @@ public class Mobibot extends PircBot { versionResponse(sender, true); } else if (cmd.equals(Commands.DEBUG_CMD) && isOp(sender)) { if (logger.isDebugEnabled()) { - logger.getLogger().setLevel(loggerLevel); + Configurator.setLevel(logger.getName(), loggerLevel); } else { - logger.getLogger().setLevel(Level.DEBUG); + Configurator.setLevel(logger.getName(), Level.DEBUG); + } send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index c3f8914..3281ac4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot; -import org.apache.commons.logging.impl.Log4JLogger; +import org.apache.logging.log4j.Logger; import java.io.*; import java.time.Clock; @@ -87,7 +87,7 @@ final class TellMessagesMgr { * @return The {@link net.thauvin.erik.mobibot.TellMessage} array. */ @SuppressWarnings("unchecked") - public static List<TellMessage> load(final String file, final Log4JLogger logger) { + public static List<TellMessage> load(final String file, final Logger logger) { try { try (ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { @@ -115,7 +115,7 @@ final class TellMessagesMgr { * @param messages The {@link net.thauvin.erik.mobibot.TellMessage} array. * @param logger The logger. */ - public static void save(final String file, final List<TellMessage> messages, final Log4JLogger logger) { + public static void save(final String file, final List<TellMessage> messages, final Logger logger) { try { try (ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { diff --git a/version.properties b/version.properties index f372b5b..0d3297b 100644 --- a/version.properties +++ b/version.properties @@ -1,8 +1,8 @@ -#Mon, 17 Apr 2017 22:49:41 -0700 +#Fri, 19 May 2017 09:20:42 -0700 #Mon Dec 07 01:31:00 PST 2015 version.project=mobibot version.major=0 version.minor=7 -version.patch=1 +version.patch=2 version.prerelease=beta -version.buildmeta=021 \ No newline at end of file +version.buildmeta=022 From dfcef78db615af051590c2d62b047ec2f0ac2268 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 May 2017 09:49:05 -0700 Subject: [PATCH 089/842] Updated licenses. --- licenses/JWeather License.txt | 504 ---------------------------- licenses/SLF4J License.txt | 36 +- licenses/delicious-java License.txt | 29 -- 3 files changed, 18 insertions(+), 551 deletions(-) delete mode 100644 licenses/JWeather License.txt delete mode 100644 licenses/delicious-java License.txt diff --git a/licenses/JWeather License.txt b/licenses/JWeather License.txt deleted file mode 100644 index b1e3f5a..0000000 --- a/licenses/JWeather License.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/licenses/SLF4J License.txt b/licenses/SLF4J License.txt index 2b57cf5..e106b98 100644 --- a/licenses/SLF4J License.txt +++ b/licenses/SLF4J License.txt @@ -1,21 +1,21 @@ -Copyright (c) 2004-2014 QOS.ch -All rights reserved. + Copyright (c) 2004-2017 QOS.ch + All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/delicious-java License.txt b/licenses/delicious-java License.txt deleted file mode 100644 index 0ffb557..0000000 --- a/licenses/delicious-java License.txt +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2004-2007, David A. Czarnecki - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of the "David A. Czarnecki" nor the names of - * its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ \ No newline at end of file From f9f219abe5ead168e24e869419c492449963a3f8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 00:21:09 -0700 Subject: [PATCH 090/842] Fixed whois output. --- .../net/thauvin/erik/mobibot/modules/Lookup.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 7fbfa08..776b02e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -51,8 +51,10 @@ final public class Lookup extends AbstractModule { */ public static final String LOOKUP_CMD = "lookup"; - // The whois host. - private static final String WHOIS_HOST = "whois.arin.net"; + /** + * The whois default host. + */ + public static final String WHOIS_HOST = "whois.arin.net"; /** * The default constructor @@ -68,7 +70,7 @@ final public class Lookup extends AbstractModule { * @return The lookup query result string. * @throws java.net.UnknownHostException If the host is unknown. */ - private static String lookup(final String query) + public static String lookup(final String query) throws UnknownHostException { final StringBuilder buffer = new StringBuilder(""); @@ -120,7 +122,7 @@ final public class Lookup extends AbstractModule { public static String[] whois(final String query, final String host) throws IOException { final WhoisClient whois = new WhoisClient(); - String[] lines; + final String[] lines; try { whois.setDefaultTimeout(Mobibot.CONNECT_TIMEOUT); @@ -128,7 +130,11 @@ final public class Lookup extends AbstractModule { whois.setSoTimeout(Mobibot.CONNECT_TIMEOUT); whois.setSoLinger(false, 0); - lines = whois.query('-' + query).split("\n"); + if (host.equals(WHOIS_HOST)) { + lines = whois.query("n - " + query).split("\n"); + } else { + lines = whois.query(query).split("\n"); + } } finally { whois.disconnect(); } From cf2d964b6eb0057b18b65d284de6a29838637db8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 00:21:34 -0700 Subject: [PATCH 091/842] Added tests. --- build.gradle | 7 + kobalt/src/Build.kt | 11 ++ .../java/net/thauvin/erik/mobibot/Utils.java | 14 +- .../erik/mobibot/modules/Weather2.java | 6 +- .../net/thauvin/erik/mobibot/UtilsTest.java | 137 ++++++++++++++++++ .../erik/mobibot/modules/LookupTest.java | 58 ++++++++ 6 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/UtilsTest.java create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java diff --git a/build.gradle b/build.gradle index edacab1..2fdec7d 100644 --- a/build.gradle +++ b/build.gradle @@ -74,6 +74,13 @@ dependencies { compile files('lib/owm-japis-2.5.0.5.jar') compileOnly semverJar + + testCompile 'org.testng:testng:6.11' + testCompile 'org.assertj:assertj-core:3.8.0' +} + +test { + useTestNG() } annotationProcessor { diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 14b107e..cfce410 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -9,6 +9,7 @@ import com.beust.kobalt.plugin.java.javadoc import com.beust.kobalt.plugin.packaging.assemble import com.beust.kobalt.plugin.packaging.install import com.beust.kobalt.plugin.publish.autoGitTag +import net.thauvin.erik.kobalt.plugin.versioneye.versionEye import java.io.File import java.io.FileInputStream import java.util.* @@ -80,6 +81,11 @@ val p = project { compileOnly(processorJar) } + dependenciesTest { + compile("org.testng:testng:6.11") + compile("org.assertj:assertj-core:3.8.0") + } + apt { outputDir = "../src/generated/java/" } @@ -124,6 +130,11 @@ val p = project { author = true links("http://www.jibble.org/javadocs/pircbot/", "http://docs.oracle.com/javase/8/docs/api/") } + + versionEye { + org = "thauvin" + team = "Owners" + } } @Task(name = "deploy", dependsOn = arrayOf("assemble", "install"), description = "Deploy application") diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 2610f20..ecf43ff 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -143,6 +143,16 @@ final public class Utils { return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replaceAll(",", ", ")); } + /** + * Capitalize a string. + * + * @param s The string. + * @return The capitalized string. + */ + public static String capitalize(final String s) { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + /** * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. * @@ -169,7 +179,7 @@ final public class Utils { /** * Returns a property as an int. * - * @param property The port property value. + * @param property The property value. * @param def The default property value. * @return The port or default value if invalid. */ @@ -300,7 +310,7 @@ final public class Utils { * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code> * * @param date The date. - * @return The fromatted date. + * @return The formatted date. */ public static String utcDateTime(final LocalDateTime date) { return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index db91e46..99fd2f1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -63,10 +63,6 @@ public class Weather2 extends AbstractModule { properties.put(OWM_API_KEY_PROP, ""); } - private String capitalize(final String s) { - return s.substring(0, 1).toUpperCase() + s.substring(1); - } - /** * {@inheritDoc} */ @@ -147,7 +143,7 @@ public class Weather2 extends AbstractModule { if (i != 0) { condition.append(", ").append(w.getWeatherDescription()); } else { - condition.append(capitalize(w.getWeatherDescription())); + condition.append(Utils.capitalize(w.getWeatherDescription())); } } bot.send(sender, condition.toString(), isPrivate); diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java new file mode 100644 index 0000000..cedac76 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -0,0 +1,137 @@ +/* + * UtilsTest.java + * + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot; + +import org.jibble.pircbot.Colors; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.File; +import java.time.LocalDateTime; +import java.util.Calendar; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>Utils Test</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2017-05-30 + * @since 1.0 + */ +public class UtilsTest { + final Calendar cal = Calendar.getInstance(); + final LocalDateTime localDateTime = + LocalDateTime.of(1952, 2, 17, 12, 30, 0); + + @BeforeClass + public void setUp() { + cal.set(1952, Calendar.FEBRUARY, 17, 12, 30, 0); + } + + + @Test + public void testBold() throws Exception { + assertThat(Utils.bold(1)).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD); + assertThat(Utils.bold("test")).as("bold(test").isEqualTo(Colors.BOLD + "test" + Colors.BOLD); + } + + @Test + public void testCapitalize() throws Exception { + assertThat(Utils.capitalize("test")).isEqualTo("Test"); + } + + @Test + public void testEnsureDir() throws Exception { + assertThat(Utils.ensureDir("test", false)).as("ensureDir(test, false)") + .isEqualTo("test" + File.separatorChar); + assertThat(Utils.ensureDir("http://erik.thauvin.net", true)) + .as("ensureDir(erik.thauvin.net, true)").isEqualTo("http://erik.thauvin.net/"); + } + + @Test + public void testGetIntProperty() throws Exception { + assertThat(Utils.getIntProperty("10", 1)).as("getIntProperty(10, 1)").isEqualTo(10); + assertThat(Utils.getIntProperty("a", 1)).as("getIntProperty(a, 1)").isEqualTo(1); + } + + @Test + public void testGreen() throws Exception { + assertThat(Utils.green("test")).isEqualTo(Colors.DARK_GREEN + "test" + Colors.NORMAL); + } + + @Test + public void testIsValidString() throws Exception { + assertThat(Utils.isValidString("test")).as("isValidString(test)").isTrue(); + assertThat(Utils.isValidString("")).as("isValidString(empty)").isFalse(); + assertThat(Utils.isValidString(" ")).as("isValidString( )").isFalse(); + assertThat(Utils.isValidString(" \t ")).as("isValidString(tab)").isFalse(); + assertThat(Utils.isValidString(null)).as("isValidString(null)").isFalse(); + } + + @Test + public void testIsoLocalDate() throws Exception { + assertThat(Utils.isoLocalDate(cal.getTime())).as("isoLocalDate(date)").isEqualTo("1952-02-17"); + assertThat(Utils.isoLocalDate(localDateTime)).as("isoLocalDate(localDate)").isEqualTo("1952-02-17"); + } + + @Test + public void testPlural() throws Exception { + assertThat(Utils.plural(1, "test", "tests")).as("plural(1, test, tests)") + .isEqualTo("test"); + assertThat(Utils.plural(2, "test", "tests")).as("plural(2, test, tests)") + .isEqualTo("tests"); + } + + @Test + public void testReverseColor() throws Exception { + assertThat(Utils.reverseColor("test")).isEqualTo(Colors.REVERSE + "test" + Colors.REVERSE); + } + + @Test + public void testToday() throws Exception { + assertThat(Utils.today()).isEqualTo(Utils.isoLocalDate(LocalDateTime.now())); + } + + @Test + public void testUnescapeXml() throws Exception { + assertThat(Utils.unescapeXml("<a name="test & ''">")) + .isEqualTo("<a name=\"test & ''\">"); + } + + @Test + public void testUtcDateTime() throws Exception { + assertThat(Utils.utcDateTime(cal.getTime())).as("utcDateTime(date)").isEqualTo("1952-02-17 12:30"); + assertThat(Utils.utcDateTime(localDateTime)).as("utcDateTime(localDate)") + .isEqualTo("1952-02-17 12:30"); + } +} \ No newline at end of file diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java new file mode 100644 index 0000000..494c0d4 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -0,0 +1,58 @@ +/* + * LookupTest.java + * + * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>Lookup Test</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2017-05-30 + * @since 1.0 + */ +public class LookupTest { + @Test + public void testLookup() throws Exception { + final String result = Lookup.lookup("erik.thauvin.net"); + assertThat(result).as("lookup(erik.thauvin.net)").contains("104.31.77.12"); + } + + @Test + public void testWhois() throws Exception { + final String[] result = Lookup.whois("17.178.96.59", Lookup.WHOIS_HOST); + assertThat(result).as("whois(17.178.96.59)") + .contains("Apple Inc. APPLE-WWNET (NET-17-0-0-0-1) 17.0.0.0 - 17.255.255.255"); + } +} \ No newline at end of file From 7e1c78b92bd98f8643eaf87c35662ba4c33974a5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 00:23:09 -0700 Subject: [PATCH 092/842] Swapped Delicious ref with Pinboard. --- website/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/index.html b/website/index.html index 1fdb583..ac5486d 100644 --- a/website/index.html +++ b/website/index.html @@ -42,7 +42,7 @@ <li><a href="http://hc.apache.org/httpclient-3.x/">Commons HTTPClient</a></li> <li><a href="http://commons.apache.org/proper/commons-logging/">Commons Logging</a></li> <li><a href="http://commons.apache.org/proper/commons-net/">Commons Net</a></li> - <li><a href="https://github.com/czarneckid/delicious-java">delicious-java</a></li> + <li><a href="https://github.com/ethauvin/pinboard-poster">Pinboard Poster/a></li> <li><a href="https://bitbucket.org/akapribot/owm-japis/">OWM JAPIs</a></li> <li><a href="http://www.objecthunter.net/exp4j/">exp4j</a></li> <li><a href="http://ostermiller.org/utils/">OstermillerUtils</a></li> From 3803bec28eefa0e270e9741d5e6accfc81e9a49c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 00:23:40 -0700 Subject: [PATCH 093/842] Added circleci configuration. --- .idea/modules/mobibot.iml | 164 +++++++++--------- README.md | 2 +- circle.yml | 11 ++ mobibot.ipr | 45 ++++- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- version.properties | 4 +- 6 files changed, 140 insertions(+), 90 deletions(-) create mode 100644 circle.yml diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 3014ce4..6f2555c 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.1-beta+022" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.2-beta+022" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/main" /> <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> @@ -17,86 +17,7 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20170516" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> - <orderEntry type="module-library" scope="PROVIDED"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20170516" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> - <orderEntry type="module-library" scope="RUNTIME"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20170516" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> <orderEntry type="module-library" scope="TEST"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> @@ -106,5 +27,88 @@ <SOURCES /> </library> </orderEntry> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.yaml:snakeyaml:1.17" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.64" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.8.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.11" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20170516" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="module-library" scope="RUNTIME"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20170516" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.3" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="module-library" scope="PROVIDED"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20170516" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> </component> </module> \ No newline at end of file diff --git a/README.md b/README.md index 5dedce8..27a184e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..128bc93 --- /dev/null +++ b/circle.yml @@ -0,0 +1,11 @@ +machine: + java: + version: oraclejdk8 + +dependencies: + override: + - chmod +x gradlew + +test: + post: + - cp -r build/reports/* $CIRCLE_TEST_REPORTS/ \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 6196e9a..15b5005 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -38,13 +38,13 @@ <property name="caretWidth" class="java.lang.Integer" /> </properties> </component> - <component name="CopyrightManager" default=""> + <component name="CopyrightManager"> <copyright> - <option name="myName" value="Copyright" /> <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> + <option name="myName" value="Copyright" /> </copyright> <module2copyright> - <element module="Copyright" copyright="Copyright" /> + <element module="Source" copyright="Copyright" /> </module2copyright> <LanguageOptions name="JAVA"> <option name="fileTypeOverride" value="3" /> @@ -55,8 +55,7 @@ </LanguageOptions> </component> <component name="DependencyValidationManager"> - <scope name="Source" pattern="file[mobibot]:src/generated/java//*||file[mobibot]:src/main/java//*" /> - <scope name="Copyright" pattern="file[mobibot]:src/main/java//*&&!file:src/main/java/net/thauvin/erik/mobibot/SwingWorker.java&&!file:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> + <scope name="Source" pattern="(file[mobibot]:src/generated/java//*.java||file[mobibot]:src/main/java//*.java||file[mobibot]:src/test/java//*.java)&&!file[mobibot]:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> </component> <component name="Encoding"> <file url="PROJECT" charset="UTF-8" /> @@ -330,6 +329,15 @@ <mapping directory="$PROJECT_DIR$" vcs="Git" /> </component> <component name="libraryTable"> + <library name="Gradle: com.beust:jcommander:1.64"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.64/456a985ac9b12d34820e4d5de063b2c2fc43ed5a/jcommander-1.64.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.64/1b16adc28aca77f62a61f31380f84961c9c3570d/jcommander-1.64-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/1d1fab03f31a75018fc30ec248859a5c89cd342f/compiler-0.9.4.jar!/" /> @@ -447,6 +455,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.8.2/c5f494286f220ba330c6eabccf10740458ef1fab/log4j-slf4j-impl-2.8.2-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.assertj:assertj-core:3.8.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.8.0/b209d90ff2e279bee3e02547ee7b11349c52d0e3/assertj-core-3.8.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.8.0/85dab3e124fb391b10196df9bfdb43640deabb09/assertj-core-3.8.0-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar!/" /> @@ -510,6 +527,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.24/9f1cecd7c757ad5dde29453d43315228bb0a7c7e/slf4j-api-1.7.24-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.testng:testng:6.11"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.11/1fdd5e22f50b14f6d846163456e8c9a7657626fb/testng-6.11.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.11/77da7afb24d57b640d9803593a6222c7de12dd4e/testng-6.11-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.twitter4j:twitter4j-core:4.0.6"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/f3722af4568b96ee66739267e13211b8b66ac7d4/twitter4j-core-4.0.6.jar!/" /> @@ -519,6 +545,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/5a3a910793c3510a3e25cea3c51672bb88124527/twitter4j-core-4.0.6-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.yaml:snakeyaml:1.17"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.17/7a27ea250c5130b2922b86dea63cbb1cc10a660c/snakeyaml-1.17.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.17/63577e87886c76228db9f8a2c50ea43cde5072eb/snakeyaml-1.17-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: pircbot:pircbot:1.5.0"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 5e7dd9d..3f0da41 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1495210914271L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1496214996413L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 2; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "022"; + public final static String BUILDMETA = "024"; /** * The full version string. diff --git a/version.properties b/version.properties index 0d3297b..e672577 100644 --- a/version.properties +++ b/version.properties @@ -1,8 +1,8 @@ -#Fri, 19 May 2017 09:20:42 -0700 +#Tue, 30 May 2017 23:32:26 -0700 #Mon Dec 07 01:31:00 PST 2015 version.project=mobibot version.major=0 version.minor=7 version.patch=2 version.prerelease=beta -version.buildmeta=022 +version.buildmeta=024 From 4f47bed3487ea5b98873f57e96faa852d6136d02 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 10:59:11 -0700 Subject: [PATCH 094/842] Added more tests. --- .../net/thauvin/erik/mobibot/UtilsTest.java | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index cedac76..273aa6c 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -49,6 +49,9 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class UtilsTest { + static final String ASCII = + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + final Calendar cal = Calendar.getInstance(); final LocalDateTime localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0); @@ -62,20 +65,20 @@ public class UtilsTest { @Test public void testBold() throws Exception { assertThat(Utils.bold(1)).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD); - assertThat(Utils.bold("test")).as("bold(test").isEqualTo(Colors.BOLD + "test" + Colors.BOLD); + assertThat(Utils.bold(ASCII)).as("bold(ascii").isEqualTo(Colors.BOLD + ASCII + Colors.BOLD); } @Test public void testCapitalize() throws Exception { - assertThat(Utils.capitalize("test")).isEqualTo("Test"); + assertThat(Utils.capitalize("this is a test.")).isEqualTo("This is a test."); } @Test public void testEnsureDir() throws Exception { - assertThat(Utils.ensureDir("test", false)).as("ensureDir(test, false)") - .isEqualTo("test" + File.separatorChar); - assertThat(Utils.ensureDir("http://erik.thauvin.net", true)) - .as("ensureDir(erik.thauvin.net, true)").isEqualTo("http://erik.thauvin.net/"); + assertThat(Utils.ensureDir("dir", false)).as("ensureDir(dir, false)") + .isEqualTo("dir" + File.separatorChar); + assertThat(Utils.ensureDir("https://erik.thauvin.net", true)) + .as("ensureDir(erik.thauvin.net, true)").isEqualTo("https://erik.thauvin.net/"); } @Test @@ -86,12 +89,12 @@ public class UtilsTest { @Test public void testGreen() throws Exception { - assertThat(Utils.green("test")).isEqualTo(Colors.DARK_GREEN + "test" + Colors.NORMAL); + assertThat(Utils.green(ASCII)).isEqualTo(Colors.DARK_GREEN + ASCII + Colors.NORMAL); } @Test public void testIsValidString() throws Exception { - assertThat(Utils.isValidString("test")).as("isValidString(test)").isTrue(); + assertThat(Utils.isValidString(ASCII)).as("isValidString(ascii)").isTrue(); assertThat(Utils.isValidString("")).as("isValidString(empty)").isFalse(); assertThat(Utils.isValidString(" ")).as("isValidString( )").isFalse(); assertThat(Utils.isValidString(" \t ")).as("isValidString(tab)").isFalse(); @@ -106,15 +109,18 @@ public class UtilsTest { @Test public void testPlural() throws Exception { - assertThat(Utils.plural(1, "test", "tests")).as("plural(1, test, tests)") - .isEqualTo("test"); - assertThat(Utils.plural(2, "test", "tests")).as("plural(2, test, tests)") - .isEqualTo("tests"); + final String week = "week"; + final String weeks = "weeks"; + + assertThat(Utils.plural(-1, week, weeks)).as("plural(-1)").isEqualTo(week); + assertThat(Utils.plural(0, week, weeks)).as("plural(0)").isEqualTo(week); + assertThat(Utils.plural(1, week, weeks)).as("plural(1)").isEqualTo(week); + assertThat(Utils.plural(2, week, weeks)).as("plural(2)").isEqualTo(weeks); } @Test public void testReverseColor() throws Exception { - assertThat(Utils.reverseColor("test")).isEqualTo(Colors.REVERSE + "test" + Colors.REVERSE); + assertThat(Utils.reverseColor(ASCII)).isEqualTo(Colors.REVERSE + ASCII + Colors.REVERSE); } @Test From ec780ff0fc890116b035513cf20bd3dd8c8ff01c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 10:59:32 -0700 Subject: [PATCH 095/842] Removed blank line. --- properties/mobibot.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 130b9f1..6fe0963 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -25,7 +25,6 @@ tell-max-size=50 # #pinboard-api-token=user\:TOKEN - # # Configure app at: https://apps.twitter.com/ # and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> From e12fceb40627a7ab67b3fe9f6299c42da0ee7768 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 11:00:08 -0700 Subject: [PATCH 096/842] Kobalt 1.0.87 update. --- kobalt/wrapper/kobalt-wrapper.properties | 2 +- mobibot.ipr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index cdcb2d1..4a5b196 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.86 \ No newline at end of file +kobalt.version=1.0.87 \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 15b5005..76b8246 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -162,7 +162,7 @@ <option name="autoDownloadKobalt" value="true" /> <option name="downloadSources" value="false" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.86" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.87" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> From 0eca6aa66032114f1cf585826159b92d4be9a314 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 May 2017 11:00:29 -0700 Subject: [PATCH 097/842] Copyright update. --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Commands.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntryComment.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntryLink.java | 2 +- src/main/java/net/thauvin/erik/mobibot/FeedReader.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/Pinboard.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Tell.java | 2 +- src/main/java/net/thauvin/erik/mobibot/TellMessage.java | 2 +- src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java | 2 +- src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Utils.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/AbstractModule.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Calc.java | 2 +- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Dice.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/GoogleSearch.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Joke.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Ping.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/StockQuote.java | 3 +-- src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/War.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java | 2 +- 26 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 3f0da41..48733f8 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,7 +18,7 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1496214996413L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1496250480235L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 2; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index fd108cd..8dc5c88 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot; /** * The <code>commands</code>, <code>keywords</code> and <code>arguments</code>. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index ca7d8a8..52462bd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -44,7 +44,7 @@ import java.util.List; /** * Manages the feed entries. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index 5c1ea0a..d94e183 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -37,7 +37,7 @@ import java.time.LocalDateTime; /** * The class used to store comments associated to a specific entry. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index d8bf472..0aa32f1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -43,7 +43,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * The class used to store link entries. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index cb82e6a..e7b99f3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -44,7 +44,7 @@ import java.util.List; /** * Reads a RSS feed. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 1, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 74060b5..c98ece2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -52,7 +52,7 @@ import java.util.*; /** * Implements the #mobitopia bot. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Jan 31, 2004 * @since 1.0 */ @@ -817,7 +817,7 @@ public class Mobibot extends PircBot { */ private void infoResponse(final String sender, final boolean isPrivate) { for (final String info : INFO_STRS) { - if (info.startsWith("http://")) { + if (info.startsWith("https://")) { send(sender, Utils.green(info), isPrivate); } else { send(sender, info, isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index ee6d45b..1c126e2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -45,7 +45,7 @@ import java.util.logging.Logger; /** * The class to handle posts to pinbard.in. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2017-05-17 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index 89b149c..98d8ac2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -37,7 +37,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * The <code>Tell</code> command. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-02 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 56d778e..8782dcd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -39,7 +39,7 @@ import java.time.format.DateTimeFormatter; /** * The <code>TellMessage</code> class. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-24 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 3281ac4..f55a074 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -42,7 +42,7 @@ import java.util.List; /** * The Tell Messages Manager. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 954e45c..71e2e26 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -21,7 +21,7 @@ import java.io.InputStreamReader; * </p> * and follow the prompts/instructions. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @author <a href="http://twitter4j.org/en/code-examples.html#oauth" target="_blank">Twitter4J</a> * @created Sep 13, 2010 * @since 1.0 diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index ecf43ff..6329b36 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -42,7 +42,7 @@ import java.util.Date; /** * Miscellaneous utilities class. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index a907b91..107e698 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -39,7 +39,7 @@ import java.util.*; /** * The <code>Module</code> abstract class. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-01 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index c2ab162..1eb88ab 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -41,7 +41,7 @@ import java.text.DecimalFormat; /** * The Calc module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-01 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 76c065d..92b62e3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -50,7 +50,7 @@ import java.util.TreeMap; /** * The CurrentConverter module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 11, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 1a0316f..20d932b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -39,7 +39,7 @@ import java.util.Random; /** * The Dice module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 73d76eb..12438dc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -45,7 +45,7 @@ import java.net.URLEncoder; /** * The GoogleSearch module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 7, 2004 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 7d6a26e..adbb412 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -43,7 +43,7 @@ import java.net.URLConnection; /** * The Joke module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-20 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 776b02e..b73e1b3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -41,7 +41,7 @@ import java.net.UnknownHostException; /** * The Lookup module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-26 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index dacdd23..7b852e3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -40,7 +40,7 @@ import java.util.Random; /** * The Ping module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2016-07-02 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 6561504..447e704 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -42,7 +42,7 @@ import java.io.IOException; /** * The StockQuote module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Feb 7, 2004 * @since 1.0 */ @@ -88,7 +88,6 @@ final public class StockQuote extends AbstractModule { */ private void run(final Mobibot bot, final String sender, final String symbol) { try { - final OkHttpClient client = new OkHttpClient(); final Request request = new Request.Builder().url(YAHOO_URL + symbol.toUpperCase()).build(); final Response response = client.newCall(request).execute(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 714a388..5aa14c8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -39,7 +39,7 @@ import twitter4j.conf.ConfigurationBuilder; /** * The Twitter module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created Sept 10, 2008 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index dd183a4..8ebd4d6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -39,7 +39,7 @@ import java.util.Random; /** * The War module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-28 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 99fd2f1..6dbf36d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -41,7 +41,7 @@ import java.io.IOException; /** * The <code>Weather2</code> module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2017-04-02 * @since 1.0 */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 9a72d7f..52845c2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -43,7 +43,7 @@ import java.util.TreeMap; /** * The WorldTime module. * - * @author <a href="http://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2014-04-27 * @since 1.0 */ From b5fdbe66afa69b9e82ed40528d6da898a3e02fc7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 1 Jun 2017 01:20:04 -0700 Subject: [PATCH 098/842] Refactored constructor. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- .../net/thauvin/erik/mobibot/Mobibot.java | 329 ++++++++---------- version.properties | 2 +- 3 files changed, 153 insertions(+), 182 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 48733f8..a173cdb 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1496250480235L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1496304965310L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 2; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "024"; + public final static String BUILDMETA = "027"; /** * The full version string. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c98ece2..8ca6474 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -63,12 +63,17 @@ public class Mobibot extends PircBot { */ public static final int CONNECT_TIMEOUT = 5000; - // The empty title string. - static final String NO_TITLE = "No Title"; + /** + * The empty title string. + */ + public static final String NO_TITLE = "No Title"; // The default port. private static final int DEFAULT_PORT = 6667; + // The default server. + private static final String DEFAULT_SERVER = "irc.freenode.net"; + // The info strings. private static final String[] INFO_STRS = { ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", @@ -129,22 +134,16 @@ public class Mobibot extends PircBot { // The tell object. private static Tell tell; - - // The main channel. - private final String channel; - // The commands list. private final List<String> commandsList = new ArrayList<>(); - // The entries array. private final List<EntryLink> entries = new ArrayList<>(0); - // The history/backlogs array. private final List<String> history = new ArrayList<>(0); - // The ignored nicks array. private final List<String> ignoredNicks = new ArrayList<>(0); - + // The main channel. + private final String ircChannel; // The IRC port. private final int ircPort; @@ -168,16 +167,22 @@ public class Mobibot extends PircBot { // The default tags/categories. private String defaultTags = ""; + // The feed URL. private String feedURL = ""; + // The ident message. private String identMsg = ""; + // The ident nick. private String identNick = ""; + // The NickServ ident password. private String identPwd = ""; + // The pinboard posts handler. private Pinboard pinboard = null; + // Today's date. private String today = Utils.today(); @@ -187,34 +192,29 @@ public class Mobibot extends PircBot { /** * Creates a new {@link Mobibot} instance. * - * @param server The server. - * @param port The port. - * @param nickname The nickname. - * @param channel The channel. - * @param logsDir The logs directory. + * @param channel The irc channel. + * @param nickname The bot's nickname. + * @param logsDirPath The path to the logs directory. + * @param p The bot's properties. */ @SuppressWarnings("WeakerAccess") - public Mobibot(final String server, - final int port, - final String nickname, - final String channel, - final String logsDir) { + public Mobibot(final String nickname, final String channel, final String logsDirPath, final Properties p) { System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); setName(nickname); - ircServer = server; - ircPort = port; - this.channel = channel; - this.logsDir = logsDir; + ircServer = p.getProperty("server", DEFAULT_SERVER); + ircPort = Utils.getIntProperty(p.getProperty("port"), DEFAULT_PORT); + ircChannel = channel; + logsDir = logsDirPath; // Set the logger level loggerLevel = logger.getLevel(); // Load the current entries, if any. try { - today = EntriesMgr.loadEntries(this.logsDir + EntriesMgr.CURRENT_XML, this.channel, entries); + today = EntriesMgr.loadEntries(logsDir + EntriesMgr.CURRENT_XML, ircChannel, entries); if (logger.isDebugEnabled()) { logger.debug("Last feed: " + today); @@ -232,13 +232,30 @@ public class Mobibot extends PircBot { // Load the backlogs, if any. try { - EntriesMgr.loadBacklogs(this.logsDir + EntriesMgr.NAV_XML, history); + EntriesMgr.loadBacklogs(logsDir + EntriesMgr.NAV_XML, history); } catch (IOException ignore) { ; // Do nothing. } catch (FeedException e) { logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); } + // Initialize the bot + setVerbose(true); + setAutoNickChange(true); + setLogin(p.getProperty("login", getName())); + setVersion(p.getProperty("weblog", "")); + setMessageDelay(MESSAGE_DELAY); + setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), + p.getProperty("ident-msg", "")); + + // Set the URLs + setWeblogUrl(getVersion()); + setFeedURL(p.getProperty("feed", "")); + setBacklogsUrl(Utils.ensureDir(p.getProperty("backlogs", weblogUrl), true)); + + // Set the pinboard authentication + setPinboardAuth(p.getProperty("pinboard-api-token")); + // Load the modules MODULES.add(new Calc()); MODULES.add(new CurrencyConverter()); @@ -252,6 +269,26 @@ public class Mobibot extends PircBot { MODULES.add(new War()); MODULES.add(new Weather2()); MODULES.add(new WorldTime()); + + // Load the modules properties + MODULES.stream().filter(AbstractModule::hasProperties).forEach( + module -> { + for (final String s : module.getPropertyKeys()) { + module.setProperty(s, p.getProperty(s, "")); + } + }); + + // Get the tell command settings + tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); + + // Set the tags + setTags(p.getProperty("tags", "")); + + // Set the ignored nicks + setIgnoredNicks(p.getProperty("ignore", "")); + + // Save the entries + saveEntries(true); } /** @@ -306,15 +343,12 @@ public class Mobibot extends PircBot { System.exit(1); } - // Get the main properties - final String channel = p.getProperty("channel"); - final String server = p.getProperty("server"); - final int port = Utils.getIntProperty(p.getProperty("port"), DEFAULT_PORT); final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase()); + final String channel = p.getProperty("channel"); final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); + // Redirect the stdout and stderr if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { - // Redirect the stdout and stderr PrintStream stdout = null; try { @@ -329,7 +363,8 @@ public class Mobibot extends PircBot { PrintStream stderr = null; try { - stderr = new PrintStream(new FileOutputStream(logsDir + nickname + ".err", true)); + stderr = new PrintStream( + new FileOutputStream(logsDir + nickname + ".err", true)); } catch (IOException e) { System.err.println("Unable to open error (stderr) log file."); e.printStackTrace(System.err); @@ -340,86 +375,11 @@ public class Mobibot extends PircBot { System.setErr(stderr); } - // Get the bot's properties - final String login = p.getProperty("login", nickname); - final String weblogURL = p.getProperty("weblog", ""); - final String feedURL = p.getProperty("feed", ""); - final String backlogsURL = Utils.ensureDir(p.getProperty("backlogs", weblogURL), true); - final String ignoredNicks = p.getProperty("ignore", ""); - final String identNick = p.getProperty("ident-nick", ""); - final String identMsg = p.getProperty("ident-msg", ""); - final String identPwd = p.getProperty("ident", ""); - final String tags = p.getProperty("tags", ""); - - // Get the pinboard properties - final String pinApiToken = p.getProperty("pinboard-api-token"); - // Create the bot - final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir); - - // Get the tell command settings - tell = new Tell(bot, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); - - // Initialize the bot - bot.setVerbose(true); - bot.setAutoNickChange(true); - bot.setLogin(login); - bot.setVersion(weblogURL); - bot.setMessageDelay(MESSAGE_DELAY); - bot.setIdentity(identPwd, identNick, identMsg); - - // Set the URLs - bot.setWeblogUrl(weblogURL); - bot.setFeedURL(feedURL); - bot.setBacklogsUrl(backlogsURL); - - // Set the pinboard authentication - bot.setPinboardAuth(pinApiToken); - - // Load the modules properties - MODULES.stream().filter(AbstractModule::hasProperties).forEach( - module -> { - for (final String s : module.getPropertyKeys()) { - module.setProperty(s, p.getProperty(s, "")); - } - }); - - // Set the tags - bot.setTags(tags); - - // Set the ignored nicks - bot.setIgnoredNicks(ignoredNicks); - - // Save the entries - bot.saveEntries(true); + final Mobibot bot = new Mobibot(nickname, channel, logsDir, p); // Connect - try { - bot.connect(server, port); - } catch (Exception e) { - int retries = 0; - - while ((retries++ < MAX_RECONNECT) && !bot.isConnected()) { - sleep(10); - - try { - bot.connect(server, port); - } catch (Exception ignore) { - if (retries == MAX_RECONNECT) { - System.err.println( - "Unable to connect to " + server + " after " + MAX_RECONNECT + " retries."); - e.printStackTrace(System.err); - System.exit(1); - } - } - } - } - - bot.setVersion(INFO_STRS[0]); - - bot.identify(); - - bot.joinChannel(channel); + bot.connect(); } } @@ -442,7 +402,7 @@ public class Mobibot extends PircBot { * @param action The action. */ final public void action(final String action) { - action(channel, action); + action(ircChannel, action); } /** @@ -457,6 +417,42 @@ public class Mobibot extends PircBot { } } + /** + * Connects to the server and joins the channel. + */ + public final void connect() { + try { + connect(ircServer, ircPort); + } catch (Exception e) { + int retries = 0; + + while ((retries++ < MAX_RECONNECT) && !isConnected()) { + sleep(10); + + try { + connect(ircServer, ircPort); + } catch (Exception ex) { + if (retries == MAX_RECONNECT) { + if (logger.isDebugEnabled()) { + logger.debug( + "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", + ex); + } + + e.printStackTrace(System.err); + System.exit(1); + } + } + } + } + + setVersion(INFO_STRS[0]); + + identify(); + + joinChannel(); + } + /** * Responds with the title and links from the RSS feed. * @@ -494,16 +490,16 @@ public class Mobibot extends PircBot { * @return The backlogs URL. */ public final String getBacklogsUrl() { - return this.backLogsUrl; + return backLogsUrl; } /** * Sets the backlogs URL. * - * @param backLogsUrl The backlogs URL. + * @param url The backlogs URL. */ - private void setBacklogsUrl(final String backLogsUrl) { - this.backLogsUrl = backLogsUrl; + private void setBacklogsUrl(final String url) { + backLogsUrl = url; } /** @@ -512,7 +508,7 @@ public class Mobibot extends PircBot { * @return The current channel. */ public final String getChannel() { - return channel; + return ircChannel; } /** @@ -521,7 +517,7 @@ public class Mobibot extends PircBot { * @return The irc server. */ public final String getIrcServer() { - return this.ircServer; + return ircServer; } /** @@ -539,7 +535,7 @@ public class Mobibot extends PircBot { * @return the log directory. */ public final String getLogsDir() { - return this.logsDir; + return logsDir; } /** @@ -570,7 +566,7 @@ public class Mobibot extends PircBot { * @return Today's date. */ public String getToday() { - return this.today; + return today; } /** @@ -579,16 +575,16 @@ public class Mobibot extends PircBot { * @return The weblog URL. */ public final String getWeblogUrl() { - return this.weblogUrl; + return weblogUrl; } /** * Sets the weblog URL. * - * @param weblogUrl The weblog URL. + * @param url The weblog URL. */ - private void setWeblogUrl(final String weblogUrl) { - this.weblogUrl = weblogUrl; + private void setWeblogUrl(final String url) { + weblogUrl = url; } /** @@ -641,9 +637,9 @@ public class Mobibot extends PircBot { } else if (lcTopic.equals(Commands.VIEW_CMD)) { send(sender, "To list or search the current URL posts:"); send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); - } else if (lcTopic.equals(channel.substring(1).toLowerCase())) { + } else if (lcTopic.equals(ircChannel.substring(1).toLowerCase())) { send(sender, "To list the last 5 posts from the channel's weblog:"); - send(sender, helpIndent(getNick() + ": " + channel.substring(1))); + send(sender, helpIndent(getNick() + ": " + ircChannel.substring(1))); } else if (lcTopic.equals(Commands.RECAP_CMD)) { send(sender, "To list the last 10 public channel messages:"); send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); @@ -686,7 +682,7 @@ public class Mobibot extends PircBot { } } - send(sender, Utils.bold("Type a URL on " + channel + " to post it.")); + send(sender, Utils.bold("Type a URL on " + ircChannel + " to post it.")); send(sender, "For more information on a specific command, type:"); send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); send(sender, "The commands are:"); @@ -694,7 +690,7 @@ public class Mobibot extends PircBot { if (commandsList.isEmpty()) { commandsList.add(Commands.IGNORE_CMD); commandsList.add(Commands.INFO_CMD); - commandsList.add(channel.substring(1)); + commandsList.add(ircChannel.substring(1)); commandsList.add(Commands.HELP_POSTING_KEYWORD); commandsList.add(Commands.HELP_TAGS_KEYWORD); commandsList.add(Commands.RECAP_CMD); @@ -882,13 +878,13 @@ public class Mobibot extends PircBot { } /** - * Returns <code>true</code> if the specified sender is an Op on the {@link #channel channel}. + * Returns <code>true</code> if the specified sender is an Op on the {@link #ircChannel channel}. * * @param sender The sender. * @return true, if the sender is an Op. */ public boolean isOp(final String sender) { - final User[] users = getUsers(channel); + final User[] users = getUsers(ircChannel); for (final User user : users) { if (user.getNick().equals(sender)) { @@ -899,6 +895,13 @@ public class Mobibot extends PircBot { return false; } + /** + * Joins the bot's channel. + */ + public final void joinChannel() { + joinChannel(ircChannel); + } + /** * {@inheritDoc} */ @@ -908,7 +911,7 @@ public class Mobibot extends PircBot { final String hostname, final String target, final String action) { - if (target.equals(channel)) { + if (target.equals(ircChannel)) { storeRecap(sender, action, true); } } @@ -924,37 +927,7 @@ public class Mobibot extends PircBot { sleep(5); - // Connect - try { - connect(ircServer, ircPort); - } catch (Exception e) { - int retries = 0; - - while ((retries++ < MAX_RECONNECT) && !isConnected()) { - sleep(10); - - try { - connect(ircServer, ircPort); - } catch (Exception ex) { - if (retries == MAX_RECONNECT) { - if (logger.isDebugEnabled()) { - logger.debug( - "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", - ex); - } - - e.printStackTrace(System.err); - System.exit(1); - } - } - } - } - - setVersion(INFO_STRS[0]); - - identify(); - - joinChannel(channel); + connect(); } /** @@ -1339,17 +1312,17 @@ public class Mobibot extends PircBot { sendRawLine("QUIT : Poof!"); System.exit(0); } else if (cmd.equals(Commands.DIE_CMD) && isOp(sender)) { - send(channel, sender + " has just signed my death sentence."); + send(ircChannel, sender + " has just signed my death sentence."); saveEntries(true); sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); } else if (cmd.equals(Commands.CYCLE_CMD)) { - send(channel, sender + " has just asked me to leave. I'll be back!"); + send(ircChannel, sender + " has just asked me to leave. I'll be back!"); sleep(0); - partChannel(channel); + partChannel(ircChannel); sleep(10); - joinChannel(channel); + joinChannel(ircChannel); } else if (cmd.equals(Commands.RECAP_CMD)) { recapResponse(sender, true); } else if (cmd.equals(Commands.USERS_CMD)) { @@ -1369,13 +1342,11 @@ public class Mobibot extends PircBot { } else { helpResponse(sender, Commands.ME_CMD); } - } else if (cmd.equals(Commands.NICK_CMD) && (cmds.length > 1)) { - if (isOp(sender)) { - changeNick(args); - } + } else if (cmd.equals(Commands.NICK_CMD) && (cmds.length > 1) && isOp(sender)) { + changeNick(args); } else if (cmd.equals(Commands.SAY_CMD) && isOp(sender)) { if (cmds.length > 1) { - send(channel, args, true); + send(ircChannel, args, true); } else { helpResponse(sender, Commands.SAY_CMD); } @@ -1431,8 +1402,8 @@ public class Mobibot extends PircBot { * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void recapResponse(final String sender, final boolean isPrivate) { - if (this.recap.size() > 0) { - for (final String recap : this.recap) { + if (recap.size() > 0) { + for (final String recap : recap) { send(sender, recap, isPrivate); } } else { @@ -1497,14 +1468,14 @@ public class Mobibot extends PircBot { /** * Sets the bot's identification. * - * @param identPwd The password for NickServ, if any. - * @param identNick The ident nick name. - * @param identMsg The ident message. + * @param pwd The password for NickServ, if any. + * @param nick The ident nick name. + * @param msg The ident message. */ - private void setIdentity(final String identPwd, final String identNick, final String identMsg) { - this.identPwd = identPwd; - this.identNick = identNick; - this.identMsg = identMsg; + private void setIdentity(final String pwd, final String nick, final String msg) { + identPwd = pwd; + identNick = nick; + identMsg = msg; } /** @@ -1565,7 +1536,7 @@ public class Mobibot extends PircBot { * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void usersResponse(final String sender, final boolean isPrivate) { - final User[] users = getUsers(channel); + final User[] users = getUsers(ircChannel); final String[] nicks = new String[users.length]; for (int i = 0; i < users.length; i++) { diff --git a/version.properties b/version.properties index e672577..9ba0d36 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=2 version.prerelease=beta -version.buildmeta=024 +version.buildmeta=027 From 30f06d97ce8d3e417c221586dbbc03f8136df650 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 2 Jul 2017 16:43:44 -0700 Subject: [PATCH 099/842] Libaries and plugins updates. --- build.gradle | 17 +++++++++-------- kobalt/src/Build.kt | 7 ++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 2fdec7d..1eed98a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" - id "com.github.ben-manes.versions" version "0.13.0" + id "com.github.ben-manes.versions" version "0.15.0" } apply plugin: 'java' @@ -12,7 +12,7 @@ defaultTasks 'deploy' def packageName = 'net.thauvin.erik.mobibot' def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -def semverJar = 'net.thauvin.erik:semver:1.0.0' +def semverJar = 'net.thauvin.erik:semver:1.0.1' def getVersion(isIncrement = false) { def propsFile = 'version.properties' @@ -50,21 +50,22 @@ repositories { dependencies { compile 'pircbot:pircbot:1.5.0' - + compileOnly 'pircbot:pircbot:1.5.0:sources' + compile 'org.apache.logging.log4j:log4j-api:2.8.2' compile 'org.apache.logging.log4j:log4j-core:2.8.2' compile 'commons-cli:commons-cli:1.4' - + compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.8.0' - + compile 'com.squareup.okhttp3:okhttp:3.8.1' + compile 'com.rometools:rome:1.7.3' compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.8.2' - + compile 'org.json:json:20170516' compile 'org.ostermiller:utils:1.07.00' - compile 'org.jsoup:jsoup:1.10.2' + compile 'org.jsoup:jsoup:1.10.3' compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.6' diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index cfce410..fadcbf4 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -50,11 +50,12 @@ val p = project { version = versionFor() - val processorJar = "net.thauvin.erik:semver:1.0.0" + val processorJar = "net.thauvin.erik:semver:1.0.1" val lib = "lib" dependencies { compile("pircbot:pircbot:1.5.0") + //compileOnly("pircbot:pircbot::sources:1.5.0") compile("org.apache.logging.log4j:log4j-api:2.8.2") compile("org.apache.logging.log4j:log4j-core:2.8.2") @@ -62,13 +63,13 @@ val p = project { compile("commons-cli:commons-cli:1.4") compile("commons-net:commons-net:3.6") - compile("com.squareup.okhttp3:okhttp:3.8.0") + compile("com.squareup.okhttp3:okhttp:3.8.1") compile("com.rometools:rome:1.7.3", "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.8.2") compile("org.json:json:20170516") compile("org.ostermiller:utils:1.07.00") - compile("org.jsoup:jsoup:1.10.2") + compile("org.jsoup:jsoup:1.10.3") compile("net.objecthunter:exp4j:0.4.8") compile("org.twitter4j:twitter4j-core:4.0.6") From c69048d935be49cde68dc435452156dd862ff4d8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 Jul 2017 00:04:21 -0700 Subject: [PATCH 100/842] Updated to Rome 1.7.4. --- build.gradle | 2 +- kobalt/src/Build.kt | 11 +++++------ .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 6 ++---- version.properties | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 1eed98a..aa78bf4 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,7 @@ dependencies { compile 'commons-net:commons-net:3.6' compile 'com.squareup.okhttp3:okhttp:3.8.1' - compile 'com.rometools:rome:1.7.3' + compile 'com.rometools:rome:1.7.4' compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.8.2' compile 'org.json:json:20170516' diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index fadcbf4..a5cdf4b 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -57,15 +57,14 @@ val p = project { compile("pircbot:pircbot:1.5.0") //compileOnly("pircbot:pircbot::sources:1.5.0") - compile("org.apache.logging.log4j:log4j-api:2.8.2") - compile("org.apache.logging.log4j:log4j-core:2.8.2") + compile("org.apache.logging.log4j:log4j-api:2.8.2", + "org.apache.logging.log4j:log4j-core:2.8.2", + "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.8.2") - compile("commons-cli:commons-cli:1.4") - - compile("commons-net:commons-net:3.6") + compile("commons-cli:commons-cli:1.4", "commons-net:commons-net:3.6") compile("com.squareup.okhttp3:okhttp:3.8.1") - compile("com.rometools:rome:1.7.3", "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.8.2") + compile("com.rometools:rome:1.7.4") compile("org.json:json:20170516") compile("org.ostermiller:utils:1.07.00") diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index a173cdb..ffd7bb2 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1496304965310L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1500447579392L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 2; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "027"; + public final static String BUILDMETA = "028"; /** * The full version string. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 8ca6474..69a0d13 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -63,10 +63,8 @@ public class Mobibot extends PircBot { */ public static final int CONNECT_TIMEOUT = 5000; - /** - * The empty title string. - */ - public static final String NO_TITLE = "No Title"; + // The empty title string. + static final String NO_TITLE = "No Title"; // The default port. private static final int DEFAULT_PORT = 6667; diff --git a/version.properties b/version.properties index 9ba0d36..bd7f2d3 100644 --- a/version.properties +++ b/version.properties @@ -5,4 +5,4 @@ version.major=0 version.minor=7 version.patch=2 version.prerelease=beta -version.buildmeta=027 +version.buildmeta=028 From a70683b76f53c63bb1502398614ceb570388d6a0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 10:16:29 -0700 Subject: [PATCH 101/842] Gradle & Kobalt updates. --- gradle/wrapper/gradle-wrapper.jar | Bin 54783 -> 54712 bytes gradle/wrapper/gradle-wrapper.properties | 5 ++--- gradlew | 6 +++--- kobalt/wrapper/kobalt-wrapper.properties | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9785bdb0326373c0795fde9eb5e8e80ecdb79d99..ed88a042a287c140a32e1639edfc91b2a233da8c 100644 GIT binary patch delta 17164 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 AzW@LL delta 17118 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 It|K5Z02u4xVgLXD diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cbb2c54..731147b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Apr 17 22:31:51 PDT 2017 +distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 4453cce..cccdd3d 100644 --- a/gradlew +++ b/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index 4a5b196..21b1f73 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.87 \ No newline at end of file +kobalt.version=1.0.90 \ No newline at end of file From afed3fcbe9027a20762439fee255ef5079730bcd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 10:17:27 -0700 Subject: [PATCH 102/842] log4J, rome, okhttp3, json, testng updates. --- build.gradle | 18 +++++++++--------- kobalt/src/Build.kt | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index aa78bf4..65ddc3b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.2" - id "com.github.ben-manes.versions" version "0.15.0" + id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.4" + id "com.github.ben-manes.versions" version "0.17.0" } apply plugin: 'java' @@ -52,18 +52,18 @@ dependencies { compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile 'org.apache.logging.log4j:log4j-api:2.8.2' - compile 'org.apache.logging.log4j:log4j-core:2.8.2' + compile 'org.apache.logging.log4j:log4j-api:2.9.1' + compile 'org.apache.logging.log4j:log4j-core:2.9.1' + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.9.1' compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.8.1' + compile 'com.squareup.okhttp3:okhttp:3.9.0' - compile 'com.rometools:rome:1.7.4' - compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.8.2' + compile 'com.rometools:rome:1.8.0' - compile 'org.json:json:20170516' + compile 'org.json:json:20171018' compile 'org.ostermiller:utils:1.07.00' compile 'org.jsoup:jsoup:1.10.3' compile 'net.objecthunter:exp4j:0.4.8' @@ -76,7 +76,7 @@ dependencies { compileOnly semverJar - testCompile 'org.testng:testng:6.11' + testCompile 'org.testng:testng:6.12' testCompile 'org.assertj:assertj-core:3.8.0' } diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index a5cdf4b..aa5b10c 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -57,16 +57,16 @@ val p = project { compile("pircbot:pircbot:1.5.0") //compileOnly("pircbot:pircbot::sources:1.5.0") - compile("org.apache.logging.log4j:log4j-api:2.8.2", - "org.apache.logging.log4j:log4j-core:2.8.2", - "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.8.2") + compile("org.apache.logging.log4j:log4j-api:2.9.1", + "org.apache.logging.log4j:log4j-core:2.9.1", + "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.9.1") compile("commons-cli:commons-cli:1.4", "commons-net:commons-net:3.6") - compile("com.squareup.okhttp3:okhttp:3.8.1") + compile("com.squareup.okhttp3:okhttp:3.9.0") - compile("com.rometools:rome:1.7.4") + compile("com.rometools:rome:1.8.0") - compile("org.json:json:20170516") + compile("org.json:json:20171018") compile("org.ostermiller:utils:1.07.00") compile("org.jsoup:jsoup:1.10.3") compile("net.objecthunter:exp4j:0.4.8") @@ -82,8 +82,8 @@ val p = project { } dependenciesTest { - compile("org.testng:testng:6.11") - compile("org.assertj:assertj-core:3.8.0") + compile("org.testng:testng:6.12") + compile("org.assertj:assertj-core:3.9.0") } apt { From 9c25eab0c63c3a626b7944d5be236e00ac1469be Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 10:17:50 -0700 Subject: [PATCH 103/842] Beta 30 --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- version.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index ffd7bb2..2ce0538 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1500447579392L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1509296915700L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 2; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "028"; + public final static String BUILDMETA = "030"; /** * The full version string. diff --git a/version.properties b/version.properties index bd7f2d3..434dfb8 100644 --- a/version.properties +++ b/version.properties @@ -1,8 +1,8 @@ -#Tue, 30 May 2017 23:32:26 -0700 +#Sun, 29 Oct 2017 09:38:57 -0700 #Mon Dec 07 01:31:00 PST 2015 version.project=mobibot version.major=0 version.minor=7 version.patch=2 version.prerelease=beta -version.buildmeta=028 +version.buildmeta=030 From b9643bdfd81247d15235ce3b595d9dbcf21b1f47 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 10:18:03 -0700 Subject: [PATCH 104/842] Added debug.log. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c6fa7ef..a5e4d03 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ /test-output /weather.log CVS +debug.log ehthumbs.db kobaltBuild kobaltw-test From ac53321f8f1e82e363054942199bdb88d9daf474 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 10:33:42 -0700 Subject: [PATCH 105/842] Added Java 9. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 58e8252..d106d01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: java jdk: - oraclejdk8 - + - oraclejdk9 + before_install: - chmod +x gradlew \ No newline at end of file From abb704cfba0393042fd6a184d6b80875f305a09d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 18:33:19 -0700 Subject: [PATCH 106/842] Added CircleCi 2.0 configuration. --- .circleci/config.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..3de2103 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,27 @@ +version: 2 +jobs: + build: + working_directory: ~/code + docker: + - image: circleci/android:api-26-alpha + environment: + JVM_OPTS: -Xmx3200m + steps: + - checkout + - restore_cache: + key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} + - run: + name: Download Dependencies + command: ./gradlew androidDependencies + - save_cache: + paths: + - ~/.gradle + key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} + - run: + name: Run Tests + command: ./gradlew lint test + - store_artifacts: + path: app/build/reports + destination: reports + - store_test_results: + path: app/build/test-results \ No newline at end of file From f2a7f6c95bcae02e2d78c3189643b7d5be03ef93 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 18:39:41 -0700 Subject: [PATCH 107/842] Made Gradle wrapper executable via: `git update-index --chmod=+x gradlew` --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From d39a2d5938458d2c3f2a236120359a6cb5415d77 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 18:44:46 -0700 Subject: [PATCH 108/842] Removed CircleCi 2.0 configuration. --- .circleci/config.yml | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 3de2103..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: 2 -jobs: - build: - working_directory: ~/code - docker: - - image: circleci/android:api-26-alpha - environment: - JVM_OPTS: -Xmx3200m - steps: - - checkout - - restore_cache: - key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} - - run: - name: Download Dependencies - command: ./gradlew androidDependencies - - save_cache: - paths: - - ~/.gradle - key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} - - run: - name: Run Tests - command: ./gradlew lint test - - store_artifacts: - path: app/build/reports - destination: reports - - store_test_results: - path: app/build/test-results \ No newline at end of file From 8f29277dedffdb8d27d86782a3ac376325991611 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 20:16:57 -0700 Subject: [PATCH 109/842] Added CircleCi 2.0 configuration. --- .circleci/config.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..f9c1af4 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,35 @@ +version: 2 +jobs: + build: + docker: + - image: circleci/openjdk:8-jdk + + working_directory: ~/repo + + environment: + JVM_OPTS: -Xmx3200m + TERM: dumb + + steps: + - checkout + - restore_cache: + keys: + - gradle-dependencies-{{ checksum "build.gradle" }} + # fallback to using the latest cache if no exact match is found + - gradle-dependencies- + + - run: + name: Gradle Dependencies + command: ./gradlew dependencies + + - save_cache: + paths: + - ~/.m2 + key: gradle-dependencies-{{ checksum "build.gradle" }} + + - run: + name: Run Tests + command: ./gradlew test + + - store_test_results: + path: build/reports/ \ No newline at end of file From 7102d4e90bfd744061a3d47baa04c9878eb5ec79 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 20:26:59 -0700 Subject: [PATCH 110/842] Added artifacts storage. --- .circleci/config.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f9c1af4..787b192 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,4 +32,6 @@ jobs: command: ./gradlew test - store_test_results: - path: build/reports/ \ No newline at end of file + path: build/reports + - store_artifacts: + path: build/reports \ No newline at end of file From 473f6a5c85c8be36d5d74336fa222c218245140d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 20:46:05 -0700 Subject: [PATCH 111/842] Collect test results. --- .circleci/config.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 787b192..3c82ef6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,7 @@ jobs: - gradle-dependencies- - run: - name: Gradle Dependencies + name: Gradle dependencies command: ./gradlew dependencies - save_cache: @@ -31,7 +31,13 @@ jobs: name: Run Tests command: ./gradlew test + - run: + name: Save test results + command: | + mkdir -p ~/test/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always - store_test_results: - path: build/reports + path: ~/test - store_artifacts: - path: build/reports \ No newline at end of file + path: ~/test \ No newline at end of file From 291e5488afd6f7b99b62303f4a1fa6bf58937afc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 20:47:51 -0700 Subject: [PATCH 112/842] Fixed test results location. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3c82ef6..6a09428 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,7 +35,7 @@ jobs: name: Save test results command: | mkdir -p ~/test/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + find . -type f -regex ".*/build/test-results/test/.*xml" -exec cp {} ~/junit/ \; when: always - store_test_results: path: ~/test From 75118c5a50737481eb1d2819ee0348cf048a4ea0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 20:49:47 -0700 Subject: [PATCH 113/842] Fixed test results location again. --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6a09428..6d6239f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,7 @@ jobs: - gradle-dependencies- - run: - name: Gradle dependencies + name: Gradle Dependencies command: ./gradlew dependencies - save_cache: @@ -32,10 +32,10 @@ jobs: command: ./gradlew test - run: - name: Save test results + name: Save Test Results command: | mkdir -p ~/test/ - find . -type f -regex ".*/build/test-results/test/.*xml" -exec cp {} ~/junit/ \; + find . -type f -regex "build/test-results/test/.*xml" -exec cp {} ~/junit/ \; when: always - store_test_results: path: ~/test From a070956a5801220319b307b8177bc4ec06e3d6cc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 20:52:04 -0700 Subject: [PATCH 114/842] Fixed test results location again. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6d6239f..a9c4c65 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,7 +35,7 @@ jobs: name: Save Test Results command: | mkdir -p ~/test/ - find . -type f -regex "build/test-results/test/.*xml" -exec cp {} ~/junit/ \; + find . -type f -regex "build/test-results/test/.*xml" -exec cp {} ~/test/ \; when: always - store_test_results: path: ~/test From 22ff6cd7beb31e849cbf2f8f338a6117410a9306 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 21:00:10 -0700 Subject: [PATCH 115/842] Copy test-results to ~/test. --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a9c4c65..c54fdac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,8 +34,8 @@ jobs: - run: name: Save Test Results command: | - mkdir -p ~/test/ - find . -type f -regex "build/test-results/test/.*xml" -exec cp {} ~/test/ \; + mkdir -p ~/test + cp -a build/reports/tests/test/. ~/test when: always - store_test_results: path: ~/test From d07069a001a669ae73f8fabd227e253fd8b39e93 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 21:32:12 -0700 Subject: [PATCH 116/842] Removed CircleCI 1.0 configuration. --- circle.yml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 circle.yml diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 128bc93..0000000 --- a/circle.yml +++ /dev/null @@ -1,11 +0,0 @@ -machine: - java: - version: oraclejdk8 - -dependencies: - override: - - chmod +x gradlew - -test: - post: - - cp -r build/reports/* $CIRCLE_TEST_REPORTS/ \ No newline at end of file From 641f11767dcf300590b7fcee97b1e9e6bc5ba150 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Oct 2017 22:11:30 -0700 Subject: [PATCH 117/842] Cleanup. --- .circleci/config.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c54fdac..2af8879 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,13 +23,12 @@ jobs: command: ./gradlew dependencies - save_cache: - paths: - - ~/.m2 + paths: ~/.m2 key: gradle-dependencies-{{ checksum "build.gradle" }} - run: - name: Run Tests - command: ./gradlew test + name: Run All Checks + command: ./gradlew check - run: name: Save Test Results From e65dcfa5b50fcaf25c227fcbd9764949dba9b899 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Oct 2017 20:36:31 -0700 Subject: [PATCH 118/842] Changed store_artifacts & store_test_results syntax. --- .circleci/config.yml | 13 ++++--------- .../net/thauvin/erik/mobibot/ReleaseInfo.class | Bin 0 -> 2269 bytes 2 files changed, 4 insertions(+), 9 deletions(-) create mode 100644 src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.class diff --git a/.circleci/config.yml b/.circleci/config.yml index 2af8879..555e2de 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,13 +30,8 @@ jobs: name: Run All Checks command: ./gradlew check - - run: - name: Save Test Results - command: | - mkdir -p ~/test - cp -a build/reports/tests/test/. ~/test - when: always - - store_test_results: - path: ~/test - store_artifacts: - path: ~/test \ No newline at end of file + path: build/reports/ + destination: reports + - store_test_results: + path: build/reports/tests/test \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.class b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.class new file mode 100644 index 0000000000000000000000000000000000000000..4da25d2fe157c9d17d62c545787c6b674766d432 GIT binary patch literal 2269 zcmbVNZFdt@5PojocDtk$LSHOkMJ-TDX;47XA`;RRwj@nWLV2lLH_2_gbh{fjn@ZIm z;wOIt9?p>*JV(F#d;IM2xw~nzr8#^sJ-xd#nVDy1o|&7!|9$-@fE)PPz-fH0VP3~A z13mac!)+a38tBIz9ccr1kxA0>o{sxUY3f)?Lf~FMAlB_O3;!6%A#Y$A1p_PiYLG@n z10|G|`9pQACK>p#`r?U(rvfKeirHc|mra+l>wH}_9}6UN&#YZ5<yh`!sucLPyE!i~ zl=0j~V7bAn<us+hiMtQY+(JHEP9OMbE5*W6HdEHICNLO&zmP6x1ty|^fxRPBIj?Ft z3sxY@lo+=^pI#~y1%%nl(pXJ@-drvem7q~rNtZMCmCz$#baLH~ukZ!5)oih37M2AD zZrQFK+!pAazO*XPm+@-MbvS3ca=E!vk$%~#IMf)~=T%t`Y}O=js?xNbT3!a$BRkky z@#Tj7TwrASQnx-_m_DlQYB*F1tm<~&s<$CW>b@*WM_LUThtY3Uop#Pf$!q#mxoE2l zPqb}Kcf*@gd?auMa{?DT+&pv}&3fJQ16eE7rEjTn4(ejpO~;X&mUFJEnDd*}!1K?s z`OcgQ^|ip1D}z+9Wi@wgHzj?0JGJ9g?1~qpV%TKnasnUYI(vHc+EoqTB=9ZP)c|;2 zz_?bOo!jzijo|@&r-OM`l(D#yz$faZufoRasA{MsAhDrgGl4DG8lEMv4JU#3@qvb& z1YCH^=bD1MrlYPReV@QA&JwLtcA@f2Rs*u$&Q@Tw8z-bc<`m5_2gH%?X0R0wh-ttF z`R@}fOkjM++LoO1sud`L_UD}j{X*5iWeJlA$JmUPK_KC6Wb0maD{niFEig8{)Sia8 z8RtC8B~(83Txr(0QjNVvAa@qzhShXf(80vTtmrx3K|QoQ+yQUnAdfrk9vp&=?Og;q zA5ZR3Gy+BCg==rRf!vh7fF5}Jts9*-yAHZ0=J>}C+%7yE-Io8S=>n}GP$R-!o$5@7 z?e2QpG8H8qvAJle(cjbXBl*vH-gdK2-b~k(e#WsH4M~cBiKpI<5zX~|%dI)GF%>z^ zzcu?qj^(|&>|(waix7?SJ}F`6+aWUC(~=#?c?~}aoIg@ux7-9K-)@JGc<-O)Wolpo zXK<Fc?Yrt~#WBj0F?oRUdolSOWo{{LC-5Pr4*bqjz7UfyQl5^<mnhG~;tWx~9Fuj* zTyMr5U>3aJc%~@LpqCiH@tIc;f1u|{viBAG{-Q?UDo^$62z-h@++<DQQ&2Og?hU@F z2$P{d<0c1M&|Xjzc@Bj7bZC3MZF`qbg*K7Yf5+gjI5`tOjG5O2!i%J?+<QXc<4g>A zfMNd7j`4T)1|_CC#(2EA+VMKr$683diTyaVJ&HZ_CiZcKJ@h7ah1gYM%MP~APRzBj z*T}H?Tpq!23nyDhws7htMtWNqJ)!aF=YI=hEsVd$JHSg!{6_PrIg`-%KEjGdi89Oc p3JIDcK`Bx)OZ`oL--FNa1D^)Hn%KiGZ5G;oS?U&{&IjT7=s#pI3TOZT literal 0 HcmV?d00001 From c2ff3fce660812f94b8790a26da6b5b861c7e389 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Oct 2017 20:40:26 -0700 Subject: [PATCH 119/842] Changed store_test_results path. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 555e2de..a8eea21 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,4 +34,4 @@ jobs: path: build/reports/ destination: reports - store_test_results: - path: build/reports/tests/test \ No newline at end of file + path: build/reports/ \ No newline at end of file From 8a83b07ce7dc1c428839f4addbc903e0bc38e7f7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 31 Oct 2017 23:26:38 -0700 Subject: [PATCH 120/842] Fixed invalid dependency. --- kobalt/src/Build.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index aa5b10c..03e8e89 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -83,7 +83,7 @@ val p = project { dependenciesTest { compile("org.testng:testng:6.12") - compile("org.assertj:assertj-core:3.9.0") + compile("org.assertj:assertj-core:3.8.0") } apt { From 2b84150c00d4c708eed5f36f07262aef71adc8bc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 10:44:54 -0800 Subject: [PATCH 121/842] Ignore kobalt test wrappers. --- .gitignore | 4 ++-- mobibot.ipr | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index a5e4d03..2de67f8 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,5 @@ CVS debug.log ehthumbs.db kobaltBuild -kobaltw-test -Thumbs.db \ No newline at end of file +kobaltw*-test +Thumbs.db diff --git a/mobibot.ipr b/mobibot.ipr index 76b8246..e43c9a5 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -162,7 +162,7 @@ <option name="autoDownloadKobalt" value="true" /> <option name="downloadSources" value="false" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.87" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.90" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> @@ -172,9 +172,6 @@ </KobaltProjectSettings> </option> </component> - <component name="KotlinCommonCompilerArguments"> - <option name="coroutinesWarn" value="false" /> - </component> <component name="MavenImportPreferences"> <option name="generalSettings"> <MavenGeneralSettings> From fa54c5beffdffc7b6535f16f9d0d51ad6bfe72e9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 10:45:19 -0800 Subject: [PATCH 122/842] Added workflow to build both in Gradle and Kobalt. --- .circleci/config.yml | 50 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a8eea21..2fb057c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ version: 2 jobs: - build: + checkout_code: docker: - image: circleci/openjdk:8-jdk @@ -12,6 +12,11 @@ jobs: steps: - checkout + + build_gradle: + working_directory: ~/repo + + steps: - restore_cache: keys: - gradle-dependencies-{{ checksum "build.gradle" }} @@ -34,4 +39,45 @@ jobs: path: build/reports/ destination: reports - store_test_results: - path: build/reports/ \ No newline at end of file + path: build/reports/ + + build_kobalt: + working_directory: ~/repo + + steps: + - restore_cache: + keys: + - kobalt-dependencies-{{ checksum "kobalt/src/Build.kt" }} + # fallback to using the latest cache if no exact match is found + - kobalt-dependencies- + + - run: + name: Check Versions + command: ./kobaltw checkVersions + + - save_cache: + paths: ~/.kobalt + key: kobalt-dependencies-{{ checksum "kobalt/src/Build.kt" }} + + - run: + name: Assemble & Test + command: ./kobaltw assemble test + + - store_artifacts: + path: kobaltBuild/test-output/ + destination: test-output + - store_test_results: + path: kobaltBuild/test-output/ + +workflows: + version: 2 + build_gradle_and_kobalt: + jobs: + - checkout_code + - build_gradle + requires: + - checkout_code + + - build_kobalt + requires: + - checkout_code \ No newline at end of file From 0a7b56a46f7236b357b588a7325901041d6b851c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 11:44:55 -0800 Subject: [PATCH 123/842] Fix workflow. --- .circleci/config.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2fb057c..0506485 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,16 +6,16 @@ jobs: working_directory: ~/repo - environment: - JVM_OPTS: -Xmx3200m - TERM: dumb - steps: - checkout - build_gradle: + build: working_directory: ~/repo + environment: + JVM_OPTS: -Xmx3200m + TERM: dumb + steps: - restore_cache: keys: @@ -44,6 +44,10 @@ jobs: build_kobalt: working_directory: ~/repo + environment: + JVM_OPTS: -Xmx3200m + TERM: dumb + steps: - restore_cache: keys: @@ -74,7 +78,7 @@ workflows: build_gradle_and_kobalt: jobs: - checkout_code - - build_gradle + - build requires: - checkout_code From 4fea5763451def7f941cf618816169daa7afa128 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 11:49:53 -0800 Subject: [PATCH 124/842] More workflow fixes. --- .circleci/config.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0506485..70a7a1f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,5 @@ -version: 2 +version: 2.0 + jobs: checkout_code: docker: @@ -9,7 +10,7 @@ jobs: steps: - checkout - build: + build_gradle: working_directory: ~/repo environment: @@ -78,7 +79,7 @@ workflows: build_gradle_and_kobalt: jobs: - checkout_code - - build + - build_gradle requires: - checkout_code From 5c224b47232536a6b0a0eff67be2af5ec0858225 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 11:56:16 -0800 Subject: [PATCH 125/842] Added defaults. --- .circleci/config.yml | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 70a7a1f..7fdfcb3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,23 +1,19 @@ +defaults: &defaults + working_directory: ~/repo + docker: + - image: circleci/openjdk:8-jdk + environment: + JVM_OPTS: -Xmx3200m + TERM: dumb + version: 2.0 jobs: - checkout_code: - docker: - - image: circleci/openjdk:8-jdk - - working_directory: ~/repo + build_gradle: + <<: *defaults steps: - checkout - - build_gradle: - working_directory: ~/repo - - environment: - JVM_OPTS: -Xmx3200m - TERM: dumb - - steps: - restore_cache: keys: - gradle-dependencies-{{ checksum "build.gradle" }} @@ -43,13 +39,10 @@ jobs: path: build/reports/ build_kobalt: - working_directory: ~/repo - - environment: - JVM_OPTS: -Xmx3200m - TERM: dumb + <<: *defaults steps: + - checkout - restore_cache: keys: - kobalt-dependencies-{{ checksum "kobalt/src/Build.kt" }} @@ -78,11 +71,5 @@ workflows: version: 2 build_gradle_and_kobalt: jobs: - - checkout_code - build_gradle - requires: - - checkout_code - - - build_kobalt - requires: - - checkout_code \ No newline at end of file + - build_kobalt \ No newline at end of file From f8f4995ad90551d0d5071c0ff7dd998676c99e0d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 18:55:13 -0800 Subject: [PATCH 126/842] Gradle wrapper update. --- gradle/wrapper/gradle-wrapper.jar | Bin 54712 -> 54727 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ed88a042a287c140a32e1639edfc91b2a233da8c..27768f1bbac3ce2d055b20d521f12da78d331e8e 100644 GIT binary patch delta 2147 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 delta 2187 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 731147b..62e1e30 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists From 81a36cdf463e97152bec1aebc0ba858fb05d86ab Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 18:55:29 -0800 Subject: [PATCH 127/842] Added JDK 9 build configuration. --- .circleci/config.yml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7fdfcb3..2070e3e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,6 +9,14 @@ defaults: &defaults version: 2.0 jobs: + jdk8: + docker: + - image: circleci/openjdk:8-jdk + + jdk9: + docker: + - image: circleci/openjdk:9-jdk + build_gradle: <<: *defaults @@ -69,7 +77,16 @@ jobs: workflows: version: 2 - build_gradle_and_kobalt: + build_jdk8: jobs: - build_gradle - - build_kobalt \ No newline at end of file + requires: + - jdk8 + - build_kobalt + requires: + - jdk8 + build_jdk9: + jobs: + - build_gradle + requires: + - jdk9 \ No newline at end of file From 46768519e733ab83f49178d824a5f0be10760d81 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 19:11:45 -0800 Subject: [PATCH 128/842] Fix jdk 9 build. --- .circleci/config.yml | 57 ++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2070e3e..c07915b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,5 @@ defaults: &defaults working_directory: ~/repo - docker: - - image: circleci/openjdk:8-jdk environment: JVM_OPTS: -Xmx3200m TERM: dumb @@ -9,17 +7,13 @@ defaults: &defaults version: 2.0 jobs: - jdk8: - docker: - - image: circleci/openjdk:8-jdk - - jdk9: - docker: - - image: circleci/openjdk:9-jdk build_gradle: <<: *defaults + docker: + - image: circleci/openjdk:8-jdk + steps: - checkout - restore_cache: @@ -49,6 +43,9 @@ jobs: build_kobalt: <<: *defaults + docker: + - image: circleci/openjdk:8-jdk + steps: - checkout - restore_cache: @@ -75,18 +72,42 @@ jobs: - store_test_results: path: kobaltBuild/test-output/ + build_gradle_jdk9: + <<: *defaults + + docker: + - image: circleci/openjdk:9-jdk + + steps: + - checkout + - restore_cache: + keys: + - gradle-dependencies-{{ checksum "build.gradle" }} + # fallback to using the latest cache if no exact match is found + - gradle-dependencies- + + - run: + name: Gradle Dependencies + command: ./gradlew dependencies + + - save_cache: + paths: ~/.m2 + key: gradle-dependencies-{{ checksum "build.gradle" }} + + - run: + name: Run All Checks + command: ./gradlew check + + - store_artifacts: + path: build/reports/ + destination: reports + - store_test_results: + path: build/reports/ + workflows: version: 2 build_jdk8: jobs: - build_gradle - requires: - - jdk8 - build_kobalt - requires: - - jdk8 - build_jdk9: - jobs: - - build_gradle - requires: - - jdk9 \ No newline at end of file + - build_gradle_jdk9 \ No newline at end of file From 987a8f9d2fef7b7089dfb66e0c1ec8cbf5aa8489 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 19:18:21 -0800 Subject: [PATCH 129/842] Split Kobalt & Gradle workflow jobs. --- .circleci/config.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c07915b..b50d75c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -106,8 +106,10 @@ jobs: workflows: version: 2 - build_jdk8: + build_gradle: jobs: - build_gradle - - build_kobalt - - build_gradle_jdk9 \ No newline at end of file + - build_gradle_jdk9 + build_kobalt: + jobs: + - build_kobalt \ No newline at end of file From 6128a3d8c93bcb4c06e43f0197e208ed79f35ec8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 6 Nov 2017 19:20:40 -0800 Subject: [PATCH 130/842] Renamed jobs. --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b50d75c..d455fe1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -106,10 +106,10 @@ jobs: workflows: version: 2 - build_gradle: + gradle: jobs: - build_gradle - build_gradle_jdk9 - build_kobalt: + kobalt: jobs: - build_kobalt \ No newline at end of file From b1ff19ac20efc62c628b4ce5416bfd0b03cf3c14 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Jan 2018 17:37:59 -0800 Subject: [PATCH 131/842] Updated copyright. --- LICENCE.txt | 27 ++++++++++++++ .../thauvin/erik/mobibot/ReleaseInfo.class | Bin 2269 -> 0 bytes .../net/thauvin/erik/mobibot/ReleaseInfo.java | 34 +++++++++++++++++- .../net/thauvin/erik/mobibot/Commands.java | 2 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 2 +- .../thauvin/erik/mobibot/EntryComment.java | 2 +- .../net/thauvin/erik/mobibot/EntryLink.java | 2 +- .../net/thauvin/erik/mobibot/FeedReader.java | 2 +- .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- .../net/thauvin/erik/mobibot/Pinboard.java | 2 +- .../java/net/thauvin/erik/mobibot/Tell.java | 2 +- .../net/thauvin/erik/mobibot/TellMessage.java | 2 +- .../thauvin/erik/mobibot/TellMessagesMgr.java | 2 +- .../java/net/thauvin/erik/mobibot/Utils.java | 2 +- .../erik/mobibot/modules/AbstractModule.java | 2 +- .../thauvin/erik/mobibot/modules/Calc.java | 2 +- .../mobibot/modules/CurrencyConverter.java | 2 +- .../thauvin/erik/mobibot/modules/Dice.java | 2 +- .../erik/mobibot/modules/GoogleSearch.java | 2 +- .../thauvin/erik/mobibot/modules/Joke.java | 2 +- .../thauvin/erik/mobibot/modules/Lookup.java | 2 +- .../thauvin/erik/mobibot/modules/Ping.java | 2 +- .../erik/mobibot/modules/StockQuote.java | 2 +- .../thauvin/erik/mobibot/modules/Twitter.java | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 2 +- .../erik/mobibot/modules/Weather2.java | 2 +- .../erik/mobibot/modules/WorldTime.java | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.java | 2 +- .../erik/mobibot/modules/LookupTest.java | 2 +- 29 files changed, 86 insertions(+), 27 deletions(-) create mode 100644 LICENCE.txt delete mode 100644 src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.class diff --git a/LICENCE.txt b/LICENCE.txt new file mode 100644 index 0000000..91706eb --- /dev/null +++ b/LICENCE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of this project nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.class b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.class deleted file mode 100644 index 4da25d2fe157c9d17d62c545787c6b674766d432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2269 zcmbVNZFdt@5PojocDtk$LSHOkMJ-TDX;47XA`;RRwj@nWLV2lLH_2_gbh{fjn@ZIm z;wOIt9?p>*JV(F#d;IM2xw~nzr8#^sJ-xd#nVDy1o|&7!|9$-@fE)PPz-fH0VP3~A z13mac!)+a38tBIz9ccr1kxA0>o{sxUY3f)?Lf~FMAlB_O3;!6%A#Y$A1p_PiYLG@n z10|G|`9pQACK>p#`r?U(rvfKeirHc|mra+l>wH}_9}6UN&#YZ5<yh`!sucLPyE!i~ zl=0j~V7bAn<us+hiMtQY+(JHEP9OMbE5*W6HdEHICNLO&zmP6x1ty|^fxRPBIj?Ft z3sxY@lo+=^pI#~y1%%nl(pXJ@-drvem7q~rNtZMCmCz$#baLH~ukZ!5)oih37M2AD zZrQFK+!pAazO*XPm+@-MbvS3ca=E!vk$%~#IMf)~=T%t`Y}O=js?xNbT3!a$BRkky z@#Tj7TwrASQnx-_m_DlQYB*F1tm<~&s<$CW>b@*WM_LUThtY3Uop#Pf$!q#mxoE2l zPqb}Kcf*@gd?auMa{?DT+&pv}&3fJQ16eE7rEjTn4(ejpO~;X&mUFJEnDd*}!1K?s z`OcgQ^|ip1D}z+9Wi@wgHzj?0JGJ9g?1~qpV%TKnasnUYI(vHc+EoqTB=9ZP)c|;2 zz_?bOo!jzijo|@&r-OM`l(D#yz$faZufoRasA{MsAhDrgGl4DG8lEMv4JU#3@qvb& z1YCH^=bD1MrlYPReV@QA&JwLtcA@f2Rs*u$&Q@Tw8z-bc<`m5_2gH%?X0R0wh-ttF z`R@}fOkjM++LoO1sud`L_UD}j{X*5iWeJlA$JmUPK_KC6Wb0maD{niFEig8{)Sia8 z8RtC8B~(83Txr(0QjNVvAa@qzhShXf(80vTtmrx3K|QoQ+yQUnAdfrk9vp&=?Og;q zA5ZR3Gy+BCg==rRf!vh7fF5}Jts9*-yAHZ0=J>}C+%7yE-Io8S=>n}GP$R-!o$5@7 z?e2QpG8H8qvAJle(cjbXBl*vH-gdK2-b~k(e#WsH4M~cBiKpI<5zX~|%dI)GF%>z^ zzcu?qj^(|&>|(waix7?SJ}F`6+aWUC(~=#?c?~}aoIg@ux7-9K-)@JGc<-O)Wolpo zXK<Fc?Yrt~#WBj0F?oRUdolSOWo{{LC-5Pr4*bqjz7UfyQl5^<mnhG~;tWx~9Fuj* zTyMr5U>3aJc%~@LpqCiH@tIc;f1u|{viBAG{-Q?UDo^$62z-h@++<DQQ&2Og?hU@F z2$P{d<0c1M&|Xjzc@Bj7bZC3MZF`qbg*K7Yf5+gjI5`tOjG5O2!i%J?+<QXc<4g>A zfMNd7j`4T)1|_CC#(2EA+VMKr$683diTyaVJ&HZ_CiZcKJ@h7ah1gYM%MP~APRzBj z*T}H?Tpq!23nyDhws7htMtWNqJ)!aF=YI=hEsVd$JHSg!{6_PrIg`-%KEjGdi89Oc p3JIDcK`Bx)OZ`oL--FNa1D^)Hn%KiGZ5G;oS?U&{&IjT7=s#pI3TOZT diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 2ce0538..5f06ccc 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,3 +1,35 @@ +/* + * ReleaseInfo.java + * + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /* * This file is automatically generated. * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -18,7 +50,7 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1509296915700L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1516151817482L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 2; diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 8dc5c88..e9227fb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -1,7 +1,7 @@ /* * Commands.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 52462bd..8535ebf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -1,7 +1,7 @@ /* * EntriesMgr.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index d94e183..32ec725 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -1,7 +1,7 @@ /* * EntryComment.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 0aa32f1..81d1fd3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -1,7 +1,7 @@ /* * EntryLink.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index e7b99f3..c78f90a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -1,7 +1,7 @@ /* * FeedReader.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 69a0d13..f26cb20 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1,7 +1,7 @@ /* * Mobibot.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 1c126e2..85da409 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -1,7 +1,7 @@ /* * Pinboard.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index 98d8ac2..c32f4f5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -1,7 +1,7 @@ /* * Tell.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 8782dcd..3318c19 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -1,7 +1,7 @@ /* * TellMessage.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index f55a074..367e8d7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -1,7 +1,7 @@ /* * TellMessagesMgr.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 6329b36..72e9a0d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -1,7 +1,7 @@ /* * Utils.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 107e698..70c2cf8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -1,7 +1,7 @@ /* * AbstractModule.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 1eb88ab..677dd5b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -1,7 +1,7 @@ /* * Calc.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 92b62e3..b6a2f60 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -1,7 +1,7 @@ /* * CurrencyConverter.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 20d932b..2447c89 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -1,7 +1,7 @@ /* * Dice.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 12438dc..ef19b94 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -1,7 +1,7 @@ /* * GoogleSearch.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index adbb412..5d6cc5f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -1,7 +1,7 @@ /* * Joke.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index b73e1b3..d4e4253 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -1,7 +1,7 @@ /* * Lookup.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 7b852e3..7bb9cbe 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -1,7 +1,7 @@ /* * Ping.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 447e704..8496c17 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -1,7 +1,7 @@ /* * StockQuote.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 5aa14c8..2c50499 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -1,7 +1,7 @@ /* * Twitter.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 8ebd4d6..93e7864 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,7 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 6dbf36d..e876238 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -1,7 +1,7 @@ /* * Weather2.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 52845c2..8bba863 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -1,7 +1,7 @@ /* * WorldTime.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index 273aa6c..8e18cd2 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -1,7 +1,7 @@ /* * UtilsTest.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index 494c0d4..18c3972 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -1,7 +1,7 @@ /* * LookupTest.java * - * Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without From a375af1352fd2b1e4223f8470ae87a651d297f95 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Jan 2018 17:39:20 -0800 Subject: [PATCH 132/842] Dependencies updates. --- build.gradle | 42 ++++++++++---------- kobalt/src/Build.kt | 18 ++++----- pom.xml | 95 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 30 deletions(-) create mode 100644 pom.xml diff --git a/build.gradle b/build.gradle index 65ddc3b..c19e5f0 100644 --- a/build.gradle +++ b/build.gradle @@ -9,18 +9,18 @@ apply plugin: 'application' defaultTasks 'deploy' -def packageName = 'net.thauvin.erik.mobibot' -def deployDir = 'deploy' +final def packageName = 'net.thauvin.erik.mobibot' +final def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -def semverJar = 'net.thauvin.erik:semver:1.0.1' +final def semverJar = 'net.thauvin.erik:semver:1.0.1' def getVersion(isIncrement = false) { - def propsFile = 'version.properties' - def majorKey = 'version.major' - def minorKey = 'version.minor' - def patchKey = 'version.patch' - def metaKey = 'version.buildmeta' - def preKey = 'version.prerelease' + final def propsFile = 'version.properties' + final def majorKey = 'version.major' + final def minorKey = 'version.minor' + final def patchKey = 'version.patch' + final def metaKey = 'version.buildmeta' + final def preKey = 'version.prerelease' if (isIncrement) { ant.propertyfile(file: propsFile) { entry(key: patchKey, @@ -30,9 +30,9 @@ def getVersion(isIncrement = false) { } } def p = new Properties() - file(propsFile).withInputStream { stream -> p.load(stream) } - def metadata = p.getProperty(metaKey, '') - def prerelease = p.getProperty(preKey, '') + file(propsFile).withInputStream { final stream -> p.load(stream) } + final def metadata = p.getProperty(metaKey, '') + final def prerelease = p.getProperty(preKey, '') return (p.getProperty(majorKey, '1') + '.' + p.getProperty(minorKey, '0') + '.' + p.getProperty(patchKey, '0') + (prerelease.length() > 0 ? '-' + prerelease : '') + (metadata.length() > 0 ? '+' + metadata : '')) } @@ -52,32 +52,32 @@ dependencies { compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile 'org.apache.logging.log4j:log4j-api:2.9.1' - compile 'org.apache.logging.log4j:log4j-core:2.9.1' - compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.9.1' + compile 'org.apache.logging.log4j:log4j-api:2.10.0' + compile 'org.apache.logging.log4j:log4j-core:2.10.0' + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.10.0' compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.9.0' + compile 'com.squareup.okhttp3:okhttp:3.9.1' - compile 'com.rometools:rome:1.8.0' + compile 'com.rometools:rome:1.9.0' compile 'org.json:json:20171018' compile 'org.ostermiller:utils:1.07.00' - compile 'org.jsoup:jsoup:1.10.3' + compile 'org.jsoup:jsoup:1.11.2' compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.6' - compile 'net.thauvin.erik:pinboard-poster:0.9.1' + compile 'net.thauvin.erik:pinboard-poster:0.9.3' // https://bitbucket.org/akapribot/owm-japis compile files('lib/owm-japis-2.5.0.5.jar') compileOnly semverJar - testCompile 'org.testng:testng:6.12' - testCompile 'org.assertj:assertj-core:3.8.0' + testCompile 'org.testng:testng:6.13.1' + testCompile 'org.assertj:assertj-core:3.9.0' } test { diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 03e8e89..11f2a28 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -57,22 +57,22 @@ val p = project { compile("pircbot:pircbot:1.5.0") //compileOnly("pircbot:pircbot::sources:1.5.0") - compile("org.apache.logging.log4j:log4j-api:2.9.1", - "org.apache.logging.log4j:log4j-core:2.9.1", - "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.9.1") + compile("org.apache.logging.log4j:log4j-api:2.10.0", + "org.apache.logging.log4j:log4j-core:2.10.0", + "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.10.0") compile("commons-cli:commons-cli:1.4", "commons-net:commons-net:3.6") - compile("com.squareup.okhttp3:okhttp:3.9.0") + compile("com.squareup.okhttp3:okhttp:3.9.1") - compile("com.rometools:rome:1.8.0") + compile("com.rometools:rome:1.9.0") compile("org.json:json:20171018") compile("org.ostermiller:utils:1.07.00") - compile("org.jsoup:jsoup:1.10.3") + compile("org.jsoup:jsoup:1.11.2") compile("net.objecthunter:exp4j:0.4.8") compile("org.twitter4j:twitter4j-core:4.0.6") - compile("net.thauvin.erik:pinboard-poster:0.9.1") + compile("net.thauvin.erik:pinboard-poster:0.9.3") // https://bitbucket.org/akapribot/owm-japis/ compile(file("lib/owm-japis-2.5.0.5.jar")) @@ -82,8 +82,8 @@ val p = project { } dependenciesTest { - compile("org.testng:testng:6.12") - compile("org.assertj:assertj-core:3.8.0") + compile("org.testng:testng:6.13.1") + compile("org.assertj:assertj-core:3.9.0") } apt { diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..578a20b --- /dev/null +++ b/pom.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <version>0.7.2-beta+030</version> + <name>mobibot</name> + <description></description> + <dependencies> + <dependency> + <groupId>pircbot</groupId> + <artifactId>pircbot</artifactId> + <version>1.5.0</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>2.10.0</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>2.10.0</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + <version>2.10.0</version> + </dependency> + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + <version>1.4</version> + </dependency> + <dependency> + <groupId>commons-net</groupId> + <artifactId>commons-net</artifactId> + <version>3.6</version> + </dependency> + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + <version>3.9.1</version> + </dependency> + <dependency> + <groupId>com.rometools</groupId> + <artifactId>rome</artifactId> + <version>1.9.0</version> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20171018</version> + </dependency> + <dependency> + <groupId>org.ostermiller</groupId> + <artifactId>utils</artifactId> + <version>1.07.00</version> + </dependency> + <dependency> + <groupId>org.jsoup</groupId> + <artifactId>jsoup</artifactId> + <version>1.11.2</version> + </dependency> + <dependency> + <groupId>net.objecthunter</groupId> + <artifactId>exp4j</artifactId> + <version>0.4.8</version> + </dependency> + <dependency> + <groupId>org.twitter4j</groupId> + <artifactId>twitter4j-core</artifactId> + <version>4.0.6</version> + </dependency> + <dependency> + <groupId>net.thauvin.erik</groupId> + <artifactId>pinboard-poster</artifactId> + <version>0.9.3</version> + </dependency> + <dependency> + <systemPath>K:\java\mobibot\.\lib\owm-japis-2.5.0.5.jar</systemPath> + </dependency> + <dependency> + <groupId>org.testng</groupId> + <artifactId>testng</artifactId> + <version>6.13.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>3.9.0</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> From 8941f9f63659318b8803438ebaf508b08653ff0d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Jan 2018 17:40:13 -0800 Subject: [PATCH 133/842] Moved to Gemnasium since VersionEye is shutting down. --- kobalt/src/Build.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 11f2a28..8d42692 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -9,14 +9,14 @@ import com.beust.kobalt.plugin.java.javadoc import com.beust.kobalt.plugin.packaging.assemble import com.beust.kobalt.plugin.packaging.install import com.beust.kobalt.plugin.publish.autoGitTag -import net.thauvin.erik.kobalt.plugin.versioneye.versionEye +import net.thauvin.erik.kobalt.plugin.pom2xml.pom2xml import java.io.File import java.io.FileInputStream import java.util.* val bs = buildScript { repos(localMaven()) - plugins("net.thauvin.erik:kobalt-versioneye:") + plugins("net.thauvin.erik:kobalt-pom2xml:") } val mainClassName = "net.thauvin.erik.mobibot.Mobibot" @@ -131,9 +131,8 @@ val p = project { links("http://www.jibble.org/javadocs/pircbot/", "http://docs.oracle.com/javase/8/docs/api/") } - versionEye { - org = "thauvin" - team = "Owners" + pom2xml { + } } From a9d5fe74f34c7bd27ad04bfc88641dd11d84bd95 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Jan 2018 17:40:40 -0800 Subject: [PATCH 134/842] Kobalt 1.0.100update. --- .idea/modules/mobibot.iml | 173 ++++++++++++----------- kobalt/wrapper/kobalt-wrapper.properties | 2 +- mobibot.ipr | 113 ++++++++------- 3 files changed, 146 insertions(+), 142 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 6f2555c..3ee18c8 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.2-beta+022" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.2-beta+030" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> - <output url="file://$MODULE_DIR$/../../build/classes/main" /> - <output-test url="file://$MODULE_DIR$/../../build/classes/test" /> + <output url="file://$MODULE_DIR$/../../out/production/classes" /> + <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> <exclude-output /> <content url="file://$MODULE_DIR$/../.."> <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> @@ -14,72 +14,33 @@ <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> <excludeFolder url="file://$MODULE_DIR$/../../build" /> <excludeFolder url="file://$MODULE_DIR$/../../kobaltBuild" /> + <excludeFolder url="file://$MODULE_DIR$/../../out" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="module-library" scope="TEST"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.yaml:snakeyaml:1.17" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.64" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.8.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.11" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20170516" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.7.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="module-library" scope="RUNTIME"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20170516" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.7.3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.9.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.9.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20171018" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.11.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.9.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="module-library" scope="PROVIDED"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> @@ -89,26 +50,66 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.7.24" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.7.3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.10.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20170516" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.7.3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.8.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.9.1" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.9.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20171018" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.11.2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.9.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="module-library" scope="RUNTIME"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.9.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.9.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20171018" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.11.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.13.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.9.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.9.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="module-library" scope="TEST"> + <library name="Gradle: owm-japis-2.5.0.5"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> </component> </module> \ No newline at end of file diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index 21b1f73..5b46f90 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.90 \ No newline at end of file +kobalt.version=1.0.100 \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index e43c9a5..496499a 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -162,7 +162,7 @@ <option name="autoDownloadKobalt" value="true" /> <option name="downloadSources" value="false" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.90" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.100" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> @@ -179,6 +179,9 @@ </MavenGeneralSettings> </option> </component> + <component name="ProjectCodeStyleConfiguration"> + <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> + </component> <component name="ProjectCodeStyleSettingsManager"> <option name="PER_PROJECT_SETTINGS"> <value /> @@ -326,13 +329,13 @@ <mapping directory="$PROJECT_DIR$" vcs="Git" /> </component> <component name="libraryTable"> - <library name="Gradle: com.beust:jcommander:1.64"> + <library name="Gradle: com.beust:jcommander:1.72"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.64/456a985ac9b12d34820e4d5de063b2c2fc43ed5a/jcommander-1.64.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.64/1b16adc28aca77f62a61f31380f84961c9c3570d/jcommander-1.64-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4"> @@ -344,31 +347,31 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/599674a480f9940ff5e25b375c6f59d14d8a4bfa/compiler-0.9.4-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome-utils:1.7.3"> + <library name="Gradle: com.rometools:rome-utils:1.9.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.3/53e57b41a8f3440ca808bd5f246cb70ba6dcc67a/rome-utils-1.7.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.9.0/9f2291327c641cd78d3111d50e9b03022f6c1090/rome-utils-1.9.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.7.3/451e50a658f7ca5d59faa68715428093397751f5/rome-utils-1.7.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.9.0/905649e08280147d3655b51454c76e99ed4f54b1/rome-utils-1.9.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.7.3"> + <library name="Gradle: com.rometools:rome:1.9.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.3/18d04e4c24025077bacc5e4f2ca0fd48587f56e8/rome-1.7.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.9.0/4546a79a6fb470c5235806c877d9438140f3bb61/rome-1.9.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.7.3/d30dbef2c5e08f388157dc4cf7d9c55faa37e929/rome-1.7.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.9.0/b54fdebe24f1df782e714c20e238619a8bdbee71/rome-1.9.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:3.8.0"> + <library name="Gradle: com.squareup.okhttp3:okhttp:3.9.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.8.0/5a11f020cce2d11eb71ba916700600e18c4547e7/okhttp-3.8.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.9.1/84b4b7d1c4a238e7899972b7446c250691e65f1f/okhttp-3.9.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.8.0/db21293949e200f08d5325e8a8eefdcc9134b752/okhttp-3.8.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.9.1/7539531ecf9b6e082fcab195f0835d4f69ead95e/okhttp-3.9.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okio:okio:1.13.0"> @@ -407,58 +410,58 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/8d86f148ff1f0d5b624eae9bb0882198ab5cd07/exp4j-0.4.8-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:pinboard-poster:0.9.1"> + <library name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.1/pinboard-poster-0.9.1.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.3/pinboard-poster-0.9.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.1/pinboard-poster-0.9.1-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.3/pinboard-poster-0.9.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:semver:1.0.0"> + <library name="Gradle: net.thauvin.erik:semver:1.0.1"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.0/semver-1.0.0.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.0/semver-1.0.0-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-api:2.8.2"> + <library name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.8.2/e590eeb783348ce8ddef205b82127f9084d82bf3/log4j-api-2.8.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.10.0/fec5797a55b786184a537abd39c3fa1449d752d6/log4j-api-2.10.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.8.2/459702eacc545a5478627fe879cec6e004c00f3a/log4j-api-2.8.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.10.0/fd42afa6acbfb3801accec744106ee28b9342567/log4j-api-2.10.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-core:2.8.2"> + <library name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.8.2/979fc0cf8460302e4ffbfe38c1b66a99450b0bb7/log4j-core-2.8.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.10.0/c90b597163cd28ab6d9687edd53db601b6ea75a1/log4j-core-2.10.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.8.2/4693b08b8d9abe152417b9564bb7a68d82630180/log4j-core-2.8.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.10.0/a2041b29bc3e761fba750c0df933cd48f76f5b3a/log4j-core-2.10.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.8.2"> + <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.8.2/36bc2a99b86be26ccdc51fe288458dc712d280c1/log4j-slf4j-impl-2.8.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.10.0/8e4e0a30736175e31c7f714d95032c1734cfbdea/log4j-slf4j-impl-2.10.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.8.2/c5f494286f220ba330c6eabccf10740458ef1fab/log4j-slf4j-impl-2.8.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.10.0/51e9adc49fcb32d961c66170c9b4b092ee809d9c/log4j-slf4j-impl-2.10.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.assertj:assertj-core:3.8.0"> + <library name="Gradle: org.assertj:assertj-core:3.9.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.8.0/b209d90ff2e279bee3e02547ee7b11349c52d0e3/assertj-core-3.8.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.9.0/de324a44efe19b9222d2218f7ac3102d728497d8/assertj-core-3.9.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.8.0/85dab3e124fb391b10196df9bfdb43640deabb09/assertj-core-3.8.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.9.0/7eefa834515f5adb0a14713b9c3cf9aa565530d8/assertj-core-3.9.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> @@ -470,13 +473,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.2-3"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.2-3/552a40eb47669b78f0f194d526cb21b3aa1f8319/kotlin-stdlib-1.1.2-3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.51/e34fe80c9714240525f665113dd3749415515655/kotlin-stdlib-1.1.51.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.2-3/7502cd343c8990a77aabb7cf41bd7e9f186c06cc/kotlin-stdlib-1.1.2-3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.51/b44aed5fe16fe27bb75488a2274d435e875fdb44/kotlin-stdlib-1.1.51-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jetbrains:annotations:13.0"> @@ -488,22 +491,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20170516"> + <library name="Gradle: org.json:json:20171018"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20170516/949abe1460757b8dc9902c562f83e49675444572/json-20170516.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20171018/36ced462c97f0845b4a7b266a25cebe95d18baa3/json-20171018.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20170516/6d919271d0ac012fb5706abd62fd3aff0f78ed95/json-20170516-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20171018/b4a63d74bc32f012342173c1a1da6fbe90065d69/json-20171018-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jsoup:jsoup:1.10.2"> + <library name="Gradle: org.jsoup:jsoup:1.11.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.10.2/33ee82e324f4b1e40167f3dc5e01234a1c5cab61/jsoup-1.10.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.2/e3eeb8a0b4ce1db246059a41e353cd7413dad226/jsoup-1.11.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.10.2/705a50ce237c266feaa279797712cb6bb6c1f34d/jsoup-1.10.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.2/f0f1f0d1ceb2cd515c98649d9ad6b8b9814899b2/jsoup-1.11.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.ostermiller:utils:1.07.00"> @@ -515,22 +518,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.slf4j:slf4j-api:1.7.24"> + <library name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.24/3f6b4bd4f8dbe8d4bea06d107a3826469b85c3e9/slf4j-api-1.7.24.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.8.0-alpha2/34fa1d87256bbdb376ae7f6fa7e479610cd07dce/slf4j-api-1.8.0-alpha2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.24/9f1cecd7c757ad5dde29453d43315228bb0a7c7e/slf4j-api-1.7.24-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.8.0-alpha2/5818a2c13c12c71c2861315d90888104a875f438/slf4j-api-1.8.0-alpha2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.testng:testng:6.11"> + <library name="Gradle: org.testng:testng:6.13.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.11/1fdd5e22f50b14f6d846163456e8c9a7657626fb/testng-6.11.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.13.1/2495393a0b4b7d7a4b49ea1f8516376f70f482c/testng-6.13.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.11/77da7afb24d57b640d9803593a6222c7de12dd4e/testng-6.11-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.13.1/a2b77dfee647263d2c1010215441bfa3e28c4b9c/testng-6.13.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.twitter4j:twitter4j-core:4.0.6"> @@ -542,15 +545,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/5a3a910793c3510a3e25cea3c51672bb88124527/twitter4j-core-4.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.yaml:snakeyaml:1.17"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.17/7a27ea250c5130b2922b86dea63cbb1cc10a660c/snakeyaml-1.17.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.17/63577e87886c76228db9f8a2c50ea43cde5072eb/snakeyaml-1.17-sources.jar!/" /> - </SOURCES> - </library> <library name="Gradle: pircbot:pircbot:1.5.0"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> @@ -560,6 +554,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: pircbot:pircbot:1.5.0:sources"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> + </SOURCES> + </library> </component> <component name="masterDetails"> <states> From c0893eec5d7aa0b24030208905f8821ec7acc122 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Jan 2018 17:43:38 -0800 Subject: [PATCH 135/842] Added Gemnasium badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27a184e..21916fd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Dependency Status](https://beta.gemnasium.com/badges/github.com/ethauvin/mobibot.svg)](https://beta.gemnasium.com/projects/github.com/ethauvin/mobibot) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: From 3e4700edfc7edf985bef3712c8f532852e2f8cac Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Jan 2018 20:49:10 -0800 Subject: [PATCH 136/842] Added snyk badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 21916fd..3c69b82 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Dependency Status](https://beta.gemnasium.com/badges/github.com/ethauvin/mobibot.svg)](https://beta.gemnasium.com/projects/github.com/ethauvin/mobibot) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Dependency Status](https://beta.gemnasium.com/badges/github.com/ethauvin/mobibot.svg)](https://beta.gemnasium.com/projects/github.com/ethauvin/mobibot) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: From 545b6820353ff3cdea545e9451f170184b16ca6d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 18 Jun 2018 19:27:32 -0700 Subject: [PATCH 137/842] Fixed badges. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c69b82..7e3e3d6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Dependency Status](https://beta.gemnasium.com/badges/github.com/ethauvin/mobibot.svg)](https://beta.gemnasium.com/projects/github.com/ethauvin/mobibot) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: From 47b5ebc462ab0ccef5c06fc50bd9b6619e1da450 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Jun 2018 13:43:40 -0700 Subject: [PATCH 138/842] Updated dependencies. --- build.gradle | 27 +++++++------- gradle/wrapper/gradle-wrapper.jar | Bin 54727 -> 54417 bytes gradle/wrapper/gradle-wrapper.properties | 4 +-- kobalt/src/Build.kt | 20 +++++------ kobalt/wrapper/kobalt-wrapper.properties | 2 +- settings.gradle | 18 +--------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 34 +----------------- 7 files changed, 27 insertions(+), 78 deletions(-) diff --git a/build.gradle b/build.gradle index c19e5f0..2fbd869 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.4" - id "com.github.ben-manes.versions" version "0.17.0" + id "com.github.ben-manes.versions" version "0.20.0" + id "org.owasp.dependencycheck" version "3.2.1" } apply plugin: 'java' @@ -52,32 +53,32 @@ dependencies { compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile 'org.apache.logging.log4j:log4j-api:2.10.0' - compile 'org.apache.logging.log4j:log4j-core:2.10.0' - compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.10.0' + compile 'org.apache.logging.log4j:log4j-api:2.11.0' + compile 'org.apache.logging.log4j:log4j-core:2.11.0' + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.0' compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.9.1' + compile 'com.squareup.okhttp3:okhttp:3.10.0' - compile 'com.rometools:rome:1.9.0' + compile 'com.rometools:rome:1.10.0' - compile 'org.json:json:20171018' + compile 'org.json:json:20180130' compile 'org.ostermiller:utils:1.07.00' - compile 'org.jsoup:jsoup:1.11.2' + compile 'org.jsoup:jsoup:1.11.3' compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.6' - compile 'net.thauvin.erik:pinboard-poster:0.9.3' + compile 'net.thauvin.erik:pinboard-poster:1.0.0' // https://bitbucket.org/akapribot/owm-japis compile files('lib/owm-japis-2.5.0.5.jar') compileOnly semverJar - testCompile 'org.testng:testng:6.13.1' - testCompile 'org.assertj:assertj-core:3.9.0' + testCompile 'org.testng:testng:6.14.3' + testCompile 'org.assertj:assertj-core:3.10.0' } test { @@ -115,10 +116,6 @@ run { args '--v' } -task wrapper(type: Wrapper) { - gradleVersion = gradle.gradleVersion -} - task copyToDeploy(type: Copy) { from('properties') from jar diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 27768f1bbac3ce2d055b20d521f12da78d331e8e..758de960ec7947253b058ff79c88ce51f3abe08a 100644 GIT binary patch delta 23052 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 X5`cG1{zvDnZ9v>48#<`{uiAeB=zVq4 delta 23328 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62e1e30..2d80b69 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 8d42692..8a1af6d 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -57,22 +57,22 @@ val p = project { compile("pircbot:pircbot:1.5.0") //compileOnly("pircbot:pircbot::sources:1.5.0") - compile("org.apache.logging.log4j:log4j-api:2.10.0", - "org.apache.logging.log4j:log4j-core:2.10.0", - "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.10.0") + compile("org.apache.logging.log4j:log4j-api:2.11.0", + "org.apache.logging.log4j:log4j-core:2.11.0", + "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.11.0") compile("commons-cli:commons-cli:1.4", "commons-net:commons-net:3.6") - compile("com.squareup.okhttp3:okhttp:3.9.1") + compile("com.squareup.okhttp3:okhttp:3.10.0") - compile("com.rometools:rome:1.9.0") + compile("com.rometools:rome:1.10.0") - compile("org.json:json:20171018") + compile("org.json:json:20180130") compile("org.ostermiller:utils:1.07.00") - compile("org.jsoup:jsoup:1.11.2") + compile("org.jsoup:jsoup:1.11.3") compile("net.objecthunter:exp4j:0.4.8") compile("org.twitter4j:twitter4j-core:4.0.6") - compile("net.thauvin.erik:pinboard-poster:0.9.3") + compile("net.thauvin.erik:pinboard-poster:1.0.0") // https://bitbucket.org/akapribot/owm-japis/ compile(file("lib/owm-japis-2.5.0.5.jar")) @@ -82,8 +82,8 @@ val p = project { } dependenciesTest { - compile("org.testng:testng:6.13.1") - compile("org.assertj:assertj-core:3.9.0") + compile("org.testng:testng:6.14.3") + compile("org.assertj:assertj-core:3.10.0") } apt { diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index 5b46f90..d630dff 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.100 \ No newline at end of file +kobalt.version=1.0.114 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 36886c7..21faf72 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,19 +1,3 @@ -/* - * This settings file was auto generated by the Gradle buildInit task - * by 'erik' at '4/19/14 12:47 PM' with Gradle 1.11 - * - * The settings file is used to specify which projects to include in your build. - * In a single project build this file can be empty or even removed. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user guide at http://gradle.org/docs/1.11/userguide/multi_project_builds.html - */ - -/* -// To declare projects as part of a multi-project build use the 'include' method -include 'shared' -include 'api' -include 'services:webservice' -*/ +enableFeaturePreview('STABLE_PUBLISHING') rootProject.name = 'mobibot' diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 5f06ccc..58ab1e0 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -1,35 +1,3 @@ -/* - * ReleaseInfo.java - * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* * This file is automatically generated. * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -50,7 +18,7 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1516151817482L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1529959364831L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 2; From ec753fdbd62292544998f9e38e11f671bf8f3003 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 7 Jul 2018 23:37:01 -0700 Subject: [PATCH 139/842] Added vcs. --- .gitignore | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 2de67f8..0d7f483 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,7 @@ -**/.idea/dictionaries -**/.idea/gradle.xml -**/.idea/libraries -**/.idea/tasks.xml -**/.idea/workspace.xml -*.iws -*.sublime-* +!.vscode/extensions.json +!.vscode/launch.json +!.vscode/settings.json +!.vscode/tasks.json .classpath .DS_Store .gradle @@ -12,6 +9,25 @@ .nb-gradle .project .settings +.vscode/* +*.code-workspace +*.iws +*.sublime-* +**/.idea/**/dataSources.ids +**/.idea/**/dataSources.local.xml +**/.idea/**/dataSources/ +**/.idea/**/dbnavigator.xml +**/.idea/**/dictionaries +**/.idea/**/dynamic.xml +**/.idea/**/gradle.xml +**/.idea/**/libraries +**/.idea/**/shelf +**/.idea/**/sqlDataSources.xml +**/.idea/**/tasks.xml +**/.idea/**/uiDesigner.xml +**/.idea/**/usage.statistics.xml +**/.idea/**/workspace.xml +**/*.class /bin /build /deploy From 7db6d76eea93b40f2cb2622dbc56ed25b8329c67 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Jul 2018 00:26:28 -0700 Subject: [PATCH 140/842] Now using SecureRandom. --- .../thauvin/erik/mobibot/modules/Dice.java | 10 +++--- .../thauvin/erik/mobibot/modules/Joke.java | 14 ++++---- .../thauvin/erik/mobibot/modules/Ping.java | 32 +++++++++---------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 2447c89..b68b579 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; -import java.util.Random; +import java.security.SecureRandom; /** * The Dice module. @@ -66,22 +66,22 @@ final public class Dice extends AbstractModule { */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - final Random r = new Random(); + final SecureRandom r = new SecureRandom(); int i = r.nextInt(6) + 1; int y = r.nextInt(6) + 1; final int playerTotal = i + y; bot.send(bot.getChannel(), - sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils - .bold(playerTotal)); + sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils + .bold(playerTotal)); i = r.nextInt(6) + 1; y = r.nextInt(6) + 1; final int total = i + y; bot.action( - "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); + "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); if (playerTotal < total) { bot.action("wins."); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 5d6cc5f..3588f29 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -39,6 +39,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; /** * The Joke module. @@ -55,7 +56,7 @@ final public class Joke extends AbstractModule { // The ICNDB URL. private static final String JOKE_URL = - "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; + "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; /** * Creates a new {@link Joke} instance. @@ -90,7 +91,8 @@ final public class Joke extends AbstractModule { final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + try (final BufferedReader reader = + new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { sb.append(line); @@ -99,10 +101,10 @@ final public class Joke extends AbstractModule { final JSONObject json = new JSONObject(sb.toString()); bot.send(bot.getChannel(), - Colors.CYAN - + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") - .replaceAll("\\\"", "\"") - + Colors.NORMAL); + Colors.CYAN + + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") + .replaceAll("\\\"", "\"") + + Colors.NORMAL); } } catch (Exception e) { bot.getLogger().warn("Unable to retrieve random joke.", e); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 7bb9cbe..697309b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -33,9 +33,9 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import java.security.SecureRandom; import java.util.Arrays; import java.util.List; -import java.util.Random; /** * The Ping module. @@ -52,19 +52,19 @@ public class Ping extends AbstractModule { // The ping responses. private static final List<String> PINGS = - Arrays.asList( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!"); + Arrays.asList( + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!"); /** * The default constructor. @@ -78,7 +78,7 @@ public class Ping extends AbstractModule { */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - final Random r = new Random(); + final SecureRandom r = new SecureRandom(); bot.action(PINGS.get(r.nextInt(PINGS.size()))); } @@ -91,4 +91,4 @@ public class Ping extends AbstractModule { bot.send(sender, "To ping the bot:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + PING_CMD)); } -} \ No newline at end of file +} From 8c3d850f54855771b962e30139888f065e316ee0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Jul 2018 00:29:01 -0700 Subject: [PATCH 141/842] Fixed whois test. --- .../net/thauvin/erik/mobibot/modules/LookupTest.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index 18c3972..4b14261 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -33,6 +33,8 @@ package net.thauvin.erik.mobibot.modules; import org.testng.annotations.Test; +import java.util.Arrays; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -46,13 +48,14 @@ public class LookupTest { @Test public void testLookup() throws Exception { final String result = Lookup.lookup("erik.thauvin.net"); - assertThat(result).as("lookup(erik.thauvin.net)").contains("104.31.77.12"); + assertThat(result).as("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12"); } @Test public void testWhois() throws Exception { final String[] result = Lookup.whois("17.178.96.59", Lookup.WHOIS_HOST); - assertThat(result).as("whois(17.178.96.59)") - .contains("Apple Inc. APPLE-WWNET (NET-17-0-0-0-1) 17.0.0.0 - 17.255.255.255"); + + assertThat(Arrays.asList(result).stream().anyMatch(m -> m.contains("Apple Inc."))) + .as("whois(17.178.96.59/Apple Inc.").isTrue(); } -} \ No newline at end of file +} From e953f1962e3bb60ce81bd1b0260537ee1dca1e8d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Jul 2018 00:29:53 -0700 Subject: [PATCH 142/842] Now using SecureRandom. --- src/main/java/net/thauvin/erik/mobibot/modules/War.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 93e7864..19c034a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; -import java.util.Random; +import java.security.SecureRandom; /** * The War module. @@ -51,7 +51,7 @@ final public class War extends AbstractModule { // The deck of card. private static final String[] WAR_DECK = - new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; + new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; // The suits for the deck of card. private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; @@ -73,7 +73,7 @@ final public class War extends AbstractModule { */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - final Random r = new Random(); + final SecureRandom r = new SecureRandom(); int i; int y; @@ -83,7 +83,7 @@ final public class War extends AbstractModule { y = r.nextInt(WAR_DECK.length); bot.send(bot.getChannel(), - sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); if (i != y) { From 2dc748be75d2317a4bfdd966c5a4587011cb9d03 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Jul 2018 00:36:07 -0700 Subject: [PATCH 143/842] Added UTF-8 encoding to intput / output streams. --- .../net/thauvin/erik/mobibot/EntriesMgr.java | 22 ++++++++++++------- .../erik/mobibot/modules/GoogleSearch.java | 20 ++++++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 8535ebf..6d494c7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -37,6 +37,7 @@ import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.SyndFeedOutput; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -86,12 +87,13 @@ final class EntriesMgr { * @throws FeedException If an error occurred while reading the feed. */ public static void loadBacklogs(final String file, final List<String> history) - throws IOException, FeedException { + throws IOException, FeedException { history.clear(); final SyndFeedInput input = new SyndFeedInput(); - try (final InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) { + try (final InputStreamReader reader = + new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); @@ -117,14 +119,14 @@ final class EntriesMgr { */ @SuppressWarnings("unchecked") public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) - throws IOException, FeedException { + throws IOException, FeedException { entries.clear(); final SyndFeedInput input = new SyndFeedInput(); final String today; - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(file)))) { + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); today = Utils.isoLocalDate(feed.getPublishedDate()); @@ -182,10 +184,11 @@ final class EntriesMgr { } if (Utils.isValidString(bot.getLogsDir()) && Utils.isValidString(bot.getWeblogUrl())) { - FileWriter fw = null; + Writer fw = null; try { - fw = new FileWriter(new File(bot.getLogsDir() + CURRENT_XML)); + fw = new OutputStreamWriter( + new FileOutputStream(bot.getLogsDir() + CURRENT_XML), StandardCharsets.UTF_8); SyndFeed rss = new SyndFeedImpl(); rss.setFeedType("rss_2.0"); @@ -249,7 +252,9 @@ final class EntriesMgr { output.output(rss, fw); fw.close(); - fw = new FileWriter(new File(bot.getLogsDir() + bot.getToday() + XML_EXT)); + fw = new OutputStreamWriter( + new FileOutputStream( + bot.getLogsDir() + bot.getToday() + XML_EXT), StandardCharsets.UTF_8); output.output(rss, fw); if (isDayBackup) { @@ -263,7 +268,8 @@ final class EntriesMgr { } fw.close(); - fw = new FileWriter(new File(bot.getLogsDir() + NAV_XML)); + fw = new OutputStreamWriter( + new FileOutputStream(bot.getLogsDir() + NAV_XML), StandardCharsets.UTF_8); rss = new SyndFeedImpl(); rss.setFeedType("rss_2.0"); rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index ef19b94..0097a28 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -31,6 +31,7 @@ */ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import org.json.JSONArray; @@ -41,6 +42,7 @@ import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; /** * The GoogleSearch module. @@ -108,22 +110,24 @@ final public class GoogleSearch extends AbstractModule { /** * Searches Google. */ + @SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD") private void run(final Mobibot bot, final String sender, final String query) { try { final String q = URLEncoder.encode(query, "UTF-8"); final URL url = - new URL("https://www.googleapis.com/customsearch/v1?key=" - + properties.get(GOOGLE_API_KEY_PROP) - + "&cx=" - + properties.get(GOOGLE_CSE_KEY_PROP) - + "&q=" - + q - + "&filter=1&num=5&alt=json"); + new URL("https://www.googleapis.com/customsearch/v1?key=" + + properties.get(GOOGLE_API_KEY_PROP) + + "&cx=" + + properties.get(GOOGLE_CSE_KEY_PROP) + + "&q=" + + q + + "&filter=1&num=5&alt=json"); final URLConnection conn = url.openConnection(); final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + try (final BufferedReader reader = + new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { sb.append(line); From f501e6c073da1e938bfdbceaf966839bef1a8b40 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Jul 2018 00:39:21 -0700 Subject: [PATCH 144/842] Added spotbugs settings & annotations. --- build.gradle | 74 +++++++------------ config/spotbugs/excludefilter.xml | 14 ++++ .../net/thauvin/erik/mobibot/Mobibot.java | 10 ++- .../thauvin/erik/mobibot/TwitterOAuth.java | 4 +- .../mobibot/modules/CurrencyConverter.java | 2 + 5 files changed, 54 insertions(+), 50 deletions(-) create mode 100644 config/spotbugs/excludefilter.xml diff --git a/build.gradle b/build.gradle index 2fbd869..ebf7053 100644 --- a/build.gradle +++ b/build.gradle @@ -1,48 +1,28 @@ plugins { - id "com.ewerk.gradle.plugins.annotation-processor" version "1.0.4" + id 'java' + id 'idea' + id 'application' id "com.github.ben-manes.versions" version "0.20.0" - id "org.owasp.dependencycheck" version "3.2.1" + id "org.owasp.dependencycheck" version "3.2.1" + id "net.thauvin.erik.gradle.semver" version "0.9.6-beta" + id "com.github.spotbugs" version "1.6.2" } -apply plugin: 'java' -apply plugin: 'idea' -apply plugin: 'application' + +import com.github.spotbugs.SpotBugsTask defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -final def semverJar = 'net.thauvin.erik:semver:1.0.1' - -def getVersion(isIncrement = false) { - final def propsFile = 'version.properties' - final def majorKey = 'version.major' - final def minorKey = 'version.minor' - final def patchKey = 'version.patch' - final def metaKey = 'version.buildmeta' - final def preKey = 'version.prerelease' - if (isIncrement) { - ant.propertyfile(file: propsFile) { - entry(key: patchKey, - type: 'int', - default: '-1', - operation: '+') - } - } - def p = new Properties() - file(propsFile).withInputStream { final stream -> p.load(stream) } - final def metadata = p.getProperty(metaKey, '') - final def prerelease = p.getProperty(preKey, '') - return (p.getProperty(majorKey, '1') + '.' + p.getProperty(minorKey, '0') + '.' + p.getProperty(patchKey, '0') + - (prerelease.length() > 0 ? '-' + prerelease : '') + (metadata.length() > 0 ? '+' + metadata : '')) -} - -version = getVersion() +final def semverProcessor = "net.thauvin.erik:semver:1.1.0-beta" mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' +compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated") + repositories { mavenLocal() mavenCentral() @@ -50,6 +30,9 @@ repositories { } dependencies { + annotationProcessor semverProcessor + compileOnly semverProcessor + compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' @@ -62,7 +45,7 @@ dependencies { compile 'commons-net:commons-net:3.6' compile 'com.squareup.okhttp3:okhttp:3.10.0' - compile 'com.rometools:rome:1.10.0' + compile 'com.rometools:rome:1.11.0' compile 'org.json:json:20180130' compile 'org.ostermiller:utils:1.07.00' @@ -75,24 +58,27 @@ dependencies { // https://bitbucket.org/akapribot/owm-japis compile files('lib/owm-japis-2.5.0.5.jar') - compileOnly semverJar - testCompile 'org.testng:testng:6.14.3' testCompile 'org.assertj:assertj-core:3.10.0' + + spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0' + compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.5' } test { useTestNG() } -annotationProcessor { - project.version = getVersion(isRelease) - library semverJar - processor 'net.thauvin.erik.semver.VersionProcessor' +tasks.withType(SpotBugsTask) { + reports { + xml.enabled = false + html.enabled = true + } + excludeFilter = file("$projectDir/config/spotbugs/excludeFilter.xml") } compileJava { - options.compilerArgs << '-proc:none' << '-Xlint:unchecked' << '-Xlint:deprecation' + options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' } javadoc { @@ -139,11 +125,7 @@ task deploy(dependsOn: ['build']) { mustRunAfter clean } - task release(dependsOn: ['wrapper', 'clean', 'deploy']) { - doLast { - group = 'Publishing' - description = 'Releases new version.' - isRelease = true - } -} \ No newline at end of file + group = 'Publishing' + description = 'Releases new version.' +} diff --git a/config/spotbugs/excludefilter.xml b/config/spotbugs/excludefilter.xml new file mode 100644 index 0000000..f7e9cce --- /dev/null +++ b/config/spotbugs/excludefilter.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FindBugsFilter + xmlns="https://github.com/spotbugs/filter/3.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd"> + <Match> + <Package name="net.thauvin.erik.mobibot.*"/> + <Or> + <Bug pattern="PATH_TRAVERSAL_IN"/> + <Bug pattern="PATH_TRAVERSAL_OUT"/> + </Or> + <Confidence value="2"/> + </Match> +</FindBugsFilter> diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index f26cb20..c1e6d01 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot; import com.rometools.rome.io.FeedException; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.modules.*; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; @@ -294,15 +295,16 @@ public class Mobibot extends PircBot { * * @param args The command line arguments. */ + @SuppressFBWarnings(value = {"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING"}) public static void main(final String[] args) { // Setup the command line options final Options options = new Options(); options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, - "print debug & logging data directly to the console"); + "print debug & logging data directly to the console"); options.addOption(Option.builder( - Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") - .longOpt(Commands.PROPS_ARG).build()); + Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") + .longOpt(Commands.PROPS_ARG).build()); options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); // Parse the command line @@ -418,6 +420,7 @@ public class Mobibot extends PircBot { /** * Connects to the server and joins the channel. */ + @SuppressFBWarnings(value = {"DM_EXIT", "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE"}) public final void connect() { try { connect(ircServer, ircPort); @@ -1287,6 +1290,7 @@ public class Mobibot extends PircBot { /** * {@inheritDoc} */ + @SuppressFBWarnings(value = "DM_EXIT", justification = "Yes, we want to bail out.") @Override protected final void onPrivateMessage(final String sender, final String login, diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 71e2e26..7796453 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -1,5 +1,6 @@ package net.thauvin.erik.mobibot; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import twitter4j.TwitterException; import twitter4j.TwitterFactory; import twitter4j.auth.AccessToken; @@ -27,8 +28,9 @@ import java.io.InputStreamReader; * @since 1.0 */ public final class TwitterOAuth { + @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING") public static void main(final String[] args) - throws Exception { + throws Exception { if (args.length == 2) { final twitter4j.Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(args[0], args[1]); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index b6a2f60..2cb3a5e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -31,6 +31,7 @@ */ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import org.jdom2.Document; @@ -114,6 +115,7 @@ final public class CurrencyConverter extends AbstractModule { /** * Converts the specified currencies. */ + @SuppressFBWarnings(value = "REDOS") private void run(final Mobibot bot, final String sender, final String query) { if (Utils.isValidString(sender)) { if (EXCHANGE_RATES.isEmpty()) { From 290bf5cf93d5c5453fc42616e08ab9e23b3eb099 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Jul 2018 01:09:09 -0700 Subject: [PATCH 145/842] Misc. updates (whitespace, editorconfig, IDEA, etc.) --- .editorconfig | 2 + .idea/modules/mobibot.iml | 114 ++++----------- build.gradle | 2 +- mobibot.ipr | 132 +++++++++++------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 16 +-- .../net/thauvin/erik/mobibot/Commands.java | 4 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 26 ++-- .../net/thauvin/erik/mobibot/EntryLink.java | 120 ++++++++-------- .../net/thauvin/erik/mobibot/Mobibot.java | 128 ++++++++--------- .../net/thauvin/erik/mobibot/Pinboard.java | 34 ++--- .../java/net/thauvin/erik/mobibot/Tell.java | 84 +++++------ .../thauvin/erik/mobibot/TellMessagesMgr.java | 4 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 12 +- .../java/net/thauvin/erik/mobibot/Utils.java | 6 +- .../mobibot/modules/CurrencyConverter.java | 28 ++-- .../thauvin/erik/mobibot/modules/Lookup.java | 12 +- .../thauvin/erik/mobibot/modules/Twitter.java | 14 +- .../erik/mobibot/modules/WorldTime.java | 12 +- .../net/thauvin/erik/mobibot/UtilsTest.java | 14 +- version.properties | 11 +- 20 files changed, 369 insertions(+), 406 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a6971e1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,2 @@ +[*] +insert_final_newline=true diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 3ee18c8..2d27c23 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -5,10 +5,8 @@ <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> <exclude-output /> <content url="file://$MODULE_DIR$/../.."> - <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/annotationProcessor/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> @@ -18,30 +16,33 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okhttp3:okhttp:3.9.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome:1.9.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.json:json:20171018" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jsoup:jsoup:1.11.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.rometools:rome-utils:1.9.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="module-library" scope="PROVIDED"> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.11.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.11.0" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.10.0" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.11.0" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20180130" level="project" /> + <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.11.3" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.1.0-beta" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.5" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.11.0" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.50" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.5" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.50" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="module-library"> <library name="Gradle: owm-japis-2.5.0.5"> <CLASSES> <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> @@ -50,66 +51,9 @@ <SOURCES /> </library> </orderEntry> - <orderEntry type="library" scope="RUNTIME" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okhttp3:okhttp:3.9.1" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome:1.9.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.json:json:20171018" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jsoup:jsoup:1.11.2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.rometools:rome-utils:1.9.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="module-library" scope="RUNTIME"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" scope="TEST" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okhttp3:okhttp:3.9.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome:1.9.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.json:json:20171018" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jsoup:jsoup:1.11.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.13.1" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.9.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.rometools:rome-utils:1.9.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.squareup.okio:okio:1.13.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.10.0" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="module-library" scope="TEST"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> + <orderEntry type="library" scope="TEST" name="Gradle: org.apache-extras.beanshell:bsh:2.0b6" level="project" /> </component> </module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index ebf7053..555b0f6 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ final def semverProcessor = "net.thauvin.erik:semver:1.1.0-beta" mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' -compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated") +compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") repositories { mavenLocal() diff --git a/mobibot.ipr b/mobibot.ipr index 496499a..690d90b 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -151,8 +151,6 @@ <option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" /> </inspection_tool> </profile> - <option name="useProjectProfile" value="false" /> - <option name="PROJECT_PROFILE" /> <option name="USE_PROJECT_PROFILE" value="false" /> <version value="1.0" /> </component> @@ -162,7 +160,7 @@ <option name="autoDownloadKobalt" value="true" /> <option name="downloadSources" value="false" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.100" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.114" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> @@ -224,7 +222,7 @@ <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8.x" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="10" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="PropertiesComponent"> @@ -338,49 +336,67 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4"> + <library name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.5"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/1d1fab03f31a75018fc30ec248859a5c89cd342f/compiler-0.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.5/4e2e5448fba7b4aa298d4eb9af25a9ba707bcb0e/spotbugs-annotations-3.1.5.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/599674a480f9940ff5e25b375c6f59d14d8a4bfa/compiler-0.9.4-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.5/da4048bfca28ece42ea0affc28c8362927aa0ee4/spotbugs-annotations-3.1.5-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome-utils:1.9.0"> + <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.5"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.9.0/9f2291327c641cd78d3111d50e9b03022f6c1090/rome-utils-1.9.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.5/2d13ba8f4e9e578ef3636c064dae4ed675c8de7d/compiler-0.9.5.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.9.0/905649e08280147d3655b51454c76e99ed4f54b1/rome-utils-1.9.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.5/45e36f16202869f7b3e335cb9497318380a3f59a/compiler-0.9.5-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.9.0"> + <library name="Gradle: com.google.code.findbugs:jsr305:3.0.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.9.0/4546a79a6fb470c5235806c877d9438140f3bb61/rome-1.9.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.9.0/b54fdebe24f1df782e714c20e238619a8bdbee71/rome-1.9.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/b19b5927c2c25b6c70f093767041e641ae0b1b35/jsr305-3.0.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:3.9.1"> + <library name="Gradle: com.rometools:rome-utils:1.11.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.9.1/84b4b7d1c4a238e7899972b7446c250691e65f1f/okhttp-3.9.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.0/a35a96278cb585b3875c25c45bd2b438058392a7/rome-utils-1.11.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.9.1/7539531ecf9b6e082fcab195f0835d4f69ead95e/okhttp-3.9.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.0/1e1d75cf40c8b75679814829fb37ed8210d4fffe/rome-utils-1.11.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okio:okio:1.13.0"> + <library name="Gradle: com.rometools:rome:1.11.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.13.0/a9283170b7305c8d92d25aff02a6ab7e45d06cbe/okio-1.13.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.0/245e0d8e5b3461b2842fa739b2693b0e7a4f0a46/rome-1.11.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.13.0/f4c91e12121af963e3ef76e81c82aa75ba6e8533/okio-1.13.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.0/77c6cb39cfd14bd0b02ccbc6bbb23a13e7300d48/rome-1.11.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: com.squareup.okhttp3:okhttp:3.10.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.10.0/7ef0f1d95bf4c0b3ba30bbae25e0e562b05cf75e/okhttp-3.10.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.10.0/e99b7b608968f16b07104b93e62cb90701174d0/okhttp-3.10.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: com.squareup.okio:okio:1.14.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.14.0/102d7be47241d781ef95f1581d414b0943053130/okio-1.14.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.14.0/e7c96b4fe5651490d8f3c022042940d743a3bdd9/okio-1.14.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: commons-cli:commons-cli:1.4"> @@ -410,58 +426,67 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/8d86f148ff1f0d5b624eae9bb0882198ab5cd07/exp4j-0.4.8-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:pinboard-poster:0.9.3"> + <library name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.3/pinboard-poster-0.9.3.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.0/pinboard-poster-1.0.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/0.9.3/pinboard-poster-0.9.3-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.0/pinboard-poster-1.0.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:semver:1.0.1"> + <library name="Gradle: net.thauvin.erik:semver:1.1.0-beta"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.1.0-beta/semver-1.1.0-beta.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.1.0-beta/semver-1.1.0-beta-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-api:2.10.0"> + <library name="Gradle: org.apache-extras.beanshell:bsh:2.0b6"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.10.0/fec5797a55b786184a537abd39c3fa1449d752d6/log4j-api-2.10.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache-extras.beanshell/bsh/2.0b6/fb418f9b33a0b951e9a2978b4b6ee93b2707e72f/bsh-2.0b6.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.10.0/fd42afa6acbfb3801accec744106ee28b9342567/log4j-api-2.10.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache-extras.beanshell/bsh/2.0b6/76a65f674e8f2df409934824c49ca338987c63ae/bsh-2.0b6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-core:2.10.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-api:2.11.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.10.0/c90b597163cd28ab6d9687edd53db601b6ea75a1/log4j-core-2.10.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.0/bede79a3f150711634a3047985517431bf6499f2/log4j-api-2.11.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.10.0/a2041b29bc3e761fba750c0df933cd48f76f5b3a/log4j-core-2.10.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.0/97f6fb29d69b48fe4bdb47cc21c834b4536cf752/log4j-api-2.11.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.10.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-core:2.11.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.10.0/8e4e0a30736175e31c7f714d95032c1734cfbdea/log4j-slf4j-impl-2.10.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.0/e6b751e02120c08702d98750f6a80bc25343b7f5/log4j-core-2.11.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.10.0/51e9adc49fcb32d961c66170c9b4b092ee809d9c/log4j-slf4j-impl-2.10.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.0/932392dc515dae4e750ac4a96c39ba69d5e81db3/log4j-core-2.11.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.assertj:assertj-core:3.9.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.9.0/de324a44efe19b9222d2218f7ac3102d728497d8/assertj-core-3.9.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.0/9ba207b78e470fe7765ebee14f1f0336c9cbcc18/log4j-slf4j-impl-2.11.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.9.0/7eefa834515f5adb0a14713b9c3cf9aa565530d8/assertj-core-3.9.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.0/eaf7016f6df9069dbc295a9c7a5d6424a1f237c2/log4j-slf4j-impl-2.11.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.assertj:assertj-core:3.10.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.10.0/4898fc7b9a2f7f8457bd380f917b08416df313f7/assertj-core-3.10.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.10.0/31def12d16eb0ce1478b77ca94c55e2b7e97b114/assertj-core-3.10.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> @@ -473,13 +498,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.1.51"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.50" type="kotlin.common"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.51/e34fe80c9714240525f665113dd3749415515655/kotlin-stdlib-1.1.51.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.50/6b19a2fcc29d34878b3aab33fd5fcf70458a73df/kotlin-stdlib-common-1.2.50.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.1.51/b44aed5fe16fe27bb75488a2274d435e875fdb44/kotlin-stdlib-1.1.51-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.50/d3189b4b8c72c7f3a327e3c21dca8dbcba267359/kotlin-stdlib-common-1.2.50-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.50"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.50/66d47b004c5b8a1d2d1df9e463187390ed741316/kotlin-stdlib-1.2.50.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.50/9ec9cf82e5e18d990a4b4fb3b5763cd3637b6d63/kotlin-stdlib-1.2.50-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jetbrains:annotations:13.0"> @@ -491,22 +525,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20171018"> + <library name="Gradle: org.json:json:20180130"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20171018/36ced462c97f0845b4a7b266a25cebe95d18baa3/json-20171018.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180130/26ba2ec0e791a32ea5dfbedfcebf36447ee5b12c/json-20180130.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20171018/b4a63d74bc32f012342173c1a1da6fbe90065d69/json-20171018-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180130/66ab57acbb9086d16201c2eafa2145d2b914bb26/json-20180130-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jsoup:jsoup:1.11.2"> + <library name="Gradle: org.jsoup:jsoup:1.11.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.2/e3eeb8a0b4ce1db246059a41e353cd7413dad226/jsoup-1.11.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.3/36da09a8f68484523fa2aaa100399d612b247d67/jsoup-1.11.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.2/f0f1f0d1ceb2cd515c98649d9ad6b8b9814899b2/jsoup-1.11.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.3/871302b15d8cee9bfb393c4f1d0386b17646d8d1/jsoup-1.11.3-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.ostermiller:utils:1.07.00"> @@ -527,13 +561,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.8.0-alpha2/5818a2c13c12c71c2861315d90888104a875f438/slf4j-api-1.8.0-alpha2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.testng:testng:6.13.1"> + <library name="Gradle: org.testng:testng:6.14.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.13.1/2495393a0b4b7d7a4b49ea1f8516376f70f482c/testng-6.13.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.14.3/d24515dc253e77e54b73df97e1fb2eb7faf34fdd/testng-6.14.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.13.1/a2b77dfee647263d2c1010215441bfa3e28c4b9c/testng-6.13.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.14.3/bfd2538501a129a142fe8366169c48b91609dc0b/testng-6.14.3-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.twitter4j:twitter4j-core:4.0.6"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 58ab1e0..bdd3309 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -4,28 +4,26 @@ */ package net.thauvin.erik.mobibot; -import java.time.*; +import java.util.Date; /** * Provides semantic version information. * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version - * Annotation Processor</a> + * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> */ public final class ReleaseInfo { public final static String PRERELEASE_PREFIX = "-"; public final static String BUILDMETA_PREFIX = "+"; - public final static String PROJECT = "mobibot"; - public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1529959364831L), ZoneId.systemDefault()); + public final static String PROJECT = ""; + public final static Date BUILDDATE = new Date(1531467929376L); public final static int MAJOR = 0; public final static int MINOR = 7; - public final static int PATCH = 2; + public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "030"; + public final static String BUILDMETA = "1"; - /** + /** * The full version string. * <p> * Formatted as: diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index e9227fb..c5eef7b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -160,7 +160,7 @@ final public class Commands { * @throws UnsupportedOperationException If the constructor is called. */ private Commands() - throws UnsupportedOperationException { + throws UnsupportedOperationException { throw new UnsupportedOperationException("Illegal constructor call."); } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 6d494c7..839d0c1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -74,7 +74,7 @@ final class EntriesMgr { * @throws UnsupportedOperationException If the constructor is called. */ private EntriesMgr() - throws UnsupportedOperationException { + throws UnsupportedOperationException { throw new UnsupportedOperationException("Illegal constructor call."); } @@ -141,13 +141,13 @@ final class EntriesMgr { for (int i = items.size() - 1; i >= 0; i--) { item = (SyndEntryImpl) items.get(i); author = item.getAuthor() - .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); + .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); entry = new EntryLink(item.getLink(), - item.getTitle(), - author, - channel, - item.getPublishedDate(), - item.getCategories()); + item.getTitle(), + author, + channel, + item.getPublishedDate(), + item.getCategories()); description = item.getDescription(); comments = description.getValue().split("<br/>"); @@ -209,8 +209,8 @@ final class EntriesMgr { entry = entries.get(i); buff = new StringBuffer( - "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' - + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); + "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' + + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); if (entry.getCommentsCount() > 0) { buff.append(" <br/><br/>"); @@ -236,7 +236,7 @@ final class EntriesMgr { item.setTitle(entry.getTitle()); item.setPublishedDate(entry.getDate()); item.setAuthor( - bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); + bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); item.setCategories(entry.getTags()); items.add(item); @@ -304,7 +304,7 @@ final class EntriesMgr { bot.getLogger().warn("Unable to generate the backlogs feed. No property configured."); } } - } catch (Exception e) { + } catch (FeedException | IOException e) { bot.getLogger().warn("Unable to generate the entries feed.", e); } finally { try { @@ -317,7 +317,7 @@ final class EntriesMgr { } } else { bot.getLogger() - .warn("Unable to generate the entries feed. At least one of the required property is missing."); + .warn("Unable to generate the entries feed. At least one of the required property is missing."); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 81d1fd3..ab2ecc9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -120,7 +120,7 @@ public class EntryLink implements Serializable { this.title = title; this.nick = nick; this.channel = channel; - this.date = date; + this.date = new Date(date.getTime()); setTags(tags); } @@ -202,23 +202,7 @@ public class EntryLink implements Serializable { * @return The date. */ public final Date getDate() { - return date; - } - - /** - * Returns the tags formatted for pinboard.in - * - * @return The tags as a comma-delimited string. - */ - public final String getPinboardTags() { - final StringBuilder tags = new StringBuilder(nick); - - for (final Object tag : this.tags) { - tags.append(','); - tags.append(((SyndCategoryImpl) tag).getName()); - } - - return tags.toString(); + return new Date(date.getTime()); } /** @@ -276,6 +260,22 @@ public class EntryLink implements Serializable { this.nick = nick; } + /** + * Returns the tags formatted for pinboard.in + * + * @return The tags as a comma-delimited string. + */ + public final String getPinboardTags() { + final StringBuilder tags = new StringBuilder(nick); + + for (final Object tag : this.tags) { + tags.append(','); + tags.append(((SyndCategoryImpl) tag).getName()); + } + + return tags.toString(); + } + /** * Returns the tags. * @@ -288,10 +288,44 @@ public class EntryLink implements Serializable { /** * Sets the tags. * - * @param tags The tags. + * @param tags The space-delimited tags. */ - private void setTags(final List<SyndCategory> tags) { - this.tags.addAll(tags); + public final void setTags(final String tags) { + if (tags != null) { + final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); + + SyndCategoryImpl tag; + String part; + char mod; + + for (final String rawPart : parts) { + part = rawPart.trim(); + + if (part.length() >= 2) { + tag = new SyndCategoryImpl(); + tag.setName(part.substring(1).toLowerCase()); + + mod = part.charAt(0); + + if (mod == '-') { + // Don't remove the channel tag, if any. + if (!tag.getName().equals(channel.substring(1))) { + this.tags.remove(tag); + } + } else if (mod == '+') { + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } else { + tag.setName(part.trim().toLowerCase()); + + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } + } + } + } } /** @@ -346,44 +380,10 @@ public class EntryLink implements Serializable { /** * Sets the tags. * - * @param tags The space-delimited tags. + * @param tags The tags. */ - public final void setTags(final String tags) { - if (tags != null) { - final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); - - SyndCategoryImpl tag; - String part; - char mod; - - for (final String rawPart : parts) { - part = rawPart.trim(); - - if (part.length() >= 2) { - tag = new SyndCategoryImpl(); - tag.setName(part.substring(1).toLowerCase()); - - mod = part.charAt(0); - - if (mod == '-') { - // Don't remove the channel tag, if any. - if (!tag.getName().equals(channel.substring(1))) { - this.tags.remove(tag); - } - } else if (mod == '+') { - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } else { - tag.setName(part.trim().toLowerCase()); - - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } - } - } - } + private void setTags(final List<SyndCategory> tags) { + this.tags.addAll(tags); } /** @@ -394,7 +394,7 @@ public class EntryLink implements Serializable { public final String toString() { return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date - + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' - + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; + + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' + + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c1e6d01..1ba630d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -75,8 +75,8 @@ public class Mobibot extends PircBot { // The info strings. private static final String[] INFO_STRS = { - ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", - "https://www.mobitopia.org/mobibot/" + ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", + "https://www.mobitopia.org/mobibot/" }; // The link match string. @@ -105,34 +105,31 @@ public class Mobibot extends PircBot { // The version strings. private static final String[] VERSION_STRS = { - "Version: " - + ReleaseInfo.VERSION - + " (" - + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', - "Platform: " - + System.getProperty("os.name") - + " (" - + System.getProperty("os.version") - + ", " - + System.getProperty("os.arch") - + ", " - + System.getProperty("user.country") + ')', - "Runtime: " - + System.getProperty("java.runtime.name") - + " (build " - + System.getProperty("java.runtime.version") - + ')', - "VM: " - + System.getProperty("java.vm.name") - + " (build " - + System.getProperty("java.vm.version") - + ", " - + System.getProperty("java.vm.info") - + ')' + "Version: " + + ReleaseInfo.VERSION + + " (" + + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + "Platform: " + + System.getProperty("os.name") + + " (" + + System.getProperty("os.version") + + ", " + + System.getProperty("os.arch") + + ", " + + System.getProperty("user.country") + ')', + "Runtime: " + + System.getProperty("java.runtime.name") + + " (build " + + System.getProperty("java.runtime.version") + + ')', + "VM: " + + System.getProperty("java.vm.name") + + " (build " + + System.getProperty("java.vm.version") + + ", " + + System.getProperty("java.vm.info") + + ')' }; - - // The tell object. - private static Tell tell; // The commands list. private final List<String> commandsList = new ArrayList<>(); // The entries array. @@ -145,43 +142,32 @@ public class Mobibot extends PircBot { private final String ircChannel; // The IRC port. private final int ircPort; - // The IRC server. private final String ircServer; - // The logger. private final Logger logger = LogManager.getLogger(Mobibot.class); - // The logger default level. private final Level loggerLevel; - // The log directory. private final String logsDir; - // The recap array. private final List<String> recap = new ArrayList<>(0); - // The backlogs URL. private String backLogsUrl = ""; - // The default tags/categories. private String defaultTags = ""; - // The feed URL. private String feedURL = ""; - // The ident message. private String identMsg = ""; - // The ident nick. private String identNick = ""; - // The NickServ ident password. private String identPwd = ""; - // The pinboard posts handler. private Pinboard pinboard = null; - + // The tell object. + private Tell tell; // Today's date. private String today = Utils.today(); @@ -245,7 +231,7 @@ public class Mobibot extends PircBot { setVersion(p.getProperty("weblog", "")); setMessageDelay(MESSAGE_DELAY); setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), - p.getProperty("ident-msg", "")); + p.getProperty("ident-msg", "")); // Set the URLs setWeblogUrl(getVersion()); @@ -271,11 +257,11 @@ public class Mobibot extends PircBot { // Load the modules properties MODULES.stream().filter(AbstractModule::hasProperties).forEach( - module -> { - for (final String s : module.getPropertyKeys()) { - module.setProperty(s, p.getProperty(s, "")); - } - }); + module -> { + for (final String s : module.getPropertyKeys()) { + module.setProperty(s, p.getProperty(s, "")); + } + }); // Get the tell command settings tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); @@ -330,7 +316,7 @@ public class Mobibot extends PircBot { final Properties p = new Properties(); try (final FileInputStream fis = new FileInputStream( - new File(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties"))) { // Load the properties files p.load(fis); } catch (FileNotFoundException e) { @@ -353,7 +339,7 @@ public class Mobibot extends PircBot { try { stdout = new PrintStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); } catch (IOException e) { System.err.println("Unable to open output (stdout) log file."); e.printStackTrace(System.err); @@ -364,7 +350,7 @@ public class Mobibot extends PircBot { try { stderr = new PrintStream( - new FileOutputStream(logsDir + nickname + ".err", true)); + new FileOutputStream(logsDir + nickname + ".err", true)); } catch (IOException e) { System.err.println("Unable to open error (stderr) log file."); e.printStackTrace(System.err); @@ -436,8 +422,8 @@ public class Mobibot extends PircBot { if (retries == MAX_RECONNECT) { if (logger.isDebugEnabled()) { logger.debug( - "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", - ex); + "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", + ex); } e.printStackTrace(System.err); @@ -550,9 +536,9 @@ public class Mobibot extends PircBot { for (final char c : getNick().toCharArray()) { if (Character.isLetter(c)) { buff.append('[') - .append(String.valueOf(c).toLowerCase()) - .append(String.valueOf(c).toUpperCase()) - .append(']'); + .append(String.valueOf(c).toLowerCase()) + .append(String.valueOf(c).toUpperCase()) + .append(']'); } else { buff.append(c); } @@ -699,7 +685,7 @@ public class Mobibot extends PircBot { commandsList.add(Commands.VIEW_CMD); MODULES.stream().filter(AbstractModule::isEnabled).forEach( - module -> commandsList.addAll(module.getCommands())); + module -> commandsList.addAll(module.getCommands())); if (tell.isEnabled()) { commandsList.add(Tell.TELL_CMD); @@ -729,11 +715,11 @@ public class Mobibot extends PircBot { if (isOp(sender)) { send(sender, "The op commands are:"); send(sender, helpIndent( - Commands.CYCLE_CMD + " " - + Commands.ME_CMD + " " - + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " - + Commands.VERSION_CMD)); + Commands.CYCLE_CMD + " " + + Commands.ME_CMD + " " + + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + + Commands.VERSION_CMD)); } } } @@ -1524,7 +1510,7 @@ public class Mobibot extends PircBot { */ private void storeRecap(final String sender, final String message, final boolean isAction) { recap.add(Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + " -> " + sender + (isAction ? " " : ": ") - + message); + + message); if (recap.size() > MAX_RECAP) { recap.remove(0); @@ -1624,13 +1610,13 @@ public class Mobibot extends PircBot { if (lcArgs.length() > 0) { if ((entry.getLink().toLowerCase().contains(lcArgs)) || - (entry.getTitle().toLowerCase().contains(lcArgs)) || - (entry.getNick().toLowerCase().contains(lcArgs))) { + (entry.getTitle().toLowerCase().contains(lcArgs)) || + (entry.getNick().toLowerCase().contains(lcArgs))) { if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), - isPrivate); + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), + isPrivate); break; } @@ -1641,9 +1627,9 @@ public class Mobibot extends PircBot { } else { if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), - isPrivate); + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), + isPrivate); break; } diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 85da409..222567b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -82,12 +82,12 @@ class Pinboard { final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() - throws Exception { + throws Exception { return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate())); + entry.getTitle(), + postedBy(entry), + entry.getPinboardTags(), + formatDate(entry.getDate())); } }; @@ -105,7 +105,7 @@ class Pinboard { final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() - throws Exception { + throws Exception { return pinboard.deletePin(link); } }; @@ -143,23 +143,23 @@ class Pinboard { final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() - throws Exception { + throws Exception { if (!oldUrl.equals(entry.getLink())) { pinboard.deletePin(oldUrl); return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate())); + entry.getTitle(), + postedBy(entry), + entry.getPinboardTags(), + formatDate(entry.getDate())); } else { return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate()), - true, - true); + entry.getTitle(), + postedBy(entry), + entry.getPinboardTags(), + formatDate(entry.getDate()), + true, + true); } } }; diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index c32f4f5..89f6a4e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -146,9 +146,9 @@ public class Tell { if (messages.size() > 0) { for (final TellMessage message : messages) { bot.send(sender, Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [ID: " + message.getId() + ", " - + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - true); + + " [ID: " + message.getId() + ", " + + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', + true); } } else { bot.send(sender, "There are no messages in the queue.", true); @@ -165,17 +165,17 @@ public class Tell { if (message.isReceived()) { bot.send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " - + message.getId() + ", DELIVERED]", - true); + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " + + message.getId() + ", DELIVERED]", + true); } else { bot.send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " - + message.getId() + ", QUEUED]", - true); + Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " + + message.getId() + ", QUEUED]", + true); } bot.send(sender, bot.helpIndent(message.getMessage(), false), true); @@ -187,10 +187,10 @@ public class Tell { } else { bot.send(sender, "To delete one or all delivered messages:"); bot.send(sender, - bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" - + TELL_ALL_KEYWORD + '>')); + bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + + TELL_ALL_KEYWORD + '>')); bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) - + Utils.plural(maxDays, " day.", " days.")); + + Utils.plural(maxDays, " day.", " days.")); } } } else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) { @@ -254,7 +254,7 @@ public class Tell { save(); bot.send(sender, "Message [ID " + message.getId() + "] was queued for " - + Utils.bold(message.getRecipient()), true); + + Utils.bold(message.getRecipient()), true); } else { bot.send(sender, "Sorry, the messages queue is currently full.", true); } @@ -282,42 +282,42 @@ public class Tell { public void send(final String nickname, final boolean isMessage) { if (!nickname.equals(bot.getNick()) && isEnabled()) { messages.stream().filter(message -> message.isMatch(nickname)).forEach( - message -> { - if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { - if (message.getSender().equals(nickname)) { - if (!isMessage) { - bot.send(nickname, Utils.bold("You") + " wanted me to remind you: " - + Utils.reverseColor(message.getMessage()), - true); - - message.setIsReceived(); - message.setIsNotified(); - - save(); - } - } else { - bot.send(nickname, message.getSender() + " wanted me to tell you: " - + Utils.reverseColor(message.getMessage()), - true); + message -> { + if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { + if (message.getSender().equals(nickname)) { + if (!isMessage) { + bot.send(nickname, Utils.bold("You") + " wanted me to remind you: " + + Utils.reverseColor(message.getMessage()), + true); message.setIsReceived(); + message.setIsNotified(); save(); } - } else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() - && !message.isNotified()) { - bot.send(nickname, - "Your message " - + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " - + Utils.bold(message.getRecipient()) + " on " - + Utils.utcDateTime(message.getReceived()), - true); + } else { + bot.send(nickname, message.getSender() + " wanted me to tell you: " + + Utils.reverseColor(message.getMessage()), + true); - message.setIsNotified(); + message.setIsReceived(); save(); } - }); + } else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() + && !message.isNotified()) { + bot.send(nickname, + "Your message " + + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + + Utils.bold(message.getRecipient()) + " on " + + Utils.utcDateTime(message.getReceived()), + true); + + message.setIsNotified(); + + save(); + } + }); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 367e8d7..1397986 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -53,7 +53,7 @@ final class TellMessagesMgr { * @throws UnsupportedOperationException If the constructor is called. */ private TellMessagesMgr() - throws UnsupportedOperationException { + throws UnsupportedOperationException { throw new UnsupportedOperationException("Illegal constructor call."); } @@ -129,4 +129,4 @@ final class TellMessagesMgr { logger.error("Unable to save messages queue.", e); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 7796453..6745d30 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -43,17 +43,17 @@ public final class TwitterOAuth { System.out.print("Enter the PIN (if available) or just hit enter.[PIN]:"); final String pin = br.readLine(); try { - if (pin.length() > 0) { + if (pin != null && pin.length() > 0) { accessToken = twitter.getOAuthAccessToken(requestToken, pin); } else { accessToken = twitter.getOAuthAccessToken(); } System.out.println( - "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" - + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" - + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken - .getTokenSecret()); + "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" + + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" + + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken + .getTokenSecret()); } catch (TwitterException te) { if (401 == te.getStatusCode()) { System.out.println("Unable to get the access token."); @@ -68,4 +68,4 @@ public final class TwitterOAuth { System.exit(0); } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 72e9a0d..ef9d181 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -53,7 +53,7 @@ final public class Utils { * @throws UnsupportedOperationException If the constructor is called. */ private Utils() - throws UnsupportedOperationException { + throws UnsupportedOperationException { throw new UnsupportedOperationException("Illegal constructor call."); } @@ -87,7 +87,7 @@ final public class Utils { */ static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) { return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " - + comment.getComment()); + + comment.getComment()); } /** @@ -316,4 +316,4 @@ final public class Utils { return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 2cb3a5e..57a33cf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -137,8 +137,8 @@ final public class CurrencyConverter extends AbstractModule { for (final Object rawCube : cubes) { cube = (Element) rawCube; EXCHANGE_RATES.put( - cube.getAttribute("currency").getValue(), - cube.getAttribute("rate").getValue()); + cube.getAttribute("currency").getValue(), + cube.getAttribute("rate").getValue()); } EXCHANGE_RATES.put("EUR", "1"); @@ -148,7 +148,7 @@ final public class CurrencyConverter extends AbstractModule { } catch (IOException e) { bot.getLogger().debug("Unable to fetch the exchange rates table.", e); bot.send(sender, - "An error has occurred while fetching the exchange rates table: " + e.getMessage()); + "An error has occurred while fetching the exchange rates table: " + e.getMessage()); } } @@ -170,18 +170,18 @@ final public class CurrencyConverter extends AbstractModule { final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); bot.send(bot.getChannel(), - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) - + ' ' - + cmds[1].toUpperCase() - + " = " - + NumberFormat.getCurrencyInstance(Locale.US) - .format((amt * to) / from) - .substring(1) - + ' ' - + cmds[3].toUpperCase()); + NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + + ' ' + + cmds[1].toUpperCase() + + " = " + + NumberFormat.getCurrencyInstance(Locale.US) + .format((amt * to) / from) + .substring(1) + + ' ' + + cmds[3].toUpperCase()); } catch (NullPointerException ignored) { bot.send(sender, - "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); } } } @@ -206,4 +206,4 @@ final public class CurrencyConverter extends AbstractModule { } } } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index d4e4253..89c9582 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -71,7 +71,7 @@ final public class Lookup extends AbstractModule { * @throws java.net.UnknownHostException If the host is unknown. */ public static String lookup(final String query) - throws UnknownHostException { + throws UnknownHostException { final StringBuilder buffer = new StringBuilder(""); final InetAddress[] results = InetAddress.getAllByName(query); @@ -106,7 +106,7 @@ final public class Lookup extends AbstractModule { * @throws java.io.IOException If a connection error occurs. */ private static String[] whois(final String query) - throws IOException { + throws IOException { return whois(query, WHOIS_HOST); } @@ -118,9 +118,9 @@ final public class Lookup extends AbstractModule { * @return The IP whois data, if any. * @throws java.io.IOException If a connection error occurs. */ - @SuppressWarnings("WeakerAccess, SameParameterValue") + @SuppressWarnings("SameParameterValue") public static String[] whois(final String query, final String host) - throws IOException { + throws IOException { final WhoisClient whois = new WhoisClient(); final String[] lines; @@ -157,8 +157,8 @@ final public class Lookup extends AbstractModule { bot.send(bot.getChannel(), Lookup.lookup(args)); } catch (UnknownHostException ignore) { if (args.matches( - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) { + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) { try { final String[] lines = Lookup.whois(args); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 2c50499..b63a88c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -106,21 +106,21 @@ final public class Twitter extends AbstractModule { try { final ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setDebugEnabled(true) - .setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) - .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) - .setOAuthAccessToken(properties.get(TOKEN_PROP)) - .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); + .setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) + .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) + .setOAuthAccessToken(properties.get(TOKEN_PROP)) + .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); final TwitterFactory tf = new TwitterFactory(cb.build()); final twitter4j.Twitter twitter = tf.getInstance(); final Status status = twitter.updateStatus(message + " (" + sender + ')'); bot.send(sender, - "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status - .getId()); + "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status + .getId()); } catch (Exception e) { bot.getLogger().warn("Unable to post to Twitter: " + message, e); bot.send(sender, "An error has occurred: " + e.getMessage()); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 8bba863..02e680e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -143,8 +143,8 @@ final public class WorldTime extends AbstractModule { COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); ZoneId.getAvailableZoneIds().stream().filter( - tz -> !tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)).forEach( - tz -> COUNTRIES_MAP.put(tz, tz)); + tz -> !tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)).forEach( + tz -> COUNTRIES_MAP.put(tz, tz)); } /** @@ -166,8 +166,8 @@ final public class WorldTime extends AbstractModule { response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); } else { response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( - DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) - + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); + DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) + + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); } } else { isInvalidTz = true; @@ -205,7 +205,7 @@ final public class WorldTime extends AbstractModule { private String internetTime() { final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) - + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); + + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); return String.format("%c%03d", '@', beats); } @@ -216,4 +216,4 @@ final public class WorldTime extends AbstractModule { public boolean isPrivateMsgEnabled() { return true; } -} \ No newline at end of file +} diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index 8e18cd2..a710d3e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -50,11 +50,11 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class UtilsTest { static final String ASCII = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; final Calendar cal = Calendar.getInstance(); final LocalDateTime localDateTime = - LocalDateTime.of(1952, 2, 17, 12, 30, 0); + LocalDateTime.of(1952, 2, 17, 12, 30, 0); @BeforeClass public void setUp() { @@ -76,9 +76,9 @@ public class UtilsTest { @Test public void testEnsureDir() throws Exception { assertThat(Utils.ensureDir("dir", false)).as("ensureDir(dir, false)") - .isEqualTo("dir" + File.separatorChar); + .isEqualTo("dir" + File.separatorChar); assertThat(Utils.ensureDir("https://erik.thauvin.net", true)) - .as("ensureDir(erik.thauvin.net, true)").isEqualTo("https://erik.thauvin.net/"); + .as("ensureDir(erik.thauvin.net, true)").isEqualTo("https://erik.thauvin.net/"); } @Test @@ -131,13 +131,13 @@ public class UtilsTest { @Test public void testUnescapeXml() throws Exception { assertThat(Utils.unescapeXml("<a name="test & ''">")) - .isEqualTo("<a name=\"test & ''\">"); + .isEqualTo("<a name=\"test & ''\">"); } @Test public void testUtcDateTime() throws Exception { assertThat(Utils.utcDateTime(cal.getTime())).as("utcDateTime(date)").isEqualTo("1952-02-17 12:30"); assertThat(Utils.utcDateTime(localDateTime)).as("utcDateTime(localDate)") - .isEqualTo("1952-02-17 12:30"); + .isEqualTo("1952-02-17 12:30"); } -} \ No newline at end of file +} diff --git a/version.properties b/version.properties index 434dfb8..8fe2cde 100644 --- a/version.properties +++ b/version.properties @@ -1,8 +1,7 @@ -#Sun, 29 Oct 2017 09:38:57 -0700 -#Mon Dec 07 01:31:00 PST 2015 -version.project=mobibot +#Generated by the Semver Plugin for Gradle +#Fri Jul 13 00:45:23 PDT 2018 +version.prerelease=beta +version.buildmeta=1 +version.patch=3 version.major=0 version.minor=7 -version.patch=2 -version.prerelease=beta -version.buildmeta=030 From 9c8a2a61ddc018c335a7c0c755a321f783022967 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 00:46:10 -0700 Subject: [PATCH 146/842] Implemented new version of OWM. --- .../erik/mobibot/modules/Weather2.java | 88 ++++++++++++------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index e876238..6fc8c80 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -31,12 +31,16 @@ */ package net.thauvin.erik.mobibot.modules; -import net.aksingh.owmjapis.CurrentWeather; -import net.aksingh.owmjapis.OpenWeatherMap; +import net.aksingh.owmjapis.api.APIException; +import net.aksingh.owmjapis.core.OWM; +import net.aksingh.owmjapis.model.CurrentWeather; +import net.aksingh.owmjapis.model.param.Main; +import net.aksingh.owmjapis.model.param.Weather; +import net.aksingh.owmjapis.model.param.Wind; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; -import java.io.IOException; +import java.util.List; /** * The <code>Weather2</code> module. @@ -71,9 +75,9 @@ public class Weather2 extends AbstractModule { new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); } - private String fAndC(final Float f) { - final Float c = (f - 32) * 5 / 9; - return Math.round(f) + " \u00B0F, " + Math.round(c) + " \u00B0C"; + private String fAndC(final Double d) { + final Double c = (d - 32) * 5 / 9; + return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; } /** @@ -101,7 +105,8 @@ public class Weather2 extends AbstractModule { * Fetches the weather data from a specific city. */ private void run(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - final OpenWeatherMap owm = new OpenWeatherMap(properties.get(OWM_API_KEY_PROP)); + final OWM owm = new OWM(properties.get(OWM_API_KEY_PROP)); + owm.setUnit(OWM.Unit.IMPERIAL); if (Utils.isValidString(args)) { final String[] argv = args.split(","); @@ -115,46 +120,54 @@ public class Weather2 extends AbstractModule { } try { - final CurrentWeather cwd = owm.currentWeatherByCityName(city, country); + final CurrentWeather cwd; + if (city.matches("\\d+")) { + cwd = owm.currentWeatherByZipCode(Integer.parseInt(city), OWM.Country.UNITED_STATES); + } else { + cwd = owm.currentWeatherByCityName(city, getCountry(country)); + } if (cwd.hasCityName()) { bot.send(sender, "City: " + cwd.getCityName() + " [" + country + "]", isPrivate); - final CurrentWeather.Main main = cwd.getMainInstance(); - if (main.hasTemperature()) { - bot.send(sender, "Temperature: " + fAndC(main.getTemperature()), isPrivate); - } + final Main main = cwd.getMainData(); + if (main != null) { + if (main.hasTemp()) { + bot.send(sender, "Temperature: " + fAndC(main.getTemp()), isPrivate); + } - if (main.hasHumidity()) { - bot.send(sender, "Humidity: " + Math.round(main.getHumidity()) + "%", isPrivate); - } - - if (cwd.hasWindInstance()) { - final CurrentWeather.Wind w = cwd.getWindInstance(); - if (w.hasWindSpeed()) { - bot.send(sender, "Wind: " + wind(w.getWindSpeed()), isPrivate); + if (main.hasHumidity()) { + bot.send(sender, "Humidity: " + Math.round(main.getHumidity()) + "%", isPrivate); } } - if (cwd.hasWeatherInstance()) { - CurrentWeather.Weather w; + if (cwd.hasWindData()) { + final Wind w = cwd.getWindData(); + if (w != null && w.hasSpeed()) { + bot.send(sender, "Wind: " + wind(w.getSpeed()), isPrivate); + } + } + + if (cwd.hasWeatherList()) { final StringBuilder condition = new StringBuilder("Condition: "); - for (int i = 0; i < cwd.getWeatherCount(); i++) { - w = cwd.getWeatherInstance(i); - if (i != 0) { - condition.append(", ").append(w.getWeatherDescription()); - } else { - condition.append(Utils.capitalize(w.getWeatherDescription())); + final List<Weather> list = cwd.getWeatherList(); + if (list != null) { + for (Weather w : list) { + if (condition.indexOf(",") == -1) { + condition.append(Utils.capitalize(w.getDescription())); + } else { + condition.append(", ").append(w.getDescription()); + } } + bot.send(sender, condition.toString(), isPrivate); } - bot.send(sender, condition.toString(), isPrivate); } - bot.send(sender, Utils.green("https://openweathermap.org/city/" + cwd.getCityCode()), isPrivate); + bot.send(sender, Utils.green("https://openweathermap.org/city/" + cwd.getCityId()), isPrivate); return; } - } catch (IOException e) { + } catch (APIException | NullPointerException e) { if (bot.getLogger().isDebugEnabled()) { bot.getLogger().debug("Unable to perform weather lookup: " + args, e); } @@ -167,8 +180,19 @@ public class Weather2 extends AbstractModule { helpResponse(bot, sender, args, isPrivate); } - private String wind(final Float w) { + private String wind(final Double w) { final double kmh = w * 1.60934; return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; } + + private OWM.Country getCountry(String countryCode) { + for (OWM.Country c : OWM.Country.values()) { + if (c.name().equalsIgnoreCase(countryCode)) { + return c; + } + + } + + return OWM.Country.UNITED_STATES; + } } From 406de816cd997c15f22ab274231338ecc38c54c9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 00:47:47 -0700 Subject: [PATCH 147/842] Version 0.7.3-beat+006 now running on #mobitopia. --- .idea/modules/mobibot.iml | 19 +++--- build.gradle | 19 ++++-- mobibot.ipr | 61 ++++++++++++++++++- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 14 +++-- version.properties | 9 +-- 5 files changed, 93 insertions(+), 29 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 2d27c23..b54e872 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.2-beta+030" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+1" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -24,7 +24,10 @@ <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.10.0" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.2.2" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.4.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.4.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.11.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.11.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20180130" level="project" /> <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> @@ -37,20 +40,14 @@ <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.14.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.30" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.30" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.50" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.2" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.5" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.50" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="module-library"> - <library name="Gradle: owm-japis-2.5.0.5"> - <CLASSES> - <root url="jar://$MODULE_DIR$/../../lib/owm-japis-2.5.0.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.10.0" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" /> diff --git a/build.gradle b/build.gradle index 555b0f6..5bb7906 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'application' id "com.github.ben-manes.versions" version "0.20.0" id "org.owasp.dependencycheck" version "3.2.1" - id "net.thauvin.erik.gradle.semver" version "0.9.6-beta" + id "net.thauvin.erik.gradle.semver" version "0.9.8-beta" id "com.github.spotbugs" version "1.6.2" } @@ -16,7 +16,7 @@ defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -final def semverProcessor = "net.thauvin.erik:semver:1.1.0-beta" +final def semverProcessor = "net.thauvin.erik:semver:1.0.1" mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' @@ -43,7 +43,7 @@ dependencies { compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.10.0' + compile 'com.squareup.okhttp3:okhttp:3.11.0' compile 'com.rometools:rome:1.11.0' @@ -55,8 +55,7 @@ dependencies { compile 'org.twitter4j:twitter4j-core:4.0.6' compile 'net.thauvin.erik:pinboard-poster:1.0.0' - // https://bitbucket.org/akapribot/owm-japis - compile files('lib/owm-japis-2.5.0.5.jar') + compile 'net.aksingh:owm-japis:2.5.2.2' testCompile 'org.testng:testng:6.14.3' testCompile 'org.assertj:assertj-core:3.10.0' @@ -125,6 +124,16 @@ task deploy(dependsOn: ['build']) { mustRunAfter clean } +incrementBuildMeta { + doFirst { + buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) + } +} + +compileJava { + dependsOn('incrementBuildMeta') +} + task release(dependsOn: ['wrapper', 'clean', 'deploy']) { group = 'Publishing' description = 'Releases new version.' diff --git a/mobibot.ipr b/mobibot.ipr index 690d90b..896fefa 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -363,6 +363,15 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/b19b5927c2c25b6c70f093767041e641ae0b1b35/jsr305-3.0.2-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: com.google.code.gson:gson:2.8.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.2/3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf/gson-2.8.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.2/b2da9f8444128651758719856de579eacff7f387/gson-2.8.2-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: com.rometools:rome-utils:1.11.0"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.0/a35a96278cb585b3875c25c45bd2b438058392a7/rome-utils-1.11.0.jar!/" /> @@ -381,13 +390,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.0/77c6cb39cfd14bd0b02ccbc6bbb23a13e7300d48/rome-1.11.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:3.10.0"> + <library name="Gradle: com.squareup.okhttp3:okhttp:3.11.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.10.0/7ef0f1d95bf4c0b3ba30bbae25e0e562b05cf75e/okhttp-3.10.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.11.0/75966e05a49046ca2ae734e5626f28837a8d1e82/okhttp-3.11.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.10.0/e99b7b608968f16b07104b93e62cb90701174d0/okhttp-3.10.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.11.0/cf16b99519850b1adc3b259c90fd8566a0f592f3/okhttp-3.11.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okio:okio:1.14.0"> @@ -399,6 +408,24 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.14.0/e7c96b4fe5651490d8f3c022042940d743a3bdd9/okio-1.14.0-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: com.squareup.retrofit2:converter-gson:2.4.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.4.0/15d7790ee311d961379c51b00aba12d5967cb7ea/converter-gson-2.4.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.4.0/5da04ef58bc122d13185fd4317ad7bfbc554d881/converter-gson-2.4.0-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: com.squareup.retrofit2:retrofit:2.4.0"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.4.0/fc4aa382632bfaa7be7b41579efba41d5a71ecf3/retrofit-2.4.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.4.0/451207353948708405a08287e2a315c3f23553fe/retrofit-2.4.0-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: commons-cli:commons-cli:1.4"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar!/" /> @@ -417,6 +444,16 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/961dc27eabbe71bf32478baffe0e1be915ce7689/commons-net-3.6-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: net.aksingh:owm-japis:2.5.2.2"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.2/cfee49bc8bc2b553c6906bb37398d3b4c91be8a4/owm-japis-2.5.2.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.2/46c29fe0847ec99e749128ee38687fa55116ea65/owm-japis-2.5.2.2-sources.jar!/" /> + <root url="file://L:/Archives/OWM/owm-japis/src" /> + </SOURCES> + </library> <library name="Gradle: net.objecthunter:exp4j:0.4.8"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar!/" /> @@ -507,6 +544,24 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.50/d3189b4b8c72c7f3a327e3c21dca8dbcba267359/kotlin-stdlib-common-1.2.50-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.30"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.30/ca12c47fc1e3a7316067b2a51e2f214745ebf8c5/kotlin-stdlib-jdk7-1.2.30.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.30/49e090644e085340bb9afc7d554b2df8394bb36c/kotlin-stdlib-jdk7-1.2.30-sources.jar!/" /> + </SOURCES> + </library> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.30"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.30/f916048adc012c9342b796a5f84c0ac6205abcac/kotlin-stdlib-jdk8-1.2.30.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.30/3a00be60f8d3eb8dfe0bedd9a174cf2948138287/kotlin-stdlib-jdk8-1.2.30-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.50"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.50/66d47b004c5b8a1d2d1df9e463187390ed741316/kotlin-stdlib-1.2.50.jar!/" /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index bdd3309..35020ee 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -4,26 +4,28 @@ */ package net.thauvin.erik.mobibot; -import java.util.Date; +import java.time.*; /** * Provides semantic version information. * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> + * @author <a href="https://github.com/ethauvin/semver">Semantic Version + * Annotation Processor</a> */ public final class ReleaseInfo { public final static String PRERELEASE_PREFIX = "-"; public final static String BUILDMETA_PREFIX = "+"; - public final static String PROJECT = ""; - public final static Date BUILDDATE = new Date(1531467929376L); + public final static String PROJECT = "mobibot"; + public final static LocalDateTime BUILDDATE = + LocalDateTime.ofInstant(Instant.ofEpochMilli(1531553255553L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "1"; + public final static String BUILDMETA = "006"; - /** + /** * The full version string. * <p> * Formatted as: diff --git a/version.properties b/version.properties index 8fe2cde..02ca2fa 100644 --- a/version.properties +++ b/version.properties @@ -1,7 +1,8 @@ #Generated by the Semver Plugin for Gradle -#Fri Jul 13 00:45:23 PDT 2018 -version.prerelease=beta -version.buildmeta=1 -version.patch=3 +#Sat Jul 14 00:27:34 PDT 2018 +version.buildmeta=006 version.major=0 version.minor=7 +version.patch=3 +version.prerelease=beta +version.project=mobibot From a99eb5d5f67efca5d2f46aefb49658cac0dbb0ef Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 15:08:02 -0700 Subject: [PATCH 148/842] Updated deps. --- kobalt/src/Build.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 8a1af6d..470b118 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -62,9 +62,9 @@ val p = project { "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.11.0") compile("commons-cli:commons-cli:1.4", "commons-net:commons-net:3.6") - compile("com.squareup.okhttp3:okhttp:3.10.0") + compile("com.squareup.okhttp3:okhttp:3.11.0") - compile("com.rometools:rome:1.10.0") + compile("com.rometools:rome:1.11.0") compile("org.json:json:20180130") compile("org.ostermiller:utils:1.07.00") @@ -74,11 +74,12 @@ val p = project { compile("org.twitter4j:twitter4j-core:4.0.6") compile("net.thauvin.erik:pinboard-poster:1.0.0") - // https://bitbucket.org/akapribot/owm-japis/ - compile(file("lib/owm-japis-2.5.0.5.jar")) + compile("net.aksingh:owm-japis:2.5.2.2") apt(processorJar) compileOnly(processorJar) + + compileOnly("com.github.spotbugs:spotbugs-annotations:3.1.5") } dependenciesTest { From 3011075f9cad72272abf1660a9ab001a5eacfb80 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 15:12:49 -0700 Subject: [PATCH 149/842] Fix for CIs. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5bb7906..2b9cf61 100644 --- a/build.gradle +++ b/build.gradle @@ -73,7 +73,7 @@ tasks.withType(SpotBugsTask) { xml.enabled = false html.enabled = true } - excludeFilter = file("$projectDir/config/spotbugs/excludeFilter.xml") + excludeFilter = file("config/spotbugs/excludeFilter.xml") } compileJava { From 71e70884ce33768dc2c5932bcd9105f4081b0041 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 15:54:36 -0700 Subject: [PATCH 150/842] Fix for CIs. --- build.gradle | 2 +- .../{excludefilter.xml => excludeFilter.xml} | 0 .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +-- .../erik/mobibot/modules/Weather2.java | 26 +++++++++---------- version.properties | 4 +-- 5 files changed, 18 insertions(+), 18 deletions(-) rename config/spotbugs/{excludefilter.xml => excludeFilter.xml} (100%) diff --git a/build.gradle b/build.gradle index 2b9cf61..5bb7906 100644 --- a/build.gradle +++ b/build.gradle @@ -73,7 +73,7 @@ tasks.withType(SpotBugsTask) { xml.enabled = false html.enabled = true } - excludeFilter = file("config/spotbugs/excludeFilter.xml") + excludeFilter = file("$projectDir/config/spotbugs/excludeFilter.xml") } compileJava { diff --git a/config/spotbugs/excludefilter.xml b/config/spotbugs/excludeFilter.xml similarity index 100% rename from config/spotbugs/excludefilter.xml rename to config/spotbugs/excludeFilter.xml diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 35020ee..bce378d 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1531553255553L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1531606310641L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "006"; + public final static String BUILDMETA = "009"; /** * The full version string. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 6fc8c80..bf75a43 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -80,6 +80,17 @@ public class Weather2 extends AbstractModule { return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; } + private OWM.Country getCountry(String countryCode) { + for (OWM.Country c : OWM.Country.values()) { + if (c.name().equalsIgnoreCase(countryCode)) { + return c; + } + + } + + return OWM.Country.UNITED_STATES; + } + /** * {@inheritDoc} */ @@ -90,7 +101,7 @@ public class Weather2 extends AbstractModule { bot.send(sender, "For example:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") - + ". Zip codes are supported in the US."); + + ". Zip codes are supported in most countries."); } /** @@ -122,7 +133,7 @@ public class Weather2 extends AbstractModule { try { final CurrentWeather cwd; if (city.matches("\\d+")) { - cwd = owm.currentWeatherByZipCode(Integer.parseInt(city), OWM.Country.UNITED_STATES); + cwd = owm.currentWeatherByZipCode(Integer.parseInt(city), getCountry(country)); } else { cwd = owm.currentWeatherByCityName(city, getCountry(country)); } @@ -184,15 +195,4 @@ public class Weather2 extends AbstractModule { final double kmh = w * 1.60934; return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; } - - private OWM.Country getCountry(String countryCode) { - for (OWM.Country c : OWM.Country.values()) { - if (c.name().equalsIgnoreCase(countryCode)) { - return c; - } - - } - - return OWM.Country.UNITED_STATES; - } } diff --git a/version.properties b/version.properties index 02ca2fa..223b738 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Sat Jul 14 00:27:34 PDT 2018 -version.buildmeta=006 +#Sat Jul 14 15:11:49 PDT 2018 +version.buildmeta=009 version.major=0 version.minor=7 version.patch=3 From 92fd9c5b0ddf1bb85b13162a78c1036784f27b24 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 16:09:19 -0700 Subject: [PATCH 151/842] Fix for CIs. --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/GoogleSearch.java | 2 +- version.properties | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index bce378d..74ce7fe 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1531606310641L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1531609684377L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "009"; + public final static String BUILDMETA = "011"; /** * The full version string. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 0097a28..9f4ca46 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -110,7 +110,7 @@ final public class GoogleSearch extends AbstractModule { /** * Searches Google. */ - @SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD") + @SuppressFBWarnings(value = {"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) private void run(final Mobibot bot, final String sender, final String query) { try { final String q = URLEncoder.encode(query, "UTF-8"); diff --git a/version.properties b/version.properties index 223b738..4c315bd 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Sat Jul 14 15:11:49 PDT 2018 -version.buildmeta=009 +#Sat Jul 14 16:08:00 PDT 2018 +version.buildmeta=011 version.major=0 version.minor=7 version.patch=3 From f0faf1fc023752830b050520ebf466069bda2dbf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 16:16:22 -0700 Subject: [PATCH 152/842] Fix for CIs. --- config/spotbugs/excludeFilter.xml | 6 ++++++ .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- version.properties | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/config/spotbugs/excludeFilter.xml b/config/spotbugs/excludeFilter.xml index f7e9cce..eb80a26 100644 --- a/config/spotbugs/excludeFilter.xml +++ b/config/spotbugs/excludeFilter.xml @@ -11,4 +11,10 @@ </Or> <Confidence value="2"/> </Match> + <Match> + <Class name="net.thauvin.erik.mobibot.Mobibot"/> + <Method name="main"/> + <Bug pattern="PATH_TRAVERSAL_OUT"/> + <Confidence value="1"/> + </Match> </FindBugsFilter> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 74ce7fe..458d141 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1531609684377L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1531610141834L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "011"; + public final static String BUILDMETA = "013"; /** * The full version string. diff --git a/version.properties b/version.properties index 4c315bd..deb7347 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Sat Jul 14 16:08:00 PDT 2018 -version.buildmeta=011 +#Sat Jul 14 16:15:40 PDT 2018 +version.buildmeta=013 version.major=0 version.minor=7 version.patch=3 From 30adb67fed5bb544b49f8f2aa05903f420993130 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 16:53:15 -0700 Subject: [PATCH 153/842] Added JDK 10 to CIs. --- .circleci/config.yml | 109 ++++++++++++++++++------------------------- .travis.yml | 3 +- 2 files changed, 48 insertions(+), 64 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d455fe1..fd2e41d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,41 +4,55 @@ defaults: &defaults JVM_OPTS: -Xmx3200m TERM: dumb +defaults_gradle: &defaults_gradle + steps: + - checkout + - restore_cache: + keys: + - gradle-dependencies-{{ checksum "build.gradle" }} + # fallback to using the latest cache if no exact match is found + - gradle-dependencies- + - run: + name: Gradle Dependencies + command: ./gradlew dependencies + - save_cache: + paths: ~/.m2 + key: gradle-dependencies-{{ checksum "build.gradle" }} + - run: + name: Run All Checks + command: ./gradlew check + - store_artifacts: + path: build/reports/ + destination: reports + - store_test_results: + path: build/reports/ + version: 2.0 jobs: + build_gradle_jdk10: + <<: *defaults - build_gradle: + docker: + - image: circleci/openjdk:10-jdk + + <<: *defaults_gradle + + build_gradle_jdk9: + <<: *defaults + + docker: + - image: circleci/openjdk:9-jdk + + <<: *defaults_gradle + + build_gradle_jdk8: <<: *defaults docker: - image: circleci/openjdk:8-jdk - steps: - - checkout - - restore_cache: - keys: - - gradle-dependencies-{{ checksum "build.gradle" }} - # fallback to using the latest cache if no exact match is found - - gradle-dependencies- - - - run: - name: Gradle Dependencies - command: ./gradlew dependencies - - - save_cache: - paths: ~/.m2 - key: gradle-dependencies-{{ checksum "build.gradle" }} - - - run: - name: Run All Checks - command: ./gradlew check - - - store_artifacts: - path: build/reports/ - destination: reports - - store_test_results: - path: build/reports/ + <<: *defaults_gradle build_kobalt: <<: *defaults @@ -72,44 +86,13 @@ jobs: - store_test_results: path: kobaltBuild/test-output/ - build_gradle_jdk9: - <<: *defaults - - docker: - - image: circleci/openjdk:9-jdk - - steps: - - checkout - - restore_cache: - keys: - - gradle-dependencies-{{ checksum "build.gradle" }} - # fallback to using the latest cache if no exact match is found - - gradle-dependencies- - - - run: - name: Gradle Dependencies - command: ./gradlew dependencies - - - save_cache: - paths: ~/.m2 - key: gradle-dependencies-{{ checksum "build.gradle" }} - - - run: - name: Run All Checks - command: ./gradlew check - - - store_artifacts: - path: build/reports/ - destination: reports - - store_test_results: - path: build/reports/ - workflows: version: 2 gradle: - jobs: - - build_gradle - - build_gradle_jdk9 + jobs: + - build_gradle + - build_gradle_jdk9 + - build_gradle_jdk10 kobalt: - jobs: - - build_kobalt \ No newline at end of file + jobs: + - build_kobalt diff --git a/.travis.yml b/.travis.yml index d106d01..80eea46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: java jdk: - oraclejdk8 - oraclejdk9 + - oraclejdk10 before_install: - - chmod +x gradlew \ No newline at end of file + - chmod +x gradlew From 35f9fec5a98012b30282f1728c6c45fb6e878435 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 14 Jul 2018 16:58:18 -0700 Subject: [PATCH 154/842] Fixed JDK 8 in CircleCI. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fd2e41d..d2fe0ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -90,7 +90,7 @@ workflows: version: 2 gradle: jobs: - - build_gradle + - build_gradle_jdk8 - build_gradle_jdk9 - build_gradle_jdk10 kobalt: From 76f7212672038e5cc2404df2c5f8d919f888c522 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 Oct 2018 15:45:27 -0700 Subject: [PATCH 155/842] Added JDK 11 to CIs --- .circleci/config.yml | 8 ++++++++ .travis.yml | 1 + 2 files changed, 9 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index d2fe0ce..13db5fc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,6 +30,14 @@ defaults_gradle: &defaults_gradle version: 2.0 jobs: + build_gradle_jdk11: + <<: *defaults + + docker: + - image: circleci/openjdk:11-jdk + + <<: *defaults_gradle + build_gradle_jdk10: <<: *defaults diff --git a/.travis.yml b/.travis.yml index 80eea46..ec199ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ jdk: - oraclejdk8 - oraclejdk9 - oraclejdk10 + - oraclejdk11 before_install: - chmod +x gradlew From c70fbc331f16a98a362c327a09a9fcc706fb930a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 Oct 2018 15:46:08 -0700 Subject: [PATCH 156/842] Updated deps. --- .idea/modules/mobibot.iml | 38 +++--- build.gradle | 24 ++-- gradle/wrapper/gradle-wrapper.jar | Bin 54417 -> 56177 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- kobalt/Build.kt.iml | 33 ----- kobalt/wrapper/kobalt-wrapper.properties | 2 +- mobibot.ipr | 113 +++++++++--------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 +- version.properties | 4 +- 9 files changed, 93 insertions(+), 127 deletions(-) delete mode 100644 kobalt/Build.kt.iml diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index b54e872..5aebc5c 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+1" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+018" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -18,38 +18,38 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.11.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.11.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.11.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.11.1" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0" level="project" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.2.2" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.2.3" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.4.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.4.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.11.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.11.0" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20180130" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.11.1" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20180813" level="project" /> <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.11.3" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.1.0-beta" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.5" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.11.0" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.8" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.11.1" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.14.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.30" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.30" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.50" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.5" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.71" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.50" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.10.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.11.1" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.apache-extras.beanshell:bsh:2.0b6" level="project" /> </component> diff --git a/build.gradle b/build.gradle index 5bb7906..42d9d30 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,9 @@ plugins { id 'idea' id 'application' id "com.github.ben-manes.versions" version "0.20.0" - id "org.owasp.dependencycheck" version "3.2.1" + id "org.owasp.dependencycheck" version "3.3.4" id "net.thauvin.erik.gradle.semver" version "0.9.8-beta" - id "com.github.spotbugs" version "1.6.2" + id "com.github.spotbugs" version "1.6.5" } @@ -36,32 +36,32 @@ dependencies { compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile 'org.apache.logging.log4j:log4j-api:2.11.0' - compile 'org.apache.logging.log4j:log4j-core:2.11.0' - compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.0' + compile 'org.apache.logging.log4j:log4j-api:2.11.1' + compile 'org.apache.logging.log4j:log4j-core:2.11.1' + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.1' compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' compile 'com.squareup.okhttp3:okhttp:3.11.0' - compile 'com.rometools:rome:1.11.0' + compile 'com.rometools:rome:1.11.1' - compile 'org.json:json:20180130' + compile 'org.json:json:20180813' compile 'org.ostermiller:utils:1.07.00' compile 'org.jsoup:jsoup:1.11.3' compile 'net.objecthunter:exp4j:0.4.8' - compile 'org.twitter4j:twitter4j-core:4.0.6' + compile 'org.twitter4j:twitter4j-core:4.0.7' compile 'net.thauvin.erik:pinboard-poster:1.0.0' - compile 'net.aksingh:owm-japis:2.5.2.2' + compile 'net.aksingh:owm-japis:2.5.2.3' testCompile 'org.testng:testng:6.14.3' - testCompile 'org.assertj:assertj-core:3.10.0' + testCompile 'org.assertj:assertj-core:3.11.1' - spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0' - compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.5' + //spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0' + compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.8' } test { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 758de960ec7947253b058ff79c88ce51f3abe08a..29953ea141f55e3b8fc691d31b5ca8816d89fa87 100644 GIT binary patch delta 49471 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 WQGf@&|Fh+cf(#evVP8i7iTyt`4<kkZ delta 47750 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 g*deErS@4UrK<)vcGA2Rx+H;t2P44+yIZ$Lf#N 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 G+WsG4)AB+9 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2d80b69..e0b3fb8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/kobalt/Build.kt.iml b/kobalt/Build.kt.iml deleted file mode 100644 index 580d86e..0000000 --- a/kobalt/Build.kt.iml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="Build.kt" external.linked.project.path="$MODULE_DIR$/.." external.root.project.path="$MODULE_DIR$/.." external.system.id="KOBALT" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager"> - <output url="file://$MODULE_DIR$/out/classes" /> - <output-test url="file://$MODULE_DIR$/out/test-classes" /> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module-library"> - <library name="Kobalt: kobalt-versioneye-0.4.5.jar"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/cache/net/thauvin/erik/kobalt-versioneye/0.4.5/kobalt-versioneye-0.4.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module-library"> - <library name="Kobalt: com.beust.kobalt:kobalt:jar:1.0.86"> - <CLASSES> - <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.86/kobalt/wrapper/kobalt-1.0.86.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.86/kobalt/wrapper/kobalt-1.0.86-sources.jar!/" /> - </SOURCES> - </library> - </orderEntry> - </component> -</module> \ No newline at end of file diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index d630dff..f16e1df 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.114 \ No newline at end of file +kobalt.version=1.0.118 \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 896fefa..6f63ba0 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -160,7 +160,7 @@ <option name="autoDownloadKobalt" value="true" /> <option name="downloadSources" value="false" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.114" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.118" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> @@ -222,7 +222,7 @@ <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="10" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="11" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="PropertiesComponent"> @@ -336,22 +336,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.5"> + <library name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.8"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.5/4e2e5448fba7b4aa298d4eb9af25a9ba707bcb0e/spotbugs-annotations-3.1.5.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.8/a8752df3cdefb9278bea6d9f36286986b4414336/spotbugs-annotations-3.1.8.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.5/da4048bfca28ece42ea0affc28c8362927aa0ee4/spotbugs-annotations-3.1.5-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.8/c71ee0a073e0fd3a900d3e91927ba601521f7e6c/spotbugs-annotations-3.1.8-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.5"> + <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.5/2d13ba8f4e9e578ef3636c064dae4ed675c8de7d/compiler-0.9.5.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/1d1fab03f31a75018fc30ec248859a5c89cd342f/compiler-0.9.4.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.5/45e36f16202869f7b3e335cb9497318380a3f59a/compiler-0.9.5-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/599674a480f9940ff5e25b375c6f59d14d8a4bfa/compiler-0.9.4-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.google.code.findbugs:jsr305:3.0.2"> @@ -363,31 +363,31 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/b19b5927c2c25b6c70f093767041e641ae0b1b35/jsr305-3.0.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.google.code.gson:gson:2.8.2"> + <library name="Gradle: com.google.code.gson:gson:2.8.5"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.2/3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf/gson-2.8.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.2/b2da9f8444128651758719856de579eacff7f387/gson-2.8.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/c5b4c491aecb72e7c32a78da0b5c6b9cda8dee0f/gson-2.8.5-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome-utils:1.11.0"> + <library name="Gradle: com.rometools:rome-utils:1.11.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.0/a35a96278cb585b3875c25c45bd2b438058392a7/rome-utils-1.11.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.1/8dc6594b46896917eff81ce87688327636743ef/rome-utils-1.11.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.0/1e1d75cf40c8b75679814829fb37ed8210d4fffe/rome-utils-1.11.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.1/258377a50b0972ea8b6ac55b2eaa730f5ff9508e/rome-utils-1.11.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.11.0"> + <library name="Gradle: com.rometools:rome:1.11.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.0/245e0d8e5b3461b2842fa739b2693b0e7a4f0a46/rome-1.11.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.1/ad04e529c37f990ac36e9e064c518e6ebfc8caf5/rome-1.11.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.0/77c6cb39cfd14bd0b02ccbc6bbb23a13e7300d48/rome-1.11.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.1/ef64b7db6743c40cc4bbf91787d4d38a9ff60c48/rome-1.11.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okhttp3:okhttp:3.11.0"> @@ -444,14 +444,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/961dc27eabbe71bf32478baffe0e1be915ce7689/commons-net-3.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.aksingh:owm-japis:2.5.2.2"> + <library name="Gradle: net.aksingh:owm-japis:2.5.2.3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.2/cfee49bc8bc2b553c6906bb37398d3b4c91be8a4/owm-japis-2.5.2.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.3/c6e92f68f97a9956375f020937230d5ed226c3a3/owm-japis-2.5.2.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.2/46c29fe0847ec99e749128ee38687fa55116ea65/owm-japis-2.5.2.2-sources.jar!/" /> - <root url="file://L:/Archives/OWM/owm-japis/src" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.3/bc50cd23c52e26569365961029bc5f44eff0684d/owm-japis-2.5.2.3-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: net.objecthunter:exp4j:0.4.8"> @@ -472,13 +471,13 @@ <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.0/pinboard-poster-1.0.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:semver:1.1.0-beta"> + <library name="Gradle: net.thauvin.erik:semver:1.0.1"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.1.0-beta/semver-1.1.0-beta.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.1.0-beta/semver-1.1.0-beta-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.apache-extras.beanshell:bsh:2.0b6"> @@ -490,40 +489,40 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache-extras.beanshell/bsh/2.0b6/76a65f674e8f2df409934824c49ca338987c63ae/bsh-2.0b6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-api:2.11.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-api:2.11.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.0/bede79a3f150711634a3047985517431bf6499f2/log4j-api-2.11.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.1/268f0fe4df3eefe052b57c87ec48517d64fb2a10/log4j-api-2.11.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.0/97f6fb29d69b48fe4bdb47cc21c834b4536cf752/log4j-api-2.11.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.1/acf35297ef03716fc14e11e2978d7c749ed3f7e5/log4j-api-2.11.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-core:2.11.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-core:2.11.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.0/e6b751e02120c08702d98750f6a80bc25343b7f5/log4j-core-2.11.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.1/592a48674c926b01a9a747c7831bcd82a9e6d6e4/log4j-core-2.11.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.0/932392dc515dae4e750ac4a96c39ba69d5e81db3/log4j-core-2.11.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.1/23bef4ea0494ba9fb9835df0e3e23c6883d8d545/log4j-core-2.11.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.0/9ba207b78e470fe7765ebee14f1f0336c9cbcc18/log4j-slf4j-impl-2.11.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.1/4b41b53a3a2d299ce381a69d165381ca19f62912/log4j-slf4j-impl-2.11.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.0/eaf7016f6df9069dbc295a9c7a5d6424a1f237c2/log4j-slf4j-impl-2.11.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.1/2b387980561fb77b2aafdcf0d61552e0fa74928a/log4j-slf4j-impl-2.11.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.assertj:assertj-core:3.10.0"> + <library name="Gradle: org.assertj:assertj-core:3.11.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.10.0/4898fc7b9a2f7f8457bd380f917b08416df313f7/assertj-core-3.10.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.11.1/fdac3217b804d6900fa3650f17b5cb48e620b140/assertj-core-3.11.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.10.0/31def12d16eb0ce1478b77ca94c55e2b7e97b114/assertj-core-3.10.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.11.1/55362d8f04018b1140c5a370057bf8cd4a63c3de/assertj-core-3.11.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> @@ -535,40 +534,40 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.50" type="kotlin.common"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71" type="kotlin.common"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.50/6b19a2fcc29d34878b3aab33fd5fcf70458a73df/kotlin-stdlib-common-1.2.50.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.71/ba18ca1aa0e40eb6f1865b324af2f4cbb691c1ec/kotlin-stdlib-common-1.2.71.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.50/d3189b4b8c72c7f3a327e3c21dca8dbcba267359/kotlin-stdlib-common-1.2.50-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.71/ff62b6a76e3200cac3d7df0859d7665fe6d54955/kotlin-stdlib-common-1.2.71-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.30"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.30/ca12c47fc1e3a7316067b2a51e2f214745ebf8c5/kotlin-stdlib-jdk7-1.2.30.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.71/4ce93f539e2133f172f1167291a911f83400a5d0/kotlin-stdlib-jdk7-1.2.71.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.30/49e090644e085340bb9afc7d554b2df8394bb36c/kotlin-stdlib-jdk7-1.2.30-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.71/1e054c1ef9afdfc7c79bc09dd5eed35791cef7a2/kotlin-stdlib-jdk7-1.2.71-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.30"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.30/f916048adc012c9342b796a5f84c0ac6205abcac/kotlin-stdlib-jdk8-1.2.30.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.71/5470d1f752cd342edb77e1062bac07e838d2cea4/kotlin-stdlib-jdk8-1.2.71.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.30/3a00be60f8d3eb8dfe0bedd9a174cf2948138287/kotlin-stdlib-jdk8-1.2.30-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.71/f3016e6e03b6a6adb777b403d856e0f2c36ea646/kotlin-stdlib-jdk8-1.2.71-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.50"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.71"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.50/66d47b004c5b8a1d2d1df9e463187390ed741316/kotlin-stdlib-1.2.50.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.71/d9717625bb3c731561251f8dd2c67a1011d6764c/kotlin-stdlib-1.2.71.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.50/9ec9cf82e5e18d990a4b4fb3b5763cd3637b6d63/kotlin-stdlib-1.2.50-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.71/2e8041d08283cbbb58ad131617486fc6449f6c85/kotlin-stdlib-1.2.71-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jetbrains:annotations:13.0"> @@ -580,13 +579,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20180130"> + <library name="Gradle: org.json:json:20180813"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180130/26ba2ec0e791a32ea5dfbedfcebf36447ee5b12c/json-20180130.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180813/8566b2b0391d9d4479ea225645c6ed47ef17fe41/json-20180813.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180130/66ab57acbb9086d16201c2eafa2145d2b914bb26/json-20180130-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180813/60753006d96d5fc85b61b0897f3326b5c4e1c4f0/json-20180813-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jsoup:jsoup:1.11.3"> @@ -607,13 +606,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.slf4j:slf4j-api:1.8.0-alpha2"> + <library name="Gradle: org.slf4j:slf4j-api:1.7.25"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.8.0-alpha2/34fa1d87256bbdb376ae7f6fa7e479610cd07dce/slf4j-api-1.8.0-alpha2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.8.0-alpha2/5818a2c13c12c71c2861315d90888104a875f438/slf4j-api-1.8.0-alpha2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/962153db4a9ea71b79d047dfd1b2a0d80d8f4739/slf4j-api-1.7.25-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.testng:testng:6.14.3"> @@ -625,13 +624,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.14.3/bfd2538501a129a142fe8366169c48b91609dc0b/testng-6.14.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.twitter4j:twitter4j-core:4.0.6"> + <library name="Gradle: org.twitter4j:twitter4j-core:4.0.7"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/f3722af4568b96ee66739267e13211b8b66ac7d4/twitter4j-core-4.0.6.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.6/5a3a910793c3510a3e25cea3c51672bb88124527/twitter4j-core-4.0.6-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/4fa8cfcfa162d2b2a76c3be71563ccbfb234a5d9/twitter4j-core-4.0.7-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: pircbot:pircbot:1.5.0"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 458d141..e2ae517 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -18,12 +18,12 @@ public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1531610141834L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1541025595051L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "013"; + public final static String BUILDMETA = "023"; /** * The full version string. diff --git a/version.properties b/version.properties index deb7347..856b864 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Sat Jul 14 16:15:40 PDT 2018 -version.buildmeta=013 +#Wed Oct 31 15:39:54 PDT 2018 +version.buildmeta=023 version.major=0 version.minor=7 version.patch=3 From 2a9eccc6324923e7e16ae9614fef16411ae00c6d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 31 Oct 2018 15:51:24 -0700 Subject: [PATCH 157/842] Using OpenJDK 10, Oracle JDK 10 is deprecated. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ec199ce..6fa1cca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: java jdk: - oraclejdk8 - oraclejdk9 - - oraclejdk10 + - openjdk10 - oraclejdk11 before_install: From 09ab2d5f8ed57b4d4a1c414b3a26e787bd597770 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 17:43:10 -0700 Subject: [PATCH 158/842] Updated for semver 1.2.0. --- version.mustache | 81 +++++------------------------------------------- 1 file changed, 8 insertions(+), 73 deletions(-) diff --git a/version.mustache b/version.mustache index 8cb1997..c984841 100644 --- a/version.mustache +++ b/version.mustache @@ -9,96 +9,31 @@ import java.time.*; /** * Provides semantic version information. * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version - * Annotation Processor</a> + * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> */ public final class {{className}} { - public final static String PRERELEASE_PREFIX = "-"; - public final static String BUILDMETA_PREFIX = "+"; - public final static String PROJECT = "{{project}}"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); public final static int MAJOR = {{major}}; public final static int MINOR = {{minor}}; public final static int PATCH = {{patch}}; public final static String PRERELEASE = "{{preRelease}}"; public final static String BUILDMETA = "{{buildMeta}}"; - /** - * The full version string. - * <p> - * Formatted as: - * <blockquote> - * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> - * </blockquote> - * <p> - * For example: - * <ul> - * <li><code>1.0.0</code></li> - * <li><code>1.0.0-beta</code></li> - * <li><code>1.0.0+20160124144700</code></li> - * <li><code>1.0.0-alpha+001</code></li> - * </ul> + /** + * The full semantic version string. */ public final static String VERSION = Integer.toString(MAJOR) + '.' + Integer.toString(MINOR) + '.' + Integer.toString(PATCH) - + preReleaseWithPrefix() + buildMetaWithPrefix(); + + ((!PRERELEASE.isEmpty()) ? "-" + PRERELEASE : "") + + ((!BUILDMETA.isEmpty()) ? "+" + BUILDMETA : ""); /** * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. */ - private {{className}}() - throws UnsupportedOperationException { + private {{className}}() { throw new UnsupportedOperationException("Illegal constructor call."); } - - /** - * Returns the build metadata with {@value #BUILDMETA_PREFIX} prefix. - * - * @return The build metadata, if any. - */ - public static String buildMetaWithPrefix() { - return buildMetaWithPrefix(BUILDMETA_PREFIX); - } - - /** - * Returns the build metadata. - * - * @param prefix Prefix to prepend. - * @return The build metadata, if any. - */ - public static String buildMetaWithPrefix(final String prefix) { - if (BUILDMETA.length() > 0 && prefix.length() > 0) { - return prefix + BUILDMETA; - } else { - return BUILDMETA; - } - } - - /** - * Returns the pre-release version with {@value #PRERELEASE_PREFIX} prefix. - * - * @return The pre-release version, if any. - */ - public static String preReleaseWithPrefix() { - return preReleaseWithPrefix(PRERELEASE_PREFIX); - } - - /** - * Returns the pre-release version. - * - * @param prefix The prefix to prepend. - * @return The pre-release version, if any. - */ - public static String preReleaseWithPrefix(final String prefix) { - if (PRERELEASE.length() > 0 && prefix.length() > 0) { - return prefix + PRERELEASE; - } else { - return PRERELEASE; - } - } -} \ No newline at end of file +} From e16a58be0838b623b6719520d7bc3832bd545f1e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 18:40:54 -0700 Subject: [PATCH 159/842] Added JDK12, remove JDK9 and JDK10. --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6fa1cca..4d45d00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,7 @@ language: java jdk: - oraclejdk8 - - oraclejdk9 - - openjdk10 - - oraclejdk11 + - oraclejdk12 before_install: - chmod +x gradlew From 06c8217ab16ef4b9cb1e3b236f7dd259b22e7ff2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 18:45:43 -0700 Subject: [PATCH 160/842] Removed JDK9 and JDK10, added JDK12. --- .circleci/config.yml | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 13db5fc..1ba296d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,7 +30,7 @@ defaults_gradle: &defaults_gradle version: 2.0 jobs: - build_gradle_jdk11: + build_gradle_jdk12: <<: *defaults docker: @@ -38,22 +38,6 @@ jobs: <<: *defaults_gradle - build_gradle_jdk10: - <<: *defaults - - docker: - - image: circleci/openjdk:10-jdk - - <<: *defaults_gradle - - build_gradle_jdk9: - <<: *defaults - - docker: - - image: circleci/openjdk:9-jdk - - <<: *defaults_gradle - build_gradle_jdk8: <<: *defaults @@ -99,8 +83,7 @@ workflows: gradle: jobs: - build_gradle_jdk8 - - build_gradle_jdk9 - - build_gradle_jdk10 + - build_gradle_jdk12 kobalt: jobs: - build_kobalt From 20ff7ed62bdc793d4e1780a0133fa37215e7f908 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 21:57:31 -0700 Subject: [PATCH 161/842] Dependencies update. --- build.gradle | 80 ++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/build.gradle b/build.gradle index 42d9d30..4cffbef 100644 --- a/build.gradle +++ b/build.gradle @@ -2,10 +2,10 @@ plugins { id 'java' id 'idea' id 'application' - id "com.github.ben-manes.versions" version "0.20.0" - id "org.owasp.dependencycheck" version "3.3.4" - id "net.thauvin.erik.gradle.semver" version "0.9.8-beta" - id "com.github.spotbugs" version "1.6.5" + id "com.github.ben-manes.versions" version "0.21.0" + id "net.thauvin.erik.gradle.semver" version "0.9.9-beta" + id "com.github.spotbugs" version "1.7.1" + id "org.sonarqube" version "2.7" } @@ -33,49 +33,41 @@ dependencies { annotationProcessor semverProcessor compileOnly semverProcessor - compile 'pircbot:pircbot:1.5.0' + implementation 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile 'org.apache.logging.log4j:log4j-api:2.11.1' - compile 'org.apache.logging.log4j:log4j-core:2.11.1' - compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.1' + implementation 'org.apache.logging.log4j:log4j-api:2.11.2' + implementation 'org.apache.logging.log4j:log4j-core:2.11.2' + implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.2' - compile 'commons-cli:commons-cli:1.4' + implementation 'commons-cli:commons-cli:1.4' - compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.11.0' + implementation 'commons-net:commons-net:3.6' + implementation 'com.squareup.okhttp3:okhttp:3.14.0' - compile 'com.rometools:rome:1.11.1' + implementation 'com.rometools:rome:1.12.0' - compile 'org.json:json:20180813' - compile 'org.ostermiller:utils:1.07.00' - compile 'org.jsoup:jsoup:1.11.3' - compile 'net.objecthunter:exp4j:0.4.8' + implementation 'org.json:json:20180813' + implementation 'org.ostermiller:utils:1.07.00' + implementation 'org.jsoup:jsoup:1.11.3' + implementation 'net.objecthunter:exp4j:0.4.8' - compile 'org.twitter4j:twitter4j-core:4.0.7' - compile 'net.thauvin.erik:pinboard-poster:1.0.0' + implementation 'org.twitter4j:twitter4j-core:4.0.7' + implementation 'net.thauvin.erik:pinboard-poster:1.0.0' - compile 'net.aksingh:owm-japis:2.5.2.3' + implementation 'net.aksingh:owm-japis:2.5.2.3' - testCompile 'org.testng:testng:6.14.3' - testCompile 'org.assertj:assertj-core:3.11.1' + testImplementation 'org.testng:testng:6.14.3' + testImplementation 'org.assertj:assertj-core:3.12.2' //spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0' - compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.8' + compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.11' } test { useTestNG() } -tasks.withType(SpotBugsTask) { - reports { - xml.enabled = false - html.enabled = true - } - excludeFilter = file("$projectDir/config/spotbugs/excludeFilter.xml") -} - compileJava { options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' } @@ -101,6 +93,24 @@ run { args '--v' } +incrementBuildMeta { + doFirst { + buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) + } +} + +compileJava { + dependsOn('incrementBuildMeta') +} + +tasks.withType(SpotBugsTask) { + reports { + xml.enabled = false + html.enabled = true + } + excludeFilter = file("$projectDir/config/spotbugs/excludeFilter.xml") +} + task copyToDeploy(type: Copy) { from('properties') from jar @@ -124,16 +134,6 @@ task deploy(dependsOn: ['build']) { mustRunAfter clean } -incrementBuildMeta { - doFirst { - buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) - } -} - -compileJava { - dependsOn('incrementBuildMeta') -} - task release(dependsOn: ['wrapper', 'clean', 'deploy']) { group = 'Publishing' description = 'Releases new version.' From 6d3e3f2c6ea1568a6400e1fc4e15a9f5c14e76d9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 21:59:23 -0700 Subject: [PATCH 162/842] sonarcloud code improvements. --- sonar-project.properties | 2 + .../net/thauvin/erik/mobibot/Commands.java | 5 +- .../net/thauvin/erik/mobibot/EntriesMgr.java | 171 ++++++++---------- .../thauvin/erik/mobibot/EntryComment.java | 2 +- .../net/thauvin/erik/mobibot/EntryLink.java | 10 +- .../net/thauvin/erik/mobibot/Mobibot.java | 42 ++--- .../java/net/thauvin/erik/mobibot/Tell.java | 15 +- .../net/thauvin/erik/mobibot/TellMessage.java | 4 +- .../thauvin/erik/mobibot/TellMessagesMgr.java | 5 +- .../java/net/thauvin/erik/mobibot/Utils.java | 5 +- .../thauvin/erik/mobibot/modules/Calc.java | 2 +- .../mobibot/modules/CurrencyConverter.java | 4 +- .../thauvin/erik/mobibot/modules/Dice.java | 2 +- .../erik/mobibot/modules/GoogleSearch.java | 2 +- .../thauvin/erik/mobibot/modules/Joke.java | 2 +- .../thauvin/erik/mobibot/modules/Lookup.java | 2 +- .../erik/mobibot/modules/StockQuote.java | 2 +- .../thauvin/erik/mobibot/modules/Twitter.java | 12 +- .../net/thauvin/erik/mobibot/modules/War.java | 4 +- .../erik/mobibot/modules/WorldTime.java | 2 +- 20 files changed, 139 insertions(+), 156 deletions(-) create mode 100644 sonar-project.properties diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..27314ec --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,2 @@ +sonar.projectKey=ethauvin_semver +sonar.sourceEncoding=UTF-8 diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index c5eef7b..14b642c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -38,7 +38,7 @@ package net.thauvin.erik.mobibot; * @created 2014-04-26 * @since 1.0 */ -final public class Commands { +public final class Commands { /** * The add (back)log command. */ @@ -159,8 +159,7 @@ final public class Commands { * * @throws UnsupportedOperationException If the constructor is called. */ - private Commands() - throws UnsupportedOperationException { + private Commands() { throw new UnsupportedOperationException("Illegal constructor call."); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 839d0c1..3fa4ddb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -73,8 +73,7 @@ final class EntriesMgr { * * @throws UnsupportedOperationException If the constructor is called. */ - private EntriesMgr() - throws UnsupportedOperationException { + private EntriesMgr() { throw new UnsupportedOperationException("Illegal constructor call."); } @@ -184,78 +183,76 @@ final class EntriesMgr { } if (Utils.isValidString(bot.getLogsDir()) && Utils.isValidString(bot.getWeblogUrl())) { - Writer fw = null; - try { - fw = new OutputStreamWriter( - new FileOutputStream(bot.getLogsDir() + CURRENT_XML), StandardCharsets.UTF_8); - + final SyndFeedOutput output = new SyndFeedOutput(); SyndFeed rss = new SyndFeedImpl(); - rss.setFeedType("rss_2.0"); - rss.setTitle(bot.getChannel() + " IRC Links"); - rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); - rss.setLink(bot.getWeblogUrl()); - rss.setPublishedDate(Calendar.getInstance().getTime()); - rss.setLanguage("en"); - - EntryLink entry; - StringBuffer buff; - EntryComment comment; final List<SyndEntry> items = new ArrayList<>(0); SyndEntry item; SyndContent description; + try (final Writer fw = new OutputStreamWriter( + new FileOutputStream(bot.getLogsDir() + CURRENT_XML), StandardCharsets.UTF_8)) { + rss.setFeedType("rss_2.0"); + rss.setTitle(bot.getChannel() + " IRC Links"); + rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); + rss.setLink(bot.getWeblogUrl()); + rss.setPublishedDate(Calendar.getInstance().getTime()); + rss.setLanguage("en"); - for (int i = (entries.size() - 1); i >= 0; --i) { - entry = entries.get(i); + EntryLink entry; + StringBuilder buff; + EntryComment comment; - buff = new StringBuffer( - "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' - + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); + for (int i = (entries.size() - 1); i >= 0; --i) { + entry = entries.get(i); - if (entry.getCommentsCount() > 0) { - buff.append(" <br/><br/>"); + buff = new StringBuilder( + "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' + + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); - final EntryComment[] comments = entry.getComments(); + if (entry.getCommentsCount() > 0) { + buff.append(" <br/><br/>"); - for (int j = 0; j < comments.length; j++) { - comment = comments[j]; + final EntryComment[] comments = entry.getComments(); - if (j > 0) { - buff.append(" <br/>"); + for (int j = 0; j < comments.length; j++) { + comment = comments[j]; + + if (j > 0) { + buff.append(" <br/>"); + } + + buff.append(comment.getNick()).append(": ").append(comment.getComment()); } - - buff.append(comment.getNick()).append(": ").append(comment.getComment()); } + + item = new SyndEntryImpl(); + item.setLink(entry.getLink()); + description = new SyndContentImpl(); + description.setValue(buff.toString()); + item.setDescription(description); + item.setTitle(entry.getTitle()); + item.setPublishedDate(entry.getDate()); + item.setAuthor( + bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); + item.setCategories(entry.getTags()); + + items.add(item); } - item = new SyndEntryImpl(); - item.setLink(entry.getLink()); - description = new SyndContentImpl(); - description.setValue(buff.toString()); - item.setDescription(description); - item.setTitle(entry.getTitle()); - item.setPublishedDate(entry.getDate()); - item.setAuthor( - bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); - item.setCategories(entry.getTags()); + rss.setEntries(items); - items.add(item); + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Writing the entries feed."); + } + + output.output(rss, fw); } - rss.setEntries(items); - - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Writing the entries feed."); - } - - final SyndFeedOutput output = new SyndFeedOutput(); - output.output(rss, fw); - fw.close(); - - fw = new OutputStreamWriter( + try (final Writer fw = new OutputStreamWriter( new FileOutputStream( - bot.getLogsDir() + bot.getToday() + XML_EXT), StandardCharsets.UTF_8); - output.output(rss, fw); + bot.getLogsDir() + bot.getToday() + XML_EXT), StandardCharsets.UTF_8)) { + output.output(rss, fw); + } if (isDayBackup) { if (Utils.isValidString(bot.getBacklogsUrl())) { @@ -266,54 +263,46 @@ final class EntriesMgr { history.remove(0); } } + + try (final Writer fw = new OutputStreamWriter( + new FileOutputStream(bot.getLogsDir() + NAV_XML), StandardCharsets.UTF_8)) { + rss = new SyndFeedImpl(); + rss.setFeedType("rss_2.0"); + rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); + rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " + bot.getChannel()); + rss.setLink(bot.getBacklogsUrl()); + rss.setPublishedDate(Calendar.getInstance().getTime()); - fw.close(); - fw = new OutputStreamWriter( - new FileOutputStream(bot.getLogsDir() + NAV_XML), StandardCharsets.UTF_8); - rss = new SyndFeedImpl(); - rss.setFeedType("rss_2.0"); - rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); - rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " + bot.getChannel()); - rss.setLink(bot.getBacklogsUrl()); - rss.setPublishedDate(Calendar.getInstance().getTime()); + String date; + items.clear(); - String date; - items.clear(); + for (int i = (history.size() - 1); i >= 0; --i) { + date = history.get(i); - for (int i = (history.size() - 1); i >= 0; --i) { - date = history.get(i); + item = new SyndEntryImpl(); + item.setLink(bot.getBacklogsUrl() + date + ".xml"); + item.setTitle(date); + description = new SyndContentImpl(); + description.setValue("Links for " + date); + item.setDescription(description); - item = new SyndEntryImpl(); - item.setLink(bot.getBacklogsUrl() + date + ".xml"); - item.setTitle(date); - description = new SyndContentImpl(); - description.setValue("Links for " + date); - item.setDescription(description); + items.add(item); + } - items.add(item); + rss.setEntries(items); + + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Writing the backlog feed."); + } + + output.output(rss, fw); } - - rss.setEntries(items); - - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Writing the backlog feed."); - } - - output.output(rss, fw); } else { bot.getLogger().warn("Unable to generate the backlogs feed. No property configured."); } } } catch (FeedException | IOException e) { bot.getLogger().warn("Unable to generate the entries feed.", e); - } finally { - try { - if (fw != null) { - fw.close(); - } - } catch (Exception ignore) { - ; // Do nothing - } } } else { bot.getLogger() diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index 32ec725..a148068 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -67,7 +67,7 @@ public class EntryComment implements Serializable { */ @SuppressWarnings("UnusedDeclaration") protected EntryComment() { - ; // Required for serialization. + // Required for serialization. } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index ab2ecc9..114248d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -266,14 +266,14 @@ public class EntryLink implements Serializable { * @return The tags as a comma-delimited string. */ public final String getPinboardTags() { - final StringBuilder tags = new StringBuilder(nick); + final StringBuilder pinboardTags = new StringBuilder(nick); - for (final Object tag : this.tags) { - tags.append(','); - tags.append(((SyndCategoryImpl) tag).getName()); + for (final SyndCategory tag : tags) { + pinboardTags.append(','); + pinboardTags.append(tag.getName()); } - return tags.toString(); + return pinboardTags.toString(); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 1ba630d..56bc199 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -57,6 +57,7 @@ import java.util.*; * @created Jan 31, 2004 * @since 1.0 */ +@SuppressWarnings("WeakerAccess") @Version(properties = "version.properties", className = "ReleaseInfo") public class Mobibot extends PircBot { /** @@ -182,7 +183,6 @@ public class Mobibot extends PircBot { * @param logsDirPath The path to the logs directory. * @param p The bot's properties. */ - @SuppressWarnings("WeakerAccess") public Mobibot(final String nickname, final String channel, final String logsDirPath, final Properties p) { System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); @@ -210,7 +210,7 @@ public class Mobibot extends PircBot { today = Utils.today(); } } catch (IOException ignore) { - ; // Do nothing. + // Do nothing. } catch (FeedException e) { logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); } @@ -219,7 +219,7 @@ public class Mobibot extends PircBot { try { EntriesMgr.loadBacklogs(logsDir + EntriesMgr.NAV_XML, history); } catch (IOException ignore) { - ; // Do nothing. + // Do nothing. } catch (FeedException e) { logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); } @@ -335,30 +335,24 @@ public class Mobibot extends PircBot { // Redirect the stdout and stderr if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { - PrintStream stdout = null; - - try { - stdout = new PrintStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + try (final PrintStream stdout = new PrintStream(new FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true))) { + System.setOut(stdout); } catch (IOException e) { System.err.println("Unable to open output (stdout) log file."); e.printStackTrace(System.err); System.exit(1); } - PrintStream stderr = null; - try { - stderr = new PrintStream( - new FileOutputStream(logsDir + nickname + ".err", true)); + try (final PrintStream stderr = new PrintStream( + new FileOutputStream(logsDir + nickname + ".err", true))) { + System.setErr(stderr); } catch (IOException e) { System.err.println("Unable to open error (stderr) log file."); e.printStackTrace(System.err); System.exit(1); } - - System.setOut(stdout); - System.setErr(stderr); } // Create the bot @@ -378,7 +372,7 @@ public class Mobibot extends PircBot { try { Thread.sleep((long) (secs * 1000)); } catch (InterruptedException ignore) { - ; // Do nothing + // Do nothing. } } @@ -387,7 +381,7 @@ public class Mobibot extends PircBot { * * @param action The action. */ - final public void action(final String action) { + public final void action(final String action) { action(ircChannel, action); } @@ -580,7 +574,7 @@ public class Mobibot extends PircBot { * @param help The help string. * @return The indented help string. */ - final public String helpIndent(final String help) { + public final String helpIndent(final String help) { return helpIndent(help, true); } @@ -624,7 +618,7 @@ public class Mobibot extends PircBot { } else if (lcTopic.equals(Commands.VIEW_CMD)) { send(sender, "To list or search the current URL posts:"); send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); - } else if (lcTopic.equals(ircChannel.substring(1).toLowerCase())) { + } else if (lcTopic.equalsIgnoreCase(ircChannel.substring(1))) { send(sender, "To list the last 5 posts from the channel's weblog:"); send(sender, helpIndent(getNick() + ": " + ircChannel.substring(1))); } else if (lcTopic.equals(Commands.RECAP_CMD)) { @@ -1390,9 +1384,9 @@ public class Mobibot extends PircBot { * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void recapResponse(final String sender, final boolean isPrivate) { - if (recap.size() > 0) { - for (final String recap : recap) { - send(sender, recap, isPrivate); + if (recap.isEmpty()) { + for (final String r : recap) { + send(sender, r, isPrivate); } } else { send(sender, "Sorry, nothing to recap.", true); @@ -1574,7 +1568,7 @@ public class Mobibot extends PircBot { final int max = entries.size(); int i = 0; - if (!(lcArgs.length() > 0) && (max > MAX_ENTRIES)) { + if ((lcArgs.length() <= 0) && (max > MAX_ENTRIES)) { i = max - MAX_ENTRIES; } @@ -1598,7 +1592,7 @@ public class Mobibot extends PircBot { i = 0; } } catch (NumberFormatException ignore) { - ; // Do nothing + // Do nothing. } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index 89f6a4e..d330622 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -67,13 +67,13 @@ public class Tell { private static final String SER_EXT = ".ser"; // The bot instance. - final private Mobibot bot; + private final Mobibot bot; // The maximum number of days to keep messages. - final private int maxDays; + private final int maxDays; // The message maximum queue size. - final private int maxSize; + private final int maxSize; // The messages queue. private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); @@ -139,13 +139,14 @@ public class Tell { * @param cmds The commands string. */ public void response(final String sender, final String cmds) { + final String arrow = " --> "; if (!Utils.isValidString(cmds)) { helpResponse(sender); } else if (cmds.startsWith(Commands.VIEW_CMD)) { if (bot.isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD)) { - if (messages.size() > 0) { + if (messages.isEmpty()) { for (final TellMessage message : messages) { - bot.send(sender, Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', true); @@ -165,14 +166,14 @@ public class Tell { if (message.isReceived()) { bot.send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " + message.getId() + ", DELIVERED]", true); } else { bot.send(sender, - Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " + message.getId() + ", QUEUED]", true); diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 3318c19..1412206 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -47,7 +47,7 @@ public class TellMessage implements Serializable { private static final long serialVersionUID = 2L; private final String id; private final String message; - final private LocalDateTime queued; + private final LocalDateTime queued; private final String recipient; private final String sender; private boolean isNotified; @@ -176,4 +176,4 @@ public class TellMessage implements Serializable { received = LocalDateTime.now(Clock.systemUTC()); isReceived = true; } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 1397986..2b84b02 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -52,8 +52,7 @@ final class TellMessagesMgr { * * @throws UnsupportedOperationException If the constructor is called. */ - private TellMessagesMgr() - throws UnsupportedOperationException { + private TellMessagesMgr() { throw new UnsupportedOperationException("Illegal constructor call."); } @@ -98,7 +97,7 @@ final class TellMessagesMgr { return ((List<TellMessage>) input.readObject()); } } catch (FileNotFoundException ignore) { - ; // Do nothing. + // Do nothing. } catch (IOException e) { logger.error("An IO error occurred loading the messages queue.", e); } catch (Exception e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index ef9d181..1033a4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -46,14 +46,13 @@ import java.util.Date; * @created 2014-04-26 * @since 1.0 */ -final public class Utils { +public final class Utils { /** * Disables the default constructor. * * @throws UnsupportedOperationException If the constructor is called. */ - private Utils() - throws UnsupportedOperationException { + private Utils() { throw new UnsupportedOperationException("Illegal constructor call."); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 677dd5b..1306373 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -90,4 +90,4 @@ public class Calc extends AbstractModule { bot.send(sender, "To solve a mathematical calculation:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>")); } -} \ No newline at end of file +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 57a33cf..3498a87 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -55,7 +55,7 @@ import java.util.TreeMap; * @created Feb 11, 2004 * @since 1.0 */ -final public class CurrencyConverter extends AbstractModule { +public final class CurrencyConverter extends AbstractModule { /** * The currency command. */ @@ -73,7 +73,7 @@ final public class CurrencyConverter extends AbstractModule { private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; // The last exchange rates table publication date. - private static String pubDate = ""; + private String pubDate = ""; /** * Creates a new {@link CurrencyConverter} instance. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index b68b579..f8cc7df 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -43,7 +43,7 @@ import java.security.SecureRandom; * @created 2014-04-28 * @since 1.0 */ -final public class Dice extends AbstractModule { +public final class Dice extends AbstractModule { /** * The dice command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 9f4ca46..f0b705b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -51,7 +51,7 @@ import java.nio.charset.StandardCharsets; * @created Feb 7, 2004 * @since 1.0 */ -final public class GoogleSearch extends AbstractModule { +public final class GoogleSearch extends AbstractModule { /** * The google command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 3588f29..285f54c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -48,7 +48,7 @@ import java.nio.charset.StandardCharsets; * @created 2014-04-20 * @since 1.0 */ -final public class Joke extends AbstractModule { +public final class Joke extends AbstractModule { /** * The joke command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 89c9582..b2580a6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -45,7 +45,7 @@ import java.net.UnknownHostException; * @created 2014-04-26 * @since 1.0 */ -final public class Lookup extends AbstractModule { +public final class Lookup extends AbstractModule { /** * The lookup command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 8496c17..135373b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -46,7 +46,7 @@ import java.io.IOException; * @created Feb 7, 2004 * @since 1.0 */ -final public class StockQuote extends AbstractModule { +public final class StockQuote extends AbstractModule { /** * The quote command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index b63a88c..db7c0bb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -43,17 +43,17 @@ import twitter4j.conf.ConfigurationBuilder; * @created Sept 10, 2008 * @since 1.0 */ -final public class Twitter extends AbstractModule { +public final class Twitter extends AbstractModule { /** * The twitter command. */ - public final static String TWITTER_CMD = "twitter"; + public static final String TWITTER_CMD = "twitter"; // The property keys. - private final static String CONSUMER_KEY_PROP = "twitter-consumerKey"; - private final static String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; - private final static String TOKEN_PROP = "twitter-token"; - private final static String TOKEN_SECRET_PROP = "twitter-tokenSecret"; + private static final String CONSUMER_KEY_PROP = "twitter-consumerKey"; + private static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; + private static final String TOKEN_PROP = "twitter-token"; + private static final String TOKEN_SECRET_PROP = "twitter-tokenSecret"; /** * Creates a new {@link Twitter} instance. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 19c034a..4a063fd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -43,7 +43,7 @@ import java.security.SecureRandom; * @created 2014-04-28 * @since 1.0 */ -final public class War extends AbstractModule { +public final class War extends AbstractModule { /** * The war command */ @@ -95,7 +95,7 @@ final public class War extends AbstractModule { if (i < y) { bot.action("lost."); - } else if (i > y) { + } else { bot.action("wins."); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 02e680e..953ff7c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -47,7 +47,7 @@ import java.util.TreeMap; * @created 2014-04-27 * @since 1.0 */ -final public class WorldTime extends AbstractModule { +public final class WorldTime extends AbstractModule { /** * The time command. */ From 74b5c4fcc93f891b656770a7d7ed53176db442c8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 22:07:34 -0700 Subject: [PATCH 163/842] Changed to openjdk12. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4d45d00..68b2eaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: java jdk: - oraclejdk8 - - oraclejdk12 + - openjdk12 before_install: - chmod +x gradlew From c6766941a3df954d50cc6b859fa77aa8ef95688f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 22:09:52 -0700 Subject: [PATCH 164/842] Updated wrapper. --- gradle/wrapper/gradle-wrapper.jar | Bin 56177 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 18 +++++++++++++++++- gradlew.bat | 18 +++++++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 29953ea141f55e3b8fc691d31b5ca8816d89fa87..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch delta 47360 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%0DJLBj1VCKrB+2 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õw=Lh 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 delta 47987 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 P{tQ5B1&<&s7});<Y<b_6 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e0b3fb8..ea13fdf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d..b0d6d0a 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/gradlew.bat b/gradlew.bat index f955316..9991c50 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome From 05c043c3accf0e3758a91290c81a12e652991d15 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 22:24:13 -0700 Subject: [PATCH 165/842] Added sonarcloud. --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 68b2eaf..468cbaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,12 @@ language: java +dist: trusty + +addons: + sonarcloud: + organization: "ethauvin-github" + +script: + - sonar-scanner jdk: - oraclejdk8 From 13fbd3d7f2ca83b30602baada38109318042e907 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 22:42:10 -0700 Subject: [PATCH 166/842] Only execute sonar scanner once. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 468cbaa..3749cd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,10 @@ addons: organization: "ethauvin-github" script: - - sonar-scanner + - | + if [ "$TRAVIS_TEST_RESULT" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then + sonar-scanner + fi jdk: - oraclejdk8 From 61827e731ae7526d292f6574566bb5d977fb1687 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 1 Apr 2019 23:08:56 -0700 Subject: [PATCH 167/842] Execute sonar-scanner after install. --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3749cd1..ec078de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,15 +5,15 @@ addons: sonarcloud: organization: "ethauvin-github" -script: - - | - if [ "$TRAVIS_TEST_RESULT" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then - sonar-scanner - fi - jdk: - oraclejdk8 - openjdk12 before_install: - chmod +x gradlew + +after_install: + - | + if [ "$TRAVIS_TEST_RESULT" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then + sonar-scanner + fi From dfee57d411a0f96d24c8815cd8116bbe45ef7a5f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 2 Apr 2019 09:19:54 -0700 Subject: [PATCH 168/842] Call sonarqube after success. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec078de..ea74ce6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,8 @@ jdk: before_install: - chmod +x gradlew -after_install: +after_success: - | if [ "$TRAVIS_TEST_RESULT" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then - sonar-scanner + ./gradlew sonarqube -Dsonar.login=${SONAR_TOKEN} fi From ed81885af644aede5fba9e30106f783c52cc9f91 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 2 Apr 2019 09:38:02 -0700 Subject: [PATCH 169/842] Added sonar.organization. --- .travis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea74ce6..16e467c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,4 @@ language: java -dist: trusty - -addons: - sonarcloud: - organization: "ethauvin-github" jdk: - oraclejdk8 @@ -15,5 +10,5 @@ before_install: after_success: - | if [ "$TRAVIS_TEST_RESULT" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then - ./gradlew sonarqube -Dsonar.login=${SONAR_TOKEN} + ./gradlew sonarqube -Dsonar.login=${SONAR_TOKEN} -Dsonar.organization=${SONAR_GITHUB} fi From 2088a07ab534607ab8ae33df645054210185e0d7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 2 Apr 2019 09:47:10 -0700 Subject: [PATCH 170/842] Added check for pull request. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 16e467c..30cc287 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,6 @@ before_install: after_success: - | - if [ "$TRAVIS_TEST_RESULT" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then + if [ "${TRAVIS_PULL_REQUEST}" != "false" ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then ./gradlew sonarqube -Dsonar.login=${SONAR_TOKEN} -Dsonar.organization=${SONAR_GITHUB} fi From d86f84583c298458e7fa164a725d10a90573f3eb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 2 Apr 2019 10:35:52 -0700 Subject: [PATCH 171/842] Added GitHub token. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 30cc287..88d21c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,5 +10,8 @@ before_install: after_success: - | if [ "${TRAVIS_PULL_REQUEST}" != "false" ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then - ./gradlew sonarqube -Dsonar.login=${SONAR_TOKEN} -Dsonar.organization=${SONAR_GITHUB} + ./gradlew sonarqube \ + -Dsonar.github.pullRequest=${TRAVIS_PULL_REQUEST} \ + -Dsonar.github.oauth=${SONAR_GITHUB} \ + -Dsonar.login=${SONAR_TOKEN} \ fi From dcc21a5544ca59032ba7b5d84676831278db955c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 2 Apr 2019 10:43:52 -0700 Subject: [PATCH 172/842] Fixed unexpected end of file. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 88d21c2..f941888 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,5 +13,5 @@ after_success: ./gradlew sonarqube \ -Dsonar.github.pullRequest=${TRAVIS_PULL_REQUEST} \ -Dsonar.github.oauth=${SONAR_GITHUB} \ - -Dsonar.login=${SONAR_TOKEN} \ + -Dsonar.login=${SONAR_TOKEN} fi From b68048a2af5a7d0ee0f872189d837f74d218ffef Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 2 Apr 2019 11:20:29 -0700 Subject: [PATCH 173/842] Added sonar.projectKey to build.grade. --- .travis.yml | 11 ++++++----- build.gradle | 7 +++++-- sonar-project.properties | 2 -- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index f941888..d1a5740 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,9 @@ language: java +addons: + sonarcloud: + organization: "ethauvin-github" + jdk: - oraclejdk8 - openjdk12 @@ -9,9 +13,6 @@ before_install: after_success: - | - if [ "${TRAVIS_PULL_REQUEST}" != "false" ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then - ./gradlew sonarqube \ - -Dsonar.github.pullRequest=${TRAVIS_PULL_REQUEST} \ - -Dsonar.github.oauth=${SONAR_GITHUB} \ - -Dsonar.login=${SONAR_TOKEN} + if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then + ./gradlew sonarqube fi diff --git a/build.gradle b/build.gradle index 4cffbef..1f3548d 100644 --- a/build.gradle +++ b/build.gradle @@ -69,6 +69,7 @@ test { } compileJava { + dependsOn('incrementBuildMeta') options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' } @@ -99,8 +100,10 @@ incrementBuildMeta { } } -compileJava { - dependsOn('incrementBuildMeta') +sonarqube { + properties { + property "sonar.projectKey", "ethauvin_mobibot" + } } tasks.withType(SpotBugsTask) { diff --git a/sonar-project.properties b/sonar-project.properties index 27314ec..e69de29 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,2 +0,0 @@ -sonar.projectKey=ethauvin_semver -sonar.sourceEncoding=UTF-8 From fbcad6e61193dda268166a13bd4a5f09a3060b6f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 3 Apr 2019 11:31:12 -0700 Subject: [PATCH 174/842] Removed kobalt. --- kobalt/src/Build.kt | 145 ----------------------- kobalt/wrapper/kobalt-wrapper.jar | Bin 11264 -> 0 bytes kobalt/wrapper/kobalt-wrapper.properties | 1 - kobaltw | 2 - kobaltw.bat | 4 - pom.xml | 95 --------------- 6 files changed, 247 deletions(-) delete mode 100644 kobalt/src/Build.kt delete mode 100644 kobalt/wrapper/kobalt-wrapper.jar delete mode 100644 kobalt/wrapper/kobalt-wrapper.properties delete mode 100755 kobaltw delete mode 100644 kobaltw.bat delete mode 100644 pom.xml diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt deleted file mode 100644 index 470b118..0000000 --- a/kobalt/src/Build.kt +++ /dev/null @@ -1,145 +0,0 @@ -import com.beust.kobalt.* -import com.beust.kobalt.api.Project -import com.beust.kobalt.api.annotation.Task -import com.beust.kobalt.misc.KobaltLogger -import com.beust.kobalt.misc.log -import com.beust.kobalt.plugin.application.application -import com.beust.kobalt.plugin.apt.apt -import com.beust.kobalt.plugin.java.javadoc -import com.beust.kobalt.plugin.packaging.assemble -import com.beust.kobalt.plugin.packaging.install -import com.beust.kobalt.plugin.publish.autoGitTag -import net.thauvin.erik.kobalt.plugin.pom2xml.pom2xml -import java.io.File -import java.io.FileInputStream -import java.util.* - -val bs = buildScript { - repos(localMaven()) - plugins("net.thauvin.erik:kobalt-pom2xml:") -} - -val mainClassName = "net.thauvin.erik.mobibot.Mobibot" -val deploy = "deploy" - -fun StringBuilder.prepend(s: String): StringBuilder { - if (this.isNotEmpty()) { - this.insert(0, s) - } - return this -} - -val p = project { - - name = "mobibot" - - fun versionFor(): String { - val propsFile = "version.properties" - val majorKey = "version.major" - val minorKey = "version.minor" - val patchKey = "version.patch" - val metaKey = "version.buildmeta" - val preKey = "version.prerelease" - - val p = Properties().apply { FileInputStream(propsFile).use { fis -> load(fis) } } - - return (p.getProperty(majorKey, "1") + "." + p.getProperty(minorKey, "0") + "." + p.getProperty(patchKey, "0") - + StringBuilder(p.getProperty(preKey, "")).prepend("-") - + StringBuilder(p.getProperty(metaKey, "")).prepend("+")) - } - - version = versionFor() - - val processorJar = "net.thauvin.erik:semver:1.0.1" - val lib = "lib" - - dependencies { - compile("pircbot:pircbot:1.5.0") - //compileOnly("pircbot:pircbot::sources:1.5.0") - - compile("org.apache.logging.log4j:log4j-api:2.11.0", - "org.apache.logging.log4j:log4j-core:2.11.0", - "org.apache.logging.log4j:log4j-slf4j-impl:jar:2.11.0") - - compile("commons-cli:commons-cli:1.4", "commons-net:commons-net:3.6") - compile("com.squareup.okhttp3:okhttp:3.11.0") - - compile("com.rometools:rome:1.11.0") - - compile("org.json:json:20180130") - compile("org.ostermiller:utils:1.07.00") - compile("org.jsoup:jsoup:1.11.3") - compile("net.objecthunter:exp4j:0.4.8") - - compile("org.twitter4j:twitter4j-core:4.0.6") - compile("net.thauvin.erik:pinboard-poster:1.0.0") - - compile("net.aksingh:owm-japis:2.5.2.2") - - apt(processorJar) - compileOnly(processorJar) - - compileOnly("com.github.spotbugs:spotbugs-annotations:3.1.5") - } - - dependenciesTest { - compile("org.testng:testng:6.14.3") - compile("org.assertj:assertj-core:3.10.0") - } - - apt { - outputDir = "../src/generated/java/" - } - - autoGitTag { - enabled = true - message = "Version $version" - annotated = true - } - - assemble { - jar { - name = "${project.name}.jar" - manifest { - attributes("Main-Class", mainClassName) - attributes("Class-Path", - collect(compileDependencies) - .map { it.file.name } - .joinToString(" ./$lib/", prefix = ". ./$lib/")) - } - } - } - - application { - mainClass = mainClassName - args("-v") - } - - install { - target = deploy - include(from("kobaltBuild/libs"), to(target), glob("**/*")) - include(from("properties"), to(target), glob("**/*")) - collect(compileDependencies) - .forEach { - copy(from(it.file.absolutePath), to("$target/$lib")) - } - } - - javadoc { - title = project.name + ' ' + project.version - tags("created") - author = true - links("http://www.jibble.org/javadocs/pircbot/", "http://docs.oracle.com/javase/8/docs/api/") - } - - pom2xml { - - } -} - -@Task(name = "deploy", dependsOn = arrayOf("assemble", "install"), description = "Deploy application") -fun deploy(project: Project): TaskResult { - File("$deploy/logs").mkdir() - KobaltLogger.log(1, " Deployed to " + File(deploy).absolutePath) - return TaskResult() -} diff --git a/kobalt/wrapper/kobalt-wrapper.jar b/kobalt/wrapper/kobalt-wrapper.jar deleted file mode 100644 index 3acc29a5b6afe2235fb862f2b7f728cc733c8c7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11264 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 AE&u=k diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties deleted file mode 100644 index f16e1df..0000000 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -kobalt.version=1.0.118 \ No newline at end of file diff --git a/kobaltw b/kobaltw deleted file mode 100755 index c5186d5..0000000 --- a/kobaltw +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env sh -java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $* diff --git a/kobaltw.bat b/kobaltw.bat deleted file mode 100644 index d578071..0000000 --- a/kobaltw.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -java -jar "%DIRNAME%/kobalt/wrapper/kobalt-wrapper.jar" %* diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 578a20b..0000000 --- a/pom.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <version>0.7.2-beta+030</version> - <name>mobibot</name> - <description></description> - <dependencies> - <dependency> - <groupId>pircbot</groupId> - <artifactId>pircbot</artifactId> - <version>1.5.0</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - <version>2.10.0</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - <version>2.10.0</version> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j-impl</artifactId> - <version>2.10.0</version> - </dependency> - <dependency> - <groupId>commons-cli</groupId> - <artifactId>commons-cli</artifactId> - <version>1.4</version> - </dependency> - <dependency> - <groupId>commons-net</groupId> - <artifactId>commons-net</artifactId> - <version>3.6</version> - </dependency> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>okhttp</artifactId> - <version>3.9.1</version> - </dependency> - <dependency> - <groupId>com.rometools</groupId> - <artifactId>rome</artifactId> - <version>1.9.0</version> - </dependency> - <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - <version>20171018</version> - </dependency> - <dependency> - <groupId>org.ostermiller</groupId> - <artifactId>utils</artifactId> - <version>1.07.00</version> - </dependency> - <dependency> - <groupId>org.jsoup</groupId> - <artifactId>jsoup</artifactId> - <version>1.11.2</version> - </dependency> - <dependency> - <groupId>net.objecthunter</groupId> - <artifactId>exp4j</artifactId> - <version>0.4.8</version> - </dependency> - <dependency> - <groupId>org.twitter4j</groupId> - <artifactId>twitter4j-core</artifactId> - <version>4.0.6</version> - </dependency> - <dependency> - <groupId>net.thauvin.erik</groupId> - <artifactId>pinboard-poster</artifactId> - <version>0.9.3</version> - </dependency> - <dependency> - <systemPath>K:\java\mobibot\.\lib\owm-japis-2.5.0.5.jar</systemPath> - </dependency> - <dependency> - <groupId>org.testng</groupId> - <artifactId>testng</artifactId> - <version>6.13.1</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <version>3.9.0</version> - <scope>test</scope> - </dependency> - </dependencies> -</project> From d4e9121ca60c9a89167597305e7a9eaebdd048b5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 3 Apr 2019 13:34:13 -0700 Subject: [PATCH 175/842] Added ThreadedModule abstract class. --- .../erik/mobibot/modules/GoogleSearch.java | 26 +------ .../erik/mobibot/modules/ThreadedModule.java | 68 +++++++++++++++++++ .../thauvin/erik/mobibot/modules/Twitter.java | 25 +------ 3 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index f0b705b..6e3271a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -51,7 +51,7 @@ import java.nio.charset.StandardCharsets; * @created Feb 7, 2004 * @since 1.0 */ -public final class GoogleSearch extends AbstractModule { +public final class GoogleSearch extends ThreadedModule { /** * The google command. */ @@ -74,18 +74,6 @@ public final class GoogleSearch extends AbstractModule { properties.put(GOOGLE_CSE_KEY_PROP, ""); } - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - if (isEnabled() && args.length() > 0) { - new Thread(() -> run(bot, sender, args)).start(); - } else { - helpResponse(bot, sender, args, isPrivate); - } - } - /** * {@inheritDoc} */ @@ -98,20 +86,12 @@ public final class GoogleSearch extends AbstractModule { bot.send(sender, "The Google searching facility is disabled."); } } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return isValidProperties(); - } - + /** * Searches Google. */ @SuppressFBWarnings(value = {"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) - private void run(final Mobibot bot, final String sender, final String query) { + void run(final Mobibot bot, final String sender, final String query) { try { final String q = URLEncoder.encode(query, "UTF-8"); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java new file mode 100644 index 0000000..85b3e3d --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java @@ -0,0 +1,68 @@ +/* + * ThreadedModule.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.Mobibot; + +/** + * The <code>ThreadedModule</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-03 + * @since 1.0 + */ +public abstract class ThreadedModule extends AbstractModule { + /** + * {@inheritDoc} + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (isEnabled() && args.length() > 0) { + new Thread(() -> run(bot, sender, args)).start(); + } else { + helpResponse(bot, sender, args, isPrivate); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEnabled() { + return isValidProperties(); + } + + /** + * Run the thread. + */ + abstract void run(Mobibot bot, String sender, String args); +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index db7c0bb..42c70bc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -43,7 +43,7 @@ import twitter4j.conf.ConfigurationBuilder; * @created Sept 10, 2008 * @since 1.0 */ -public final class Twitter extends AbstractModule { +public final class Twitter extends ThreadedModule { /** * The twitter command. */ @@ -66,18 +66,6 @@ public final class Twitter extends AbstractModule { properties.put(TOKEN_SECRET_PROP, ""); } - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - if (isEnabled() && args.length() > 0) { - new Thread(() -> run(bot, sender, args)).start(); - } else { - helpResponse(bot, sender, args, isPrivate); - } - } - /** * {@inheritDoc} */ @@ -91,18 +79,11 @@ public final class Twitter extends AbstractModule { } } - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return isValidProperties(); - } - /** * Posts to twitter. */ - private void run(final Mobibot bot, final String sender, final String message) { + @Override + void run(final Mobibot bot, final String sender, final String message) { try { final ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setDebugEnabled(true) From cb70995f08e0b9c5460e00a2590b88b9f9eeebae Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 3 Apr 2019 13:36:20 -0700 Subject: [PATCH 176/842] Added sonarcloud, jacoco and now using a fatjar. --- build.gradle | 28 ++++++++++++++++------------ sonar-project.properties | 0 2 files changed, 16 insertions(+), 12 deletions(-) delete mode 100644 sonar-project.properties diff --git a/build.gradle b/build.gradle index 1f3548d..536138c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,13 @@ plugins { - id 'java' - id 'idea' id 'application' + id 'idea' + id 'jacoco' + id 'java' id "com.github.ben-manes.versions" version "0.21.0" - id "net.thauvin.erik.gradle.semver" version "0.9.9-beta" id "com.github.spotbugs" version "1.7.1" + id "net.thauvin.erik.gradle.semver" version "0.9.9-beta" id "org.sonarqube" version "2.7" + id "com.github.johnrengelman.shadow" version "5.0.0" } @@ -80,9 +82,11 @@ javadoc { } jar { - manifest.attributes('Main-Class': mainClassName, - 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) + manifest.attributes('Main-Class': mainClassName) +} +shadowJar { + classifier = null version = null } @@ -106,6 +110,12 @@ sonarqube { } } +jacocoTestReport { + reports { + html.enabled true + } +} + tasks.withType(SpotBugsTask) { reports { xml.enabled = false @@ -116,21 +126,15 @@ tasks.withType(SpotBugsTask) { task copyToDeploy(type: Copy) { from('properties') - from jar + from shadowJar into deployDir } -task copyToDeployLib(type: Copy) { - from configurations.compile - into deployDir + '/lib' -} - task deploy(dependsOn: ['build']) { description = 'Copies all needed files to the ${deployDir} directory.' group = 'Publishing' outputs.dir deployDir inputs.files copyToDeploy - inputs.files copyToDeployLib doLast { file(deployDir + '/logs').mkdir() } diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index e69de29..0000000 From 2918c48a3ec6a3b9ee478d0005c02c5162580d6c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 3 Apr 2019 13:43:29 -0700 Subject: [PATCH 177/842] Removed kobalt. --- .circleci/config.yml | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1ba296d..d13f709 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,44 +46,10 @@ jobs: <<: *defaults_gradle - build_kobalt: - <<: *defaults - - docker: - - image: circleci/openjdk:8-jdk - - steps: - - checkout - - restore_cache: - keys: - - kobalt-dependencies-{{ checksum "kobalt/src/Build.kt" }} - # fallback to using the latest cache if no exact match is found - - kobalt-dependencies- - - - run: - name: Check Versions - command: ./kobaltw checkVersions - - - save_cache: - paths: ~/.kobalt - key: kobalt-dependencies-{{ checksum "kobalt/src/Build.kt" }} - - - run: - name: Assemble & Test - command: ./kobaltw assemble test - - - store_artifacts: - path: kobaltBuild/test-output/ - destination: test-output - - store_test_results: - path: kobaltBuild/test-output/ - workflows: version: 2 gradle: jobs: - build_gradle_jdk8 - build_gradle_jdk12 - kobalt: - jobs: - - build_kobalt + From 53f0f7df8c07775b742bbd5bd6197db16eae137a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 3 Apr 2019 23:34:06 -0700 Subject: [PATCH 178/842] Fixed jacoco reports. --- build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index 536138c..9884175 100644 --- a/build.gradle +++ b/build.gradle @@ -113,6 +113,7 @@ sonarqube { jacocoTestReport { reports { html.enabled true + xml.enabled = true } } @@ -145,3 +146,7 @@ task release(dependsOn: ['wrapper', 'clean', 'deploy']) { group = 'Publishing' description = 'Releases new version.' } + +tasks.sonarqube { + dependsOn("jacocoTestReport") +} From be94c6ebbf76508a99acacc6288b840f72a5ef97 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 5 Apr 2019 22:01:28 -0700 Subject: [PATCH 179/842] Updated licenses. --- licenses/Apache License.txt | 3 +-- licenses/JDOM License.txt | 30 +++++++++++------------ licenses/LICENSE.txt | 4 ++-- licenses/OWM JAPIs License.txt | 4 ++-- licenses/OstermillerUtil License.txt | 21 +++++++++------- licenses/SLF4J License.txt | 36 ++++++++++++++-------------- licenses/jsoup License.txt | 10 ++++---- 7 files changed, 55 insertions(+), 53 deletions(-) diff --git a/licenses/Apache License.txt b/licenses/Apache License.txt index 7a4a3ea..261eeb9 100644 --- a/licenses/Apache License.txt +++ b/licenses/Apache License.txt @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -199,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/licenses/JDOM License.txt b/licenses/JDOM License.txt index abf4678..660cedb 100644 --- a/licenses/JDOM License.txt +++ b/licenses/JDOM License.txt @@ -1,34 +1,34 @@ -/*-- +/*-- Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. - + 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions, and the disclaimer that follows - these conditions in the documentation and/or other materials + notice, this list of conditions, and the disclaimer that follows + these conditions in the documentation and/or other materials provided with the distribution. 3. The name "JDOM" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact <request_AT_jdom_DOT_org>. - + 4. Products derived from this software may not be called "JDOM", nor may "JDOM" appear in their name, without prior written permission from the JDOM Project Management <request_AT_jdom_DOT_org>. - - In addition, we request (but do not require) that you include in the - end-user documentation provided with the redistribution and/or in the + + In addition, we request (but do not require) that you include in the + end-user documentation provided with the redistribution and/or in the software itself an acknowledgement equivalent to the following: "This product includes software developed by the JDOM Project (http://www.jdom.org/)." - Alternatively, the acknowledgment may be graphical using the logos + Alternatively, the acknowledgment may be graphical using the logos available at http://www.jdom.org/images/logos. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED @@ -44,10 +44,10 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - This software consists of voluntary contributions made by many - individuals on behalf of the JDOM Project and was originally + This software consists of voluntary contributions made by many + individuals on behalf of the JDOM Project and was originally created by Jason Hunter <jhunter_AT_jdom_DOT_org> and Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information - on the JDOM Project, please see <http://www.jdom.org/>. + on the JDOM Project, please see <http://www.jdom.org/>. - */ \ No newline at end of file + */ diff --git a/licenses/LICENSE.txt b/licenses/LICENSE.txt index 9690435..4efe703 100644 --- a/licenses/LICENSE.txt +++ b/licenses/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2017, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without @@ -24,4 +24,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/OWM JAPIs License.txt b/licenses/OWM JAPIs License.txt index 24a127e..cf80d7d 100644 --- a/licenses/OWM JAPIs License.txt +++ b/licenses/OWM JAPIs License.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013-2015 Ashutosh Kumar Singh <me@aksingh.net> +Copyright (c) 2013- Ashutosh Kumar Singh <ashutosh@aksingh.net> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/licenses/OstermillerUtil License.txt b/licenses/OstermillerUtil License.txt index 122e443..7b9549c 100644 --- a/licenses/OstermillerUtil License.txt +++ b/licenses/OstermillerUtil License.txt @@ -1,13 +1,16 @@ License (http://ostermiller.org/utils/) -OstermillerUtil Java Utilities Copyright (c) 2001-2011 by Stephen Ostermiller -and other contributors +Copyright (C) 2004-2010 Stephen Ostermiller +http://ostermiller.org/contact.pl?regarding=Java+Utilities -The OstermillerUtils library is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2 of the License or (at your option) -any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. -This program is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -PARTICULAR PURPOSE. See the GNU General Public License for more details. \ No newline at end of file +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +See the GNU General Public License (https://ostermiller.org/utils/license.html) for more details. diff --git a/licenses/SLF4J License.txt b/licenses/SLF4J License.txt index e106b98..744377c 100644 --- a/licenses/SLF4J License.txt +++ b/licenses/SLF4J License.txt @@ -1,21 +1,21 @@ - Copyright (c) 2004-2017 QOS.ch - All rights reserved. +Copyright (c) 2004-2017 QOS.ch +All rights reserved. - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/jsoup License.txt b/licenses/jsoup License.txt index d5d19a2..4d95827 100644 --- a/licenses/jsoup License.txt +++ b/licenses/jsoup License.txt @@ -1,6 +1,6 @@ The MIT License -© 2009-2016, Jonathan Hedley <jonathan@hedley.net> +Copyright (c) 2009-2018 Jonathan Hedley <jonathan@hedley.net> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 95899c2840dd547f172858806ef2e3899a46b057 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 00:22:58 -0700 Subject: [PATCH 180/842] Reverted to using a lib directory, the fatjar was causing problems with log4j not finding the configuration file. --- build.gradle | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index 9884175..48760c0 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,6 @@ plugins { id "com.github.spotbugs" version "1.7.1" id "net.thauvin.erik.gradle.semver" version "0.9.9-beta" id "org.sonarqube" version "2.7" - id "com.github.johnrengelman.shadow" version "5.0.0" } @@ -35,29 +34,29 @@ dependencies { annotationProcessor semverProcessor compileOnly semverProcessor - implementation 'pircbot:pircbot:1.5.0' + compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - implementation 'org.apache.logging.log4j:log4j-api:2.11.2' - implementation 'org.apache.logging.log4j:log4j-core:2.11.2' - implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.2' + compile 'org.apache.logging.log4j:log4j-api:2.11.2' + compile 'org.apache.logging.log4j:log4j-core:2.11.2' + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.2' - implementation 'commons-cli:commons-cli:1.4' + compile 'commons-cli:commons-cli:1.4' - implementation 'commons-net:commons-net:3.6' - implementation 'com.squareup.okhttp3:okhttp:3.14.0' + compile 'commons-net:commons-net:3.6' + compile 'com.squareup.okhttp3:okhttp:3.14.0' - implementation 'com.rometools:rome:1.12.0' + compile 'com.rometools:rome:1.12.0' - implementation 'org.json:json:20180813' - implementation 'org.ostermiller:utils:1.07.00' - implementation 'org.jsoup:jsoup:1.11.3' - implementation 'net.objecthunter:exp4j:0.4.8' + compile 'org.json:json:20180813' + compile 'org.ostermiller:utils:1.07.00' + compile 'org.jsoup:jsoup:1.11.3' + compile 'net.objecthunter:exp4j:0.4.8' - implementation 'org.twitter4j:twitter4j-core:4.0.7' - implementation 'net.thauvin.erik:pinboard-poster:1.0.0' + compile 'org.twitter4j:twitter4j-core:4.0.7' + compile 'net.thauvin.erik:pinboard-poster:1.0.0' - implementation 'net.aksingh:owm-japis:2.5.2.3' + compile 'net.aksingh:owm-japis:2.5.2.3' testImplementation 'org.testng:testng:6.14.3' testImplementation 'org.assertj:assertj-core:3.12.2' @@ -82,11 +81,9 @@ javadoc { } jar { - manifest.attributes('Main-Class': mainClassName) -} + manifest.attributes('Main-Class': mainClassName, + 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) -shadowJar { - classifier = null version = null } @@ -127,15 +124,21 @@ tasks.withType(SpotBugsTask) { task copyToDeploy(type: Copy) { from('properties') - from shadowJar + from jar into deployDir } +task copyToDeployLib(type: Copy) { + from configurations.compile + into deployDir + '/lib' +} + task deploy(dependsOn: ['build']) { description = 'Copies all needed files to the ${deployDir} directory.' group = 'Publishing' outputs.dir deployDir inputs.files copyToDeploy + inputs.files copyToDeployLib doLast { file(deployDir + '/logs').mkdir() } From 398f4559cfea8a38fb4a20b2536bdeb2e8b8bdee Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 00:23:48 -0700 Subject: [PATCH 181/842] Reverted to the old way of redirecting stderr and stdout. The try-resource method was closing the stream. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 56bc199..36dd86d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -335,8 +335,9 @@ public class Mobibot extends PircBot { // Redirect the stdout and stderr if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { - try (final PrintStream stdout = new PrintStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true))) { + try { + final PrintStream stdout = new PrintStream(new FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); System.setOut(stdout); } catch (IOException e) { System.err.println("Unable to open output (stdout) log file."); @@ -344,9 +345,9 @@ public class Mobibot extends PircBot { System.exit(1); } - - try (final PrintStream stderr = new PrintStream( - new FileOutputStream(logsDir + nickname + ".err", true))) { + try { + final PrintStream stderr = new PrintStream( + new FileOutputStream(logsDir + nickname + ".err", true)); System.setErr(stderr); } catch (IOException e) { System.err.println("Unable to open error (stderr) log file."); From 5d42c3eb3f13ef841eb97f4e0b17bfd635e200f3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 00:24:21 -0700 Subject: [PATCH 182/842] Fixed recap. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 36dd86d..3a47c9e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1385,7 +1385,7 @@ public class Mobibot extends PircBot { * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void recapResponse(final String sender, final boolean isPrivate) { - if (recap.isEmpty()) { + if (!recap.isEmpty()) { for (final String r : recap) { send(sender, r, isPrivate); } From ecfba4720ef4b6c69ccef029adea81bdfc6d31b8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 00:24:46 -0700 Subject: [PATCH 183/842] Added recap size to info response. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 3a47c9e..2352e86 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -844,7 +844,7 @@ public class Mobibot extends PircBot { info.append(", Messages: ").append(tell.size()); } - info.append(']'); + info.append(", Recap: ").append(recap.size()).append(']'); send(sender, info.toString(), isPrivate); } From d04ed1a6ab8233c1088692be1905a11fb3aafe3f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 01:05:26 -0700 Subject: [PATCH 184/842] Don't increase build meta on CIs. --- .circleci/config.yml | 1 + .travis.yml | 7 +++++++ build.gradle | 4 +++- version.properties | 4 ++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d13f709..b6d9624 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,6 +3,7 @@ defaults: &defaults environment: JVM_OPTS: -Xmx3200m TERM: dumb + CI: true defaults_gradle: &defaults_gradle steps: diff --git a/.travis.yml b/.travis.yml index d1a5740..018f0e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,12 @@ language: java +env: + global: + - CI=true + +install: + - git fetch --unshallow --tags + addons: sonarcloud: organization: "ethauvin-github" diff --git a/build.gradle b/build.gradle index 48760c0..fdde004 100644 --- a/build.gradle +++ b/build.gradle @@ -97,7 +97,9 @@ run { incrementBuildMeta { doFirst { - buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) + if (!System.getenv('CI')) { + buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) + } } } diff --git a/version.properties b/version.properties index 856b864..d5d23eb 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Wed Oct 31 15:39:54 PDT 2018 -version.buildmeta=023 +#Sun Apr 07 00:56:46 PDT 2019 +version.buildmeta=117 version.major=0 version.minor=7 version.patch=3 From 6fe36dbe8e102845c1f49636924b34320a546716 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 01:27:00 -0700 Subject: [PATCH 185/842] Updated copryright. --- LICENCE.txt | 4 ++-- src/main/java/net/thauvin/erik/mobibot/Commands.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntryComment.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntryLink.java | 2 +- src/main/java/net/thauvin/erik/mobibot/FeedReader.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Pinboard.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Tell.java | 2 +- src/main/java/net/thauvin/erik/mobibot/TellMessage.java | 2 +- src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java | 2 +- src/main/java/net/thauvin/erik/mobibot/Utils.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/AbstractModule.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Calc.java | 2 +- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Dice.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/GoogleSearch.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Joke.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Ping.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/StockQuote.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/War.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java | 2 +- src/test/java/net/thauvin/erik/mobibot/UtilsTest.java | 3 +-- .../java/net/thauvin/erik/mobibot/modules/LookupTest.java | 2 +- 27 files changed, 28 insertions(+), 29 deletions(-) diff --git a/LICENCE.txt b/LICENCE.txt index 91706eb..4efe703 100644 --- a/LICENCE.txt +++ b/LICENCE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without @@ -24,4 +24,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 14b642c..a6487a1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -1,7 +1,7 @@ /* * Commands.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 3fa4ddb..19756d8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -1,7 +1,7 @@ /* * EntriesMgr.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index a148068..d665179 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -1,7 +1,7 @@ /* * EntryComment.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 114248d..94395f9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -1,7 +1,7 @@ /* * EntryLink.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index c78f90a..e85f2c2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -1,7 +1,7 @@ /* * FeedReader.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 2352e86..d6f7fa4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1,7 +1,7 @@ /* * Mobibot.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 222567b..f8e9b61 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -1,7 +1,7 @@ /* * Pinboard.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index d330622..fd5b98d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -1,7 +1,7 @@ /* * Tell.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index 1412206..bb4c607 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -1,7 +1,7 @@ /* * TellMessage.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 2b84b02..eb46a1c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -1,7 +1,7 @@ /* * TellMessagesMgr.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 1033a4d..25de366 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -1,7 +1,7 @@ /* * Utils.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 70c2cf8..4a8908a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -1,7 +1,7 @@ /* * AbstractModule.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 1306373..7250408 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -1,7 +1,7 @@ /* * Calc.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 3498a87..49c7710 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -1,7 +1,7 @@ /* * CurrencyConverter.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index f8cc7df..ec37090 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -1,7 +1,7 @@ /* * Dice.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 6e3271a..48ab1b7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -1,7 +1,7 @@ /* * GoogleSearch.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 285f54c..53f2036 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -1,7 +1,7 @@ /* * Joke.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index b2580a6..da051c3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -1,7 +1,7 @@ /* * Lookup.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 697309b..2648dc1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -1,7 +1,7 @@ /* * Ping.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 135373b..55d9ab5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -1,7 +1,7 @@ /* * StockQuote.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 42c70bc..6a1dd9f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -1,7 +1,7 @@ /* * Twitter.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 4a063fd..71e606e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,7 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index bf75a43..a230c26 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -1,7 +1,7 @@ /* * Weather2.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 953ff7c..a656ddc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -1,7 +1,7 @@ /* * WorldTime.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index a710d3e..80f875b 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -1,7 +1,7 @@ /* * UtilsTest.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -61,7 +61,6 @@ public class UtilsTest { cal.set(1952, Calendar.FEBRUARY, 17, 12, 30, 0); } - @Test public void testBold() throws Exception { assertThat(Utils.bold(1)).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index 4b14261..a2bd651 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -1,7 +1,7 @@ /* * LookupTest.java * - * Copyright (c) 2004-2018, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without From 015c8fcd14e4a4c74b0ec5391431fe255d237c12 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 01:27:21 -0700 Subject: [PATCH 186/842] Print no increment on CIs. --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index fdde004..8dccb7a 100644 --- a/build.gradle +++ b/build.gradle @@ -99,6 +99,8 @@ incrementBuildMeta { doFirst { if (!System.getenv('CI')) { buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) + } else { + println "No increment on CIs." } } } From 4e211238207cc9d24c655e2d77db3fb34a55f1d3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 02:03:32 -0700 Subject: [PATCH 187/842] Added tests. --- .../thauvin/erik/mobibot/modules/Calc.java | 30 ++++++----- .../erik/mobibot/modules/CalcTest.java | 53 +++++++++++++++++++ 2 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 7250408..17c3ba9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -58,28 +58,34 @@ public class Calc extends AbstractModule { commands.add(CALC_CMD); } + /** + * Calculate. + * + * @param query The query. + * @return The calculation result. + */ + public static String calc(final String query) { + final DecimalFormat decimalFormat = new DecimalFormat("#.##"); + + try { + final Expression calc = new ExpressionBuilder(query).build(); + return query.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate()); + } catch (Exception e) { + return "No idea. This is the kind of math I don't get."; + } + } + /** * {@inheritDoc} */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (Utils.isValidString(args)) { - final DecimalFormat decimalFormat = new DecimalFormat("#.##"); + bot.send(bot.getChannel(), calc(args)); - try { - final Expression calc = new ExpressionBuilder(args).build(); - bot.send(bot.getChannel(), args.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate())); - } catch (Exception e) { - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Unable to calculate: " + args, e); - } - - bot.send(bot.getChannel(), "No idea. This is the kind of math I don't get."); - } } else { helpResponse(bot, sender, args, isPrivate); } - } /** diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java new file mode 100644 index 0000000..e8726c2 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java @@ -0,0 +1,53 @@ +/* + * CalcTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>CalcTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class CalcTest { + @Test + public void testCalc() { + assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = 2"); + assertThat(Calc.calc("1 -3")).as("calc(1 -3)").isEqualTo("1-3 = -2"); + assertThat(Calc.calc("pi+π+e+φ")).as("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = 10.62"); + assertThat(Calc.calc("one + one")).as("calc(one + one)").startsWith("No idea."); + } +} From 4e26666823ddc4eb047de9061c00dc27376999f2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 14:43:10 -0700 Subject: [PATCH 188/842] Added Message classes to facilitate testing modules. --- .../erik/mobibot/msg/ErrorMessage.java | 46 +++++++ .../net/thauvin/erik/mobibot/msg/Message.java | 118 ++++++++++++++++++ .../erik/mobibot/msg/PrivateMessage.java | 46 +++++++ .../erik/mobibot/msg/PublicMessage.java | 46 +++++++ 4 files changed, 256 insertions(+) create mode 100644 src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/msg/Message.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java new file mode 100644 index 0000000..bc42e6e --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java @@ -0,0 +1,46 @@ +/* + * ErrorMessage.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg; + +/** + * The <code>ErrorMessage</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class ErrorMessage extends Message { + public ErrorMessage(String message) { + this.setMessage(message); + this.setError(true); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java new file mode 100644 index 0000000..4217d37 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -0,0 +1,118 @@ +/* + * Message.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg; + +/** + * The <code>Message</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class Message { + private boolean isError; + private boolean isPrivate; + private String msg = ""; + + /** + * Creates a new message. + */ + public Message() { + + } + + /** + * Creates a new message. + * + * @param message The message. + * @param isPrivate The private flag. + */ + public Message(String message, boolean isPrivate, boolean isError) { + msg = message; + this.isPrivate = isPrivate; + this.isError = isError; + } + + /** + * Returns the message. + * + * @return The message. + */ + public String getMessage() { + return msg; + } + + /** + * Sets the message. + * + * @param message The new message. + */ + public void setMessage(String message) { + msg = message; + } + + /** + * Returns the error flag. + * + * @return The error flag. + */ + public boolean isError() { + return isError; + } + + /** + * Sets the error flag. + * + * @param error The error flag. + */ + public void setError(boolean error) { + isError = error; + } + + /** + * Returns the private message flag. + * + * @return The private flag. + */ + public boolean isPrivate() { + return isPrivate; + } + + /** + * Set the private message flag. + * + * @param isPrivate The private flag. + */ + public void setPrivate(boolean isPrivate) { + this.isPrivate = isPrivate; + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java new file mode 100644 index 0000000..45cdb4b --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java @@ -0,0 +1,46 @@ +/* + * PrivateMessage.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg; + +/** + * The <code>PrivateMessage</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class PrivateMessage extends Message { + public PrivateMessage(String message) { + this.setMessage(message); + this.setPrivate(true); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java new file mode 100644 index 0000000..5fcc1e5 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java @@ -0,0 +1,46 @@ +/* + * PublicMessage.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg; + +/** + * The <code>PublicMessage</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class PublicMessage extends Message { + public PublicMessage(String message) { + this.setMessage(message); + this.setPrivate(false); + } +} From e77ccf321109e8e91dfe22b83f2b1dc180c85a72 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 14:46:19 -0700 Subject: [PATCH 189/842] Added test for AbstractModule classes. --- .../erik/mobibot/modules/AbstractModule.java | 2 +- .../thauvin/erik/mobibot/modules/Calc.java | 4 +- .../thauvin/erik/mobibot/modules/Lookup.java | 12 ++-- .../mobibot/modules/AbstractModuleTest.java | 58 +++++++++++++++++++ .../erik/mobibot/modules/CalcTest.java | 5 ++ .../erik/mobibot/modules/LookupTest.java | 5 ++ 6 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 4a8908a..8abafaf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -123,7 +123,7 @@ public abstract class AbstractModule { * * @return <code>true</code> if the properties are valid, <code>false</code> otherwise. */ - public boolean isValidProperties() { + boolean isValidProperties() { for (final String s : getPropertyKeys()) { if (!Utils.isValidString(properties.get(s))) { return false; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 17c3ba9..2c2a513 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -49,7 +49,7 @@ public class Calc extends AbstractModule { /** * The Calc command. */ - public static final String CALC_CMD = "calc"; + private static final String CALC_CMD = "calc"; /** * The default constructor. @@ -64,7 +64,7 @@ public class Calc extends AbstractModule { * @param query The query. * @return The calculation result. */ - public static String calc(final String query) { + static String calc(final String query) { final DecimalFormat decimalFormat = new DecimalFormat("#.##"); try { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index da051c3..3f6d8e2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -46,15 +46,13 @@ import java.net.UnknownHostException; * @since 1.0 */ public final class Lookup extends AbstractModule { - /** - * The lookup command. - */ - public static final String LOOKUP_CMD = "lookup"; - /** * The whois default host. */ - public static final String WHOIS_HOST = "whois.arin.net"; + static final String WHOIS_HOST = "whois.arin.net"; + + // The lookup command. + private static final String LOOKUP_CMD = "lookup"; /** * The default constructor @@ -72,7 +70,7 @@ public final class Lookup extends AbstractModule { */ public static String lookup(final String query) throws UnknownHostException { - final StringBuilder buffer = new StringBuilder(""); + final StringBuilder buffer = new StringBuilder(); final InetAddress[] results = InetAddress.getAllByName(query); String hostInfo; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java new file mode 100644 index 0000000..2c2e109 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -0,0 +1,58 @@ +/* + * AbstractModuleTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>AbstractModuleTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +final class AbstractModuleTest { + static void testAbstractModule(AbstractModule module) { + final String name = module.getClass().getName(); + assertThat(module.isEnabled()).as(name + ": enabled").isTrue(); + assertThat(module.getCommands().size()).as(name + ": commands > 0").isGreaterThan(0); + if (!module.hasProperties()) { + assertThat(module.getPropertyKeys().size()).as(name + ": no properties").isEqualTo(0); + module.setProperty("test", "test"); + module.setProperty("", "invalid"); + } + assertThat(module.getPropertyKeys().size()).as(name + ": properties > 0").isGreaterThan(0); + assertThat(module.isValidProperties()).as(name + ": isValidProperties()").isTrue(); + module.setProperty("invalid", ""); + assertThat(module.isValidProperties()).as(name + ": invalid properties").isFalse(); + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java index e8726c2..b70450b 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java @@ -43,6 +43,11 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class CalcTest { + @Test + public void testCalcImpl() { + AbstractModuleTest.testAbstractModule(new Calc()); + } + @Test public void testCalc() { assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = 2"); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index a2bd651..bd85a7a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -45,6 +45,11 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class LookupTest { + @Test + public void testLookupImpl() { + AbstractModuleTest.testAbstractModule(new Lookup()); + } + @Test public void testLookup() throws Exception { final String result = Lookup.lookup("erik.thauvin.net"); From 03b932d50224c9808f9f6505edcd550a0aceaa57 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 14:46:45 -0700 Subject: [PATCH 190/842] Added tests. --- .../mobibot/modules/CurrencyConverter.java | 189 +++++++++--------- .../mobibot/modules/CurrencyConvertTest.java | 62 ++++++ 2 files changed, 158 insertions(+), 93 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 49c7710..3cd623c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -32,8 +32,11 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.*; +import net.thauvin.erik.mobibot.msg.ErrorMessage; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.PrivateMessage; +import net.thauvin.erik.mobibot.msg.PublicMessage; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; @@ -56,24 +59,22 @@ import java.util.TreeMap; * @since 1.0 */ public final class CurrencyConverter extends AbstractModule { - /** - * The currency command. - */ - public static final String CURRENCY_CMD = "currency"; - /** * The rates keyword. */ - public static final String CURRENCY_RATES_KEYWORD = "rates"; + static final String CURRENCY_RATES_KEYWORD = "rates"; + + // The currency command. + private static final String CURRENCY_CMD = "currency"; // The exchange rates. private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); // The exchange rates table URL. - private static final String EXCHANGE_TABLE_URL = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"; + private static final String EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"; // The last exchange rates table publication date. - private String pubDate = ""; + private static String pubDate = ""; /** * Creates a new {@link CurrencyConverter} instance. @@ -82,6 +83,85 @@ public final class CurrencyConverter extends AbstractModule { commands.add(CURRENCY_CMD); } + static Message converyCurrency(String query) { + if (EXCHANGE_RATES.isEmpty()) { + try { + final SAXBuilder builder = new SAXBuilder(); + builder.setIgnoringElementContentWhitespace(true); + + final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); + final Element root = doc.getRootElement(); + final Namespace ns = root.getNamespace(""); + final Element cubeRoot = root.getChild("Cube", ns); + final Element cubeTime = cubeRoot.getChild("Cube", ns); + + pubDate = cubeTime.getAttribute("time").getValue(); + + final List cubes = cubeTime.getChildren(); + Element cube; + + for (final Object rawCube : cubes) { + cube = (Element) rawCube; + EXCHANGE_RATES.put( + cube.getAttribute("currency").getValue(), + cube.getAttribute("rate").getValue()); + } + + EXCHANGE_RATES.put("EUR", "1"); + } catch (JDOMException e) { + return new ErrorMessage("An error has occurred while parsing the exchange rates table."); + } catch (IOException e) { + return new ErrorMessage( + "An error has occurred while fetching the exchange rates table: " + e.getMessage()); + } + } + + if (EXCHANGE_RATES.isEmpty()) { + return new ErrorMessage("Sorry, but the exchange rate table is empty."); + } else { + final String[] cmds = query.split(" "); + + if (cmds.length == 4) { + if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) { + return new ErrorMessage("You're kidding, right?"); + } else { + try { + final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); + final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); + final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); + + return new PublicMessage( + NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + + ' ' + + cmds[1].toUpperCase() + + " = " + + NumberFormat.getCurrencyInstance(Locale.US) + .format((amt * to) / from) + .substring(1) + + ' ' + + cmds[3].toUpperCase()); + } catch (NullPointerException ignored) { + return new ErrorMessage( + "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + } + } + } else if (query.equals(CURRENCY_RATES_KEYWORD)) { + + final StringBuilder buff = new StringBuilder('[' + pubDate + "]: "); + + int i = 0; + for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { + if (i > 0) buff.append(", "); + buff.append(rate.getKey()).append(": ").append(rate.getValue()); + i++; + } + + return new PrivateMessage(buff.toString()); + } + } + return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + } + /** * {@inheritDoc} */ @@ -117,92 +197,15 @@ public final class CurrencyConverter extends AbstractModule { */ @SuppressFBWarnings(value = "REDOS") private void run(final Mobibot bot, final String sender, final String query) { - if (Utils.isValidString(sender)) { - if (EXCHANGE_RATES.isEmpty()) { - try { - final SAXBuilder builder = new SAXBuilder(); - builder.setIgnoringElementContentWhitespace(true); - - final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); - final Element root = doc.getRootElement(); - final Namespace ns = root.getNamespace(""); - final Element cubeRoot = root.getChild("Cube", ns); - final Element cubeTime = cubeRoot.getChild("Cube", ns); - - pubDate = cubeTime.getAttribute("time").getValue(); - - final List cubes = cubeTime.getChildren(); - Element cube; - - for (final Object rawCube : cubes) { - cube = (Element) rawCube; - EXCHANGE_RATES.put( - cube.getAttribute("currency").getValue(), - cube.getAttribute("rate").getValue()); - } - - EXCHANGE_RATES.put("EUR", "1"); - } catch (JDOMException e) { - bot.getLogger().debug("Unable to parse the exchange rates table.", e); - bot.send(sender, "An error has occurred while parsing the exchange rates table."); - } catch (IOException e) { - bot.getLogger().debug("Unable to fetch the exchange rates table.", e); - bot.send(sender, - "An error has occurred while fetching the exchange rates table: " + e.getMessage()); - } - } - - if (EXCHANGE_RATES.isEmpty()) { - bot.getLogger().debug("The exchange rate table is empty."); - bot.send(sender, "Sorry, but the exchange rate table is empty."); - } else if (Utils.isValidString(query)) { - if (!EXCHANGE_RATES.isEmpty()) { - if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { - final String[] cmds = query.split(" "); - - if (cmds.length == 4) { - if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) { - bot.send(sender, "You're kidding, right?"); - } else { - try { - final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); - final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); - final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); - - bot.send(bot.getChannel(), - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) - + ' ' - + cmds[1].toUpperCase() - + " = " - + NumberFormat.getCurrencyInstance(Locale.US) - .format((amt * to) / from) - .substring(1) - + ' ' - + cmds[3].toUpperCase()); - } catch (NullPointerException ignored) { - bot.send(sender, - "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); - } - } - } - } else if (query.equals(CURRENCY_RATES_KEYWORD)) { - bot.send(sender, "Last Update: " + pubDate); - - final StringBuilder buff = new StringBuilder(0); - - for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { - if (buff.length() > 0) { - buff.append(", "); - } - buff.append(rate.getKey()).append(": ").append(rate.getValue()); - } - - bot.send(sender, buff.toString()); - } + if (Utils.isValidString(sender) && Utils.isValidString(query)) { + if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { + final Message msg = converyCurrency(query.substring(query.indexOf(' '))); + if (msg.isError()) { + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); } + bot.send(msg.isPrivate() ? sender : bot.getChannel(), msg.getMessage()); } else { helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); - bot.send(sender, "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); } } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java new file mode 100644 index 0000000..9c9238a --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java @@ -0,0 +1,62 @@ +/* + * CurrencyConvertTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>CurrencyConvertTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class CurrencyConvertTest { + @Test + public void testLookupImpl() { + AbstractModuleTest.testAbstractModule(new CurrencyConverter()); + } + + @Test + public void testConvertCurrency() { + assertThat(CurrencyConverter.converyCurrency("100 USD to EUR").getMessage()) + .as("100 USD to EUR").startsWith("100.00 USD = "); + assertThat(CurrencyConverter.converyCurrency("100 BLA to USD").isError()) + .as("100 BLA to USD").isTrue(); + assertThat(CurrencyConverter.converyCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).isPrivate()) + .as(CurrencyConverter.CURRENCY_RATES_KEYWORD + " is private").isTrue(); + assertThat(CurrencyConverter.converyCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).getMessage()) + .as(CurrencyConverter.CURRENCY_RATES_KEYWORD).contains("USD: "); + } +} From e3e11c7998ab15df893937a21d8e9d7ebf1e0f24 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 15:40:13 -0700 Subject: [PATCH 191/842] Added ModuleException class. --- .../mobibot/modules/CurrencyConverter.java | 24 +++--- .../erik/mobibot/modules/ModuleException.java | 75 +++++++++++++++++++ .../mobibot/modules/CurrencyConvertTest.java | 2 +- 3 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 3cd623c..35019b2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -32,7 +32,8 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.*; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.PrivateMessage; @@ -83,7 +84,7 @@ public final class CurrencyConverter extends AbstractModule { commands.add(CURRENCY_CMD); } - static Message converyCurrency(String query) { + static Message converyCurrency(String query) throws ModuleException { if (EXCHANGE_RATES.isEmpty()) { try { final SAXBuilder builder = new SAXBuilder(); @@ -109,10 +110,10 @@ public final class CurrencyConverter extends AbstractModule { EXCHANGE_RATES.put("EUR", "1"); } catch (JDOMException e) { - return new ErrorMessage("An error has occurred while parsing the exchange rates table."); + throw new ModuleException(query, "An error has occurred while parsing the exchange rates table.", e); } catch (IOException e) { - return new ErrorMessage( - "An error has occurred while fetching the exchange rates table: " + e.getMessage()); + throw new ModuleException( + query, "An error has occurred while fetching the exchange rates table.", e); } } @@ -199,11 +200,16 @@ public final class CurrencyConverter extends AbstractModule { private void run(final Mobibot bot, final String sender, final String query) { if (Utils.isValidString(sender) && Utils.isValidString(query)) { if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { - final Message msg = converyCurrency(query.substring(query.indexOf(' '))); - if (msg.isError()) { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); + try { + final Message msg = converyCurrency(query.substring(query.indexOf(' '))); + if (msg.isError()) { + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); + } + bot.send(msg.isPrivate() ? sender : bot.getChannel(), msg.getMessage()); + } catch (ModuleException e) { + bot.getLogger().debug(e.getMessage(), e); + bot.send(sender, e.getMessage()); } - bot.send(msg.isPrivate() ? sender : bot.getChannel(), msg.getMessage()); } else { helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java new file mode 100644 index 0000000..68ba44b --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -0,0 +1,75 @@ +/* + * ModuleException.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +/** + * The <code>ModuleExcepetion</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class ModuleException extends Exception { + private static final long serialVersionUID = -3036774290621088107L; + + private final String query; + + ModuleException(String query, String message) { + super(message); + this.query = query; + + } + + ModuleException(String query, Throwable cause) { + super(cause); + this.query = query; + } + + ModuleException(String query, String message, Throwable cause) { + super(message, cause); + this.query = query; + } + + ModuleException(String message) { + super(message); + query = ""; + } + + ModuleException(Throwable cause) { + super(cause); + query = ""; + } + + public String getQuery() { + return query; + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java index 9c9238a..cf5a6c7 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java @@ -49,7 +49,7 @@ public class CurrencyConvertTest { } @Test - public void testConvertCurrency() { + public void testConvertCurrency() throws ModuleException { assertThat(CurrencyConverter.converyCurrency("100 USD to EUR").getMessage()) .as("100 USD to EUR").startsWith("100.00 USD = "); assertThat(CurrencyConverter.converyCurrency("100 BLA to USD").isError()) From 8f5860af3523e9ca00cb163cfe52b833c1437235 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 15:40:40 -0700 Subject: [PATCH 192/842] Added tests for the Joke module. --- .../thauvin/erik/mobibot/modules/Joke.java | 59 ++++++++++++------- .../erik/mobibot/modules/JokeTest.java | 55 +++++++++++++++++ 2 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 53f2036..98fa80f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -32,6 +32,8 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.PublicMessage; import org.jibble.pircbot.Colors; import org.json.JSONObject; @@ -65,6 +67,37 @@ public final class Joke extends AbstractModule { commands.add(JOKE_CMD); } + /** + * Retrieves a random joke. + * + * @return The new joke. + */ + static Message randomJoke() throws ModuleException { + try { + final URL url = new URL(JOKE_URL); + final URLConnection conn = url.openConnection(); + + final StringBuilder sb = new StringBuilder(); + try (final BufferedReader reader = + new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + + final JSONObject json = new JSONObject(sb.toString()); + + return new PublicMessage( + Colors.CYAN + + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") + .replaceAll("\\\"", "\"") + + Colors.NORMAL); + } + } catch (Exception e) { + throw new ModuleException("An error has occurred retrieving a random joke.", e); + } + } + /** * {@inheritDoc} */ @@ -87,28 +120,10 @@ public final class Joke extends AbstractModule { */ private void run(final Mobibot bot, final String sender) { try { - final URL url = new URL(JOKE_URL); - final URLConnection conn = url.openConnection(); - - final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = - new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - } - - final JSONObject json = new JSONObject(sb.toString()); - - bot.send(bot.getChannel(), - Colors.CYAN - + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") - .replaceAll("\\\"", "\"") - + Colors.NORMAL); - } - } catch (Exception e) { - bot.getLogger().warn("Unable to retrieve random joke.", e); - bot.send(sender, "An error has occurred retrieving a random joke: " + e.getMessage()); + randomJoke(); + } catch (ModuleException e) { + bot.getLogger().warn(e.getMessage(), e); + bot.send(sender, e.getMessage()); } } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java new file mode 100644 index 0000000..38eeabf --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -0,0 +1,55 @@ +/* + * JokeTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>JokeTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class JokeTest { + @Test + public void testLookupImpl() { + AbstractModuleTest.testAbstractModule(new Joke()); + } + + @Test + public void testRamdomJoke() throws ModuleException { + assertThat(Joke.randomJoke().getMessage().length() > 0).as("randomJoke()").isTrue(); + } +} From 1bbc7108977448b64ca0fc931353172c43950819 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 16:32:34 -0700 Subject: [PATCH 193/842] Fixed names. --- .../{CurrencyConvertTest.java => CurrentConverterTest.java} | 4 ++-- src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/test/java/net/thauvin/erik/mobibot/modules/{CurrencyConvertTest.java => CurrentConverterTest.java} (97%) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java similarity index 97% rename from src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java index cf5a6c7..d05759a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConvertTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java @@ -42,9 +42,9 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2019-04-07 * @since 1.0 */ -public class CurrencyConvertTest { +public class CurrentConverterTest { @Test - public void testLookupImpl() { + public void testCurrencyConvertererImpl() { AbstractModuleTest.testAbstractModule(new CurrencyConverter()); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java index 38eeabf..d5c1130 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class JokeTest { @Test - public void testLookupImpl() { + public void testJokeImpl() { AbstractModuleTest.testAbstractModule(new Joke()); } From 0d716b0bb3d2cdc729502f9d901caf889b487399 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 7 Apr 2019 16:33:32 -0700 Subject: [PATCH 194/842] Adeed WorldTime module tests. --- .../erik/mobibot/modules/WorldTime.java | 100 ++++++++++-------- .../erik/mobibot/modules/WordTimeTest.java | 57 ++++++++++ 2 files changed, 111 insertions(+), 46 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index a656ddc..2c9a78a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -32,6 +32,9 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.msg.ErrorMessage; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.PublicMessage; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -48,23 +51,17 @@ import java.util.TreeMap; * @since 1.0 */ public final class WorldTime extends AbstractModule { + // The beats (Internet Time) keyword. + private static final String BEATS_KEYWORD = ".beats"; + // The supported countries. + private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); + /** * The time command. */ - public static final String TIME_CMD = "time"; - - // The beats (Internet Time) keyword. - private static final String BEATS_KEYWORD = ".beats"; - - // The supported countries. - private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); - - /** - * Creates a new {@link WorldTime} instance. - */ - public WorldTime() { - commands.add(TIME_CMD); + private static final String TIME_CMD = "time"; + static { // Initialize the countries map COUNTRIES_MAP.put("AE", "Asia/Dubai"); COUNTRIES_MAP.put("AF", "Asia/Kabul"); @@ -141,12 +138,50 @@ public final class WorldTime extends AbstractModule { COUNTRIES_MAP.put("ZULU", "Zulu"); COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); - + ZoneId.getAvailableZoneIds().stream().filter( tz -> !tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)).forEach( tz -> COUNTRIES_MAP.put(tz, tz)); } + /** + * Creates a new {@link WorldTime} instance. + */ + public WorldTime() { + commands.add(TIME_CMD); + } + + /** + * Returns the current Internet (beat) Time. + * + * @return The Internet Time string. + */ + private static String internetTime() { + final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); + final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) + + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); + return String.format("%c%03d", '@', beats); + } + + static Message worldTime(String query) { + final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim().toUpperCase()))); + final String response; + + if (tz != null) { + if (tz.equals(BEATS_KEYWORD)) { + response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); + } else { + response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( + DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) + + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); + } + } else { + return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet().toString()); + } + + return new PublicMessage(response); + } + /** * Responds with the current time in the specified timezone/country. * @@ -157,30 +192,15 @@ public final class WorldTime extends AbstractModule { */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - boolean isInvalidTz = false; - final String tz = (COUNTRIES_MAP.get((args.substring(args.indexOf(' ') + 1).trim().toUpperCase()))); - final String response; - - if (tz != null) { - if (tz.equals(BEATS_KEYWORD)) { - response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); - } else { - response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( - DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) - + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); - } - } else { - isInvalidTz = true; - response = "The supported countries/zones are: " + COUNTRIES_MAP.keySet().toString(); - } + final Message msg = worldTime(args); if (isPrivate) { - bot.send(sender, response, true); + bot.send(sender, msg.getMessage(), true); } else { - if (isInvalidTz) { - bot.send(sender, response); + if (msg.isError()) { + bot.send(sender, msg.getMessage()); } else { - bot.send(bot.getChannel(), response); + bot.send(bot.getChannel(), msg.getMessage()); } } } @@ -197,18 +217,6 @@ public final class WorldTime extends AbstractModule { bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD)); } - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - private String internetTime() { - final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); - final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) - + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); - return String.format("%c%03d", '@', beats); - } - /** * {@inheritDoc} */ diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java new file mode 100644 index 0000000..97d420a --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java @@ -0,0 +1,57 @@ +/* + * WordTimeTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>WordTimeTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class WordTimeTest { + @Test + public void testWorldTimeImpl() { + AbstractModuleTest.testAbstractModule(new Lookup()); + } + + @Test + public void testWorldTime() { + assertThat(WorldTime.worldTime("PST").getMessage()).as("PST").startsWith("The time is "); + assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue(); + assertThat(WorldTime.worldTime("BEATS").getMessage()).as("BEATS").contains("@"); + } +} From 59869fcdc3b31378c8dc79e8ec8fecf4dd3d308d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:39:18 -0700 Subject: [PATCH 195/842] Changed message to debugMessage, trimmed constructors. --- .../mobibot/modules/CurrencyConverter.java | 10 ++-- .../thauvin/erik/mobibot/modules/Joke.java | 2 +- .../erik/mobibot/modules/ModuleException.java | 53 ++++++++++--------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 35019b2..73784ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -141,9 +141,9 @@ public final class CurrencyConverter extends AbstractModule { .substring(1) + ' ' + cmds[3].toUpperCase()); - } catch (NullPointerException ignored) { - return new ErrorMessage( - "The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + } catch (Exception e) { + throw new ModuleException("convertCurrency(" + query + ')', + "The supported currencies are: " + EXCHANGE_RATES.keySet().toString(), e); } } } else if (query.equals(CURRENCY_RATES_KEYWORD)) { @@ -206,8 +206,8 @@ public final class CurrencyConverter extends AbstractModule { helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); } bot.send(msg.isPrivate() ? sender : bot.getChannel(), msg.getMessage()); - } catch (ModuleException e) { - bot.getLogger().debug(e.getMessage(), e); + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); } } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 98fa80f..fd0edb3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -122,7 +122,7 @@ public final class Joke extends AbstractModule { try { randomJoke(); } catch (ModuleException e) { - bot.getLogger().warn(e.getMessage(), e); + bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 68ba44b..2bbf3b8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -32,44 +32,45 @@ package net.thauvin.erik.mobibot.modules; /** - * The <code>ModuleExcepetion</code> class. + * The <code>ModuleException</code> class. * * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2019-04-07 * @since 1.0 */ -public class ModuleException extends Exception { +class ModuleException extends Exception { private static final long serialVersionUID = -3036774290621088107L; + private final String debugMessage; - private final String query; - - ModuleException(String query, String message) { - super(message); - this.query = query; - - } - - ModuleException(String query, Throwable cause) { - super(cause); - this.query = query; - } - - ModuleException(String query, String message, Throwable cause) { + /** + * Creates a new exception. + * + * @param debugMessage The debug message. + * @param message The exception message. + * @param cause The cause. + */ + ModuleException(String debugMessage, String message, Throwable cause) { super(message, cause); - this.query = query; + this.debugMessage = debugMessage; } - ModuleException(String message) { + /** + * Creates a new exception. + * + * @param debugMessage The debug message. + * @param message The exception message. + */ + ModuleException(String debugMessage, String message) { super(message); - query = ""; + this.debugMessage = debugMessage; } - ModuleException(Throwable cause) { - super(cause); - query = ""; - } - - public String getQuery() { - return query; + /** + * Returns the debug message. + * + * @return The debug message. + */ + String getDebugMessage() { + return debugMessage; } } From 4b0ef573bcee28f33de647ca41f604df4387fcb6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:39:50 -0700 Subject: [PATCH 196/842] Added tests. --- .../thauvin/erik/mobibot/modules/Ping.java | 5 +- .../erik/mobibot/modules/PingTest.java | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 2648dc1..a8a075d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -48,10 +48,10 @@ public class Ping extends AbstractModule { /** * The ping command. */ - public static final String PING_CMD = "ping"; + private static final String PING_CMD = "ping"; // The ping responses. - private static final List<String> PINGS = + static final List<String> PINGS = Arrays.asList( "is barely alive.", "is trying to stay awake.", @@ -79,7 +79,6 @@ public class Ping extends AbstractModule { @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { final SecureRandom r = new SecureRandom(); - bot.action(PINGS.get(r.nextInt(PINGS.size()))); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java new file mode 100644 index 0000000..81c4427 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java @@ -0,0 +1,54 @@ +/* + * PingTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +/** + * The <code>PingTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class PingTest { + @Test + public void testPingImpl() { + AbstractModuleTest.testAbstractModule(new Ping()); + } + + @Test + public void testPingsArray() { + assertThat(Ping.PINGS).as("Pings array is not empty.").isNotEmpty(); + } +} From df6567ad1ca03e39148adf5b43542a7e2bf3827f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:40:28 -0700 Subject: [PATCH 197/842] Switched to Alpha Avantage API, created test. --- .../erik/mobibot/modules/StockQuote.java | 141 +++++++++++------- .../erik/mobibot/modules/StockQuoteTest.java | 81 ++++++++++ 2 files changed, 172 insertions(+), 50 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 55d9ab5..0eb67f0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -31,13 +31,20 @@ */ package net.thauvin.erik.mobibot.modules; -import com.Ostermiller.util.CSVParser; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.msg.ErrorMessage; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.PrivateMessage; +import net.thauvin.erik.mobibot.msg.PublicMessage; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import org.json.JSONException; +import org.json.JSONObject; import java.io.IOException; +import java.util.ArrayList; /** * The StockQuote module. @@ -47,19 +54,86 @@ import java.io.IOException; * @since 1.0 */ public final class StockQuote extends AbstractModule { + + static final String ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key"; + + // The Alpha Advantage URL. + private static final String ALAPHADVANTAGE_URL = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE"; + /** * The quote command. */ - public static final String STOCK_CMD = "stock"; - - // The Yahoo! stock quote URL. - private static final String YAHOO_URL = "http://finance.yahoo.com/d/quotes.csv?&f=snl1d1t1c1oghv&e=.csv&s="; + private static final String STOCK_CMD = "stock"; /** * Creates a new {@link StockQuote} instance. */ public StockQuote() { commands.add(STOCK_CMD); + properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); + } + + + /** + * Get a stock quote. + * + * @param symbol The stock symbol. + * @return The stock quote. + * @throws ModuleException If an errors occurs. + */ + static ArrayList<Message> getQuote(String symbol, String apiKey) throws ModuleException { + final String debugMessage = "getQuote(" + symbol + ')'; + final ArrayList<Message> messages = new ArrayList<>(); + final OkHttpClient client = new OkHttpClient(); + final Request request = + new Request.Builder().url(ALAPHADVANTAGE_URL + "&symbol=" + symbol + "&apikey=" + apiKey).build(); + + try { + final Response response = client.newCall(request).execute(); + final JSONObject json = new JSONObject(response.body().string()); + + try { + final String info = json.getString("Information"); + if (!info.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + try { + final String error = json.getString("Error Message"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + final JSONObject quote = json.getJSONObject("Global Quote"); + + if (quote.isEmpty()) { + messages.add(new ErrorMessage("Invalid symbol.")); + return messages; + } + + messages.add( + new PublicMessage("Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")))); + messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); + messages.add(new PublicMessage(" Previous: " + + Utils.unescapeXml(quote.getString("08. previous close")))); + messages.add(new PrivateMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); + messages.add(new PrivateMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); + messages.add(new PrivateMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); + messages.add(new PrivateMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); + messages.add(new PrivateMessage(" Latest: " + + Utils.unescapeXml(quote.getString("07. latest trading day")))); + messages.add(new PrivateMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) + + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']')); + } catch (IOException e) { + throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); + } + return messages; } /** @@ -80,60 +154,27 @@ public final class StockQuote extends AbstractModule { @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To retrieve a stock quote:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol[.country code]>")); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol>")); } /** - * Returns the specified stock quote from Yahoo! + * Returns the specified stock quote from Alpha Advantage. */ private void run(final Mobibot bot, final String sender, final String symbol) { try { - final OkHttpClient client = new OkHttpClient(); - final Request request = new Request.Builder().url(YAHOO_URL + symbol.toUpperCase()).build(); - final Response response = client.newCall(request).execute(); - - final String[][] lines = CSVParser.parse(response.body().string()); - - if (lines.length > 0) { - final String[] quote = lines[0]; - - if (quote.length > 0) { - if ((quote.length > 3) && (!"N/A".equalsIgnoreCase(quote[3]))) { - bot.send(bot.getChannel(), "Symbol: " + quote[0] + " [" + quote[1] + ']'); - - if (quote.length > 5) { - bot.send(bot.getChannel(), "Last Trade: " + quote[2] + " (" + quote[5] + ')'); - } else { - bot.send(bot.getChannel(), "Last Trade: " + quote[2]); - } - - if (quote.length > 4) { - bot.send(sender, "Time: " + quote[3] + ' ' + quote[4]); - } - - if (quote.length > 6 && !"N/A".equalsIgnoreCase(quote[6])) { - bot.send(sender, "Open: " + quote[6]); - } - - if (quote.length > 7 && !"N/A".equalsIgnoreCase(quote[7]) && !"N/A".equalsIgnoreCase(quote[8])) { - bot.send(sender, "Day's Range: " + quote[7] + " - " + quote[8]); - } - - if (quote.length > 9 && !"0".equalsIgnoreCase(quote[9])) { - bot.send(sender, "Volume: " + quote[9]); - } - } else { - bot.send(sender, "Invalid ticker symbol."); - } + final ArrayList<Message> messages = + getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); + for (Message msg : messages) { + if (msg.isPrivate() || msg.isError()) { + bot.send(sender, msg.getMessage()); } else { - bot.send(sender, "No values returned."); + bot.send(bot.getChannel(), msg.getMessage()); } - } else { - bot.send(sender, "No data returned."); } - } catch (NullPointerException | IOException e) { - bot.getLogger().debug("Unable to retrieve stock quote for: " + symbol, e); - bot.send(sender, "An error has occurred retrieving a stock quote: " + e.getMessage()); + + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(sender, "An error has occurred retrieving a stock quote."); } } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java new file mode 100644 index 0000000..d3c9a69 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -0,0 +1,81 @@ +/* + * StockQuoteTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.msg.Message; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>StockQuoteTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-07 + * @since 1.0 + */ +public class StockQuoteTest { + @Test + public void testGetQuote() throws ModuleException, IOException { + String apiKey = System.getenv("ALPHA_ADVANTAGE"); + if (apiKey == null) { + final Path localProps = Paths.get("local.properties"); + if (Files.exists(localProps)) { + try (final InputStream stream = Files.newInputStream(localProps)) { + final Properties p = new Properties(); + p.load(stream); + apiKey = p.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); + } + } + } + + ArrayList<Message> messages = StockQuote.getQuote("AAPL", apiKey); + assertThat(messages).as("response not empty").isNotEmpty(); + assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL"); + + messages = StockQuote.getQuote("012", apiKey); + assertThat(messages.get(0).isError()).as("invalid symbol error").isTrue(); + } + + @Test + public void testStockQuoteImpl() { + AbstractModuleTest.testAbstractModule(new StockQuote()); + } +} From 1d7fcbd6ad0d4b30038513cb456ea0c0c36e9bd4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:41:14 -0700 Subject: [PATCH 198/842] Spacing fix. --- .../net/thauvin/erik/mobibot/modules/AbstractModuleTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java index 2c2e109..5771ce5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -43,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; final class AbstractModuleTest { static void testAbstractModule(AbstractModule module) { final String name = module.getClass().getName(); + assertThat(module.isEnabled()).as(name + ": enabled").isTrue(); assertThat(module.getCommands().size()).as(name + ": commands > 0").isGreaterThan(0); if (!module.hasProperties()) { @@ -50,8 +51,9 @@ final class AbstractModuleTest { module.setProperty("test", "test"); module.setProperty("", "invalid"); } + assertThat(module.getPropertyKeys().size()).as(name + ": properties > 0").isGreaterThan(0); - assertThat(module.isValidProperties()).as(name + ": isValidProperties()").isTrue(); + module.setProperty("invalid", ""); assertThat(module.isValidProperties()).as(name + ": invalid properties").isFalse(); } From d592536aca5f0a0620b1b683dd117493fa04446d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:41:59 -0700 Subject: [PATCH 199/842] Added more tests. --- .../erik/mobibot/modules/CurrencyConverter.java | 4 ++-- .../erik/mobibot/modules/CurrentConverterTest.java | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 73784ef..ecf59ff 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -84,7 +84,7 @@ public final class CurrencyConverter extends AbstractModule { commands.add(CURRENCY_CMD); } - static Message converyCurrency(String query) throws ModuleException { + static Message convertCurrency(String query) throws ModuleException { if (EXCHANGE_RATES.isEmpty()) { try { final SAXBuilder builder = new SAXBuilder(); @@ -201,7 +201,7 @@ public final class CurrencyConverter extends AbstractModule { if (Utils.isValidString(sender) && Utils.isValidString(query)) { if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { try { - final Message msg = converyCurrency(query.substring(query.indexOf(' '))); + final Message msg = convertCurrency(query); if (msg.isError()) { helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java index d05759a..4b0ebc3 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java @@ -48,15 +48,18 @@ public class CurrentConverterTest { AbstractModuleTest.testAbstractModule(new CurrencyConverter()); } + @Test(expectedExceptions = ModuleException.class) + public void testException() throws ModuleException { + CurrencyConverter.convertCurrency("100 BLA to USD"); + } + @Test public void testConvertCurrency() throws ModuleException { - assertThat(CurrencyConverter.converyCurrency("100 USD to EUR").getMessage()) + assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMessage()) .as("100 USD to EUR").startsWith("100.00 USD = "); - assertThat(CurrencyConverter.converyCurrency("100 BLA to USD").isError()) - .as("100 BLA to USD").isTrue(); - assertThat(CurrencyConverter.converyCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).isPrivate()) + assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).isPrivate()) .as(CurrencyConverter.CURRENCY_RATES_KEYWORD + " is private").isTrue(); - assertThat(CurrencyConverter.converyCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).getMessage()) + assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).getMessage()) .as(CurrencyConverter.CURRENCY_RATES_KEYWORD).contains("USD: "); } } From 24a7d5ad56d7c443b23fe5015d16db18fe856b9d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:42:17 -0700 Subject: [PATCH 200/842] Added more tests. --- .../java/net/thauvin/erik/mobibot/modules/Joke.java | 10 ++++------ .../net/thauvin/erik/mobibot/modules/JokeTest.java | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index fd0edb3..7157814 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -88,13 +88,11 @@ public final class Joke extends AbstractModule { final JSONObject json = new JSONObject(sb.toString()); return new PublicMessage( - Colors.CYAN - + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") - .replaceAll("\\\"", "\"") - + Colors.NORMAL); + json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") + .replaceAll("\\\"", "\"")); } } catch (Exception e) { - throw new ModuleException("An error has occurred retrieving a random joke.", e); + throw new ModuleException("randomJoke()", "An error has occurred retrieving a random joke.", e); } } @@ -120,7 +118,7 @@ public final class Joke extends AbstractModule { */ private void run(final Mobibot bot, final String sender) { try { - randomJoke(); + bot.send(bot.getChannel(), Colors.CYAN + randomJoke().getMessage() + Colors.NORMAL); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java index d5c1130..90fbd4d 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -50,6 +50,7 @@ public class JokeTest { @Test public void testRamdomJoke() throws ModuleException { - assertThat(Joke.randomJoke().getMessage().length() > 0).as("randomJoke()").isTrue(); + assertThat(Joke.randomJoke().getMessage().length() > 0).as("randomJoke() > 0").isTrue(); + assertThat(Joke.randomJoke().getMessage()).as("randomJoke()").contains("Chuck"); } } From 07af8b295739e0bdd462391f59403c876943e3b5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:42:44 -0700 Subject: [PATCH 201/842] Removed ostermiller utils. --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8dccb7a..169fc41 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,6 @@ dependencies { compile 'com.rometools:rome:1.12.0' compile 'org.json:json:20180813' - compile 'org.ostermiller:utils:1.07.00' compile 'org.jsoup:jsoup:1.11.3' compile 'net.objecthunter:exp4j:0.4.8' From 21ec7b95261ea4e2e302df7ee82702fc3635456f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:43:12 -0700 Subject: [PATCH 202/842] Added Alpha Advantage key. --- properties/mobibot.properties | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 6fe0963..545107d 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -44,4 +44,9 @@ tell-max-size=50 # # Get OpenWeatherMap API key from: https://openweathermap.org/ # -#owm-api-key= \ No newline at end of file +#owm-api-key= + +# +# Get Alpha Vantage Stock Quote API key from: https://www.alphavantage.co/support/#api-key +# +#alphavantage-api-key= From c114473368cbd182a29484bddccd571f300d29c3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:43:57 -0700 Subject: [PATCH 203/842] Fix UTF-8 encoding. --- .../thauvin/erik/mobibot/modules/GoogleSearch.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 48ab1b7..a05775f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -52,13 +52,12 @@ import java.nio.charset.StandardCharsets; * @since 1.0 */ public final class GoogleSearch extends ThreadedModule { - /** - * The google command. - */ - public static final String GOOGLE_CMD = "google"; - // The Google API Key property. private static final String GOOGLE_API_KEY_PROP = "google-api-key"; + + // The Google command + private static final String GOOGLE_CMD = "google"; + // The Google Custom Search Engine ID property. private static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; @@ -86,14 +85,14 @@ public final class GoogleSearch extends ThreadedModule { bot.send(sender, "The Google searching facility is disabled."); } } - + /** * Searches Google. */ @SuppressFBWarnings(value = {"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) void run(final Mobibot bot, final String sender, final String query) { try { - final String q = URLEncoder.encode(query, "UTF-8"); + final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); final URL url = new URL("https://www.googleapis.com/customsearch/v1?key=" From 9c9547a75e078f9d71d990e476554c942f3a91d8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:44:58 -0700 Subject: [PATCH 204/842] Version 0.7.3-beta172 --- version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.properties b/version.properties index d5d23eb..4cad559 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Sun Apr 07 00:56:46 PDT 2019 -version.buildmeta=117 +#Mon Apr 08 01:30:11 PDT 2019 +version.buildmeta=172 version.major=0 version.minor=7 version.patch=3 From f18fab2d67fcb77e1c207cf0e17406f24d3c14f4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 01:45:32 -0700 Subject: [PATCH 205/842] Misc IDEA files updates. --- .gitignore | 1 + .idea/inspectionProfiles/Project_Default.xml | 36 ++++ .idea/modules/mobibot.iml | 28 +-- .idea/vcs.xml | 6 + mobibot.ipr | 201 ++++++++++++++---- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 83 +------- 6 files changed, 231 insertions(+), 124 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 0d7f483..51bd2bf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .classpath .DS_Store .gradle +.history .kobalt .nb-gradle .project diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..838047c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,36 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="TOP_LEVEL_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="INNER_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="METHOD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> + </value> + </option> + <option name="FIELD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="IGNORE_DEPRECATED" value="false" /> + <option name="IGNORE_JAVADOC_PERIOD" value="true" /> + <option name="IGNORE_DUPLICATED_THROWS" value="false" /> + <option name="IGNORE_POINT_TO_ITSELF" value="false" /> + <option name="myAdditionalJavadocTags" value="created" /> + </inspection_tool> + </profile> +</component> \ No newline at end of file diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 5aebc5c..9fd394e 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+018" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+151" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" generated="true" /> <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> <excludeFolder url="file://$MODULE_DIR$/../../build" /> <excludeFolder url="file://$MODULE_DIR$/../../kobaltBuild" /> @@ -18,38 +19,39 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.11.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.11.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.11.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.11.2" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.2.3" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.4.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.4.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.11.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.11.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.0" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20180813" level="project" /> <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.11.3" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: org.patriques:alphavantage4j:1.4" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.0.1" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.8" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.11.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.11" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.14.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.11.1" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.12.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.apache-extras.beanshell:bsh:2.0b6" level="project" /> </component> diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr index 6f63ba0..2bb2f68 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -160,7 +160,7 @@ <option name="autoDownloadKobalt" value="true" /> <option name="downloadSources" value="false" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.118" /> + <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.122" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> @@ -177,6 +177,127 @@ </MavenGeneralSettings> </option> </component> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> <component name="ProjectCodeStyleConfiguration"> <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> </component> @@ -218,11 +339,10 @@ </component> <component name="ProjectModuleManager"> <modules> - <module fileurl="file://$PROJECT_DIR$/kobalt/Build.kt.iml" filepath="$PROJECT_DIR$/kobalt/Build.kt.iml" /> - <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" group="mobibot" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="11" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="12" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="PropertiesComponent"> @@ -336,22 +456,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.8"> + <library name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.11"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.8/a8752df3cdefb9278bea6d9f36286986b4414336/spotbugs-annotations-3.1.8.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.11/5380cad296d13aae1dff9d55c72a42e10a62b345/spotbugs-annotations-3.1.11.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.8/c71ee0a073e0fd3a900d3e91927ba601521f7e6c/spotbugs-annotations-3.1.8-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.11/9d24d67fd213fb77c4b3e6c5e2458a93666cca1b/spotbugs-annotations-3.1.11-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.4"> + <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/1d1fab03f31a75018fc30ec248859a5c89cd342f/compiler-0.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.6/1b8707299c34406ed0ba40bbf8513352ac4765c9/compiler-0.9.6.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.4/599674a480f9940ff5e25b375c6f59d14d8a4bfa/compiler-0.9.4-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.6/a23ce3f24ae450f13f52aaa336bf836759f22331/compiler-0.9.6-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.google.code.findbugs:jsr305:3.0.2"> @@ -372,40 +492,40 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/c5b4c491aecb72e7c32a78da0b5c6b9cda8dee0f/gson-2.8.5-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome-utils:1.11.1"> + <library name="Gradle: com.rometools:rome-utils:1.12.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.1/8dc6594b46896917eff81ce87688327636743ef/rome-utils-1.11.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.0/c714531d168f733f1ebed7ee8f6ebd255bac52dc/rome-utils-1.12.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.11.1/258377a50b0972ea8b6ac55b2eaa730f5ff9508e/rome-utils-1.11.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.0/4087c6c81afefde27b14143ff4123bf36c0bec88/rome-utils-1.12.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.11.1"> + <library name="Gradle: com.rometools:rome:1.12.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.1/ad04e529c37f990ac36e9e064c518e6ebfc8caf5/rome-1.11.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.0/a7057d89b443792277662380cc9f020f1707927b/rome-1.12.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.11.1/ef64b7db6743c40cc4bbf91787d4d38a9ff60c48/rome-1.11.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.0/75e65a94c7663e232de3f5b9cb68004cb449cf3/rome-1.12.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:3.11.0"> + <library name="Gradle: com.squareup.okhttp3:okhttp:3.14.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.11.0/75966e05a49046ca2ae734e5626f28837a8d1e82/okhttp-3.11.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.0/c3a9efd10f26a802da2c47dbe54a8d4c44a6019f/okhttp-3.14.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.11.0/cf16b99519850b1adc3b259c90fd8566a0f592f3/okhttp-3.11.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.0/775ec91f22324e24657fb832a9fc47b90738b25d/okhttp-3.14.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okio:okio:1.14.0"> + <library name="Gradle: com.squareup.okio:okio:1.17.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.14.0/102d7be47241d781ef95f1581d414b0943053130/okio-1.14.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.14.0/e7c96b4fe5651490d8f3c022042940d743a3bdd9/okio-1.14.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/331ecaeba2fd87c06a0766e8ebe718e1e294f27d/okio-1.17.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.retrofit2:converter-gson:2.4.0"> @@ -471,13 +591,13 @@ <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.0/pinboard-poster-1.0.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:semver:1.0.1"> + <library name="Gradle: net.thauvin.erik:semver:1.2.0"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.0.1/semver-1.0.1-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.apache-extras.beanshell:bsh:2.0b6"> @@ -489,40 +609,40 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache-extras.beanshell/bsh/2.0b6/76a65f674e8f2df409934824c49ca338987c63ae/bsh-2.0b6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-api:2.11.1"> + <library name="Gradle: org.apache.logging.log4j:log4j-api:2.11.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.1/268f0fe4df3eefe052b57c87ec48517d64fb2a10/log4j-api-2.11.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.2/f5e9a2ffca496057d6891a3de65128efc636e26e/log4j-api-2.11.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.1/acf35297ef03716fc14e11e2978d7c749ed3f7e5/log4j-api-2.11.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.2/a11bdc0e8f95da31527b4f34f1a35c23e197498d/log4j-api-2.11.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-core:2.11.1"> + <library name="Gradle: org.apache.logging.log4j:log4j-core:2.11.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.1/592a48674c926b01a9a747c7831bcd82a9e6d6e4/log4j-core-2.11.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.2/6c2fb3f5b7cd27504726aef1b674b542a0c9cf53/log4j-core-2.11.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.1/23bef4ea0494ba9fb9835df0e3e23c6883d8d545/log4j-core-2.11.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.2/338d2678d341eedbc5273f983766dc65ffed13c2/log4j-core-2.11.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.1"> + <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.1/4b41b53a3a2d299ce381a69d165381ca19f62912/log4j-slf4j-impl-2.11.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.2/4d44e4edc4a7fb39f09b95b09f560a15976fa1ba/log4j-slf4j-impl-2.11.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.1/2b387980561fb77b2aafdcf0d61552e0fa74928a/log4j-slf4j-impl-2.11.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.2/6bb10559db88828dac3627de26974035a5dd4ddb/log4j-slf4j-impl-2.11.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.assertj:assertj-core:3.11.1"> + <library name="Gradle: org.assertj:assertj-core:3.12.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.11.1/fdac3217b804d6900fa3650f17b5cb48e620b140/assertj-core-3.11.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.12.2/3c9724faa6090ab78a61d4e54011df72de2361c4/assertj-core-3.12.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.11.1/55362d8f04018b1140c5a370057bf8cd4a63c3de/assertj-core-3.11.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.12.2/685240f44440706f41832ba4ff223204d3e9e06d/assertj-core-3.12.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> @@ -606,6 +726,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.patriques:alphavantage4j:1.4"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.patriques/alphavantage4j/1.4/36248e503759c89bd402aa5188ae6cc81596656/alphavantage4j-1.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> <library name="Gradle: org.slf4j:slf4j-api:1.7.25"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index e2ae517..b8d456e 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -9,96 +9,31 @@ import java.time.*; /** * Provides semantic version information. * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version - * Annotation Processor</a> + * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> */ public final class ReleaseInfo { - public final static String PRERELEASE_PREFIX = "-"; - public final static String BUILDMETA_PREFIX = "+"; - public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1541025595051L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1554712215517L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "023"; + public final static String BUILDMETA = "172"; - /** - * The full version string. - * <p> - * Formatted as: - * <blockquote> - * <code>MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]</code> - * </blockquote> - * <p> - * For example: - * <ul> - * <li><code>1.0.0</code></li> - * <li><code>1.0.0-beta</code></li> - * <li><code>1.0.0+20160124144700</code></li> - * <li><code>1.0.0-alpha+001</code></li> - * </ul> + /** + * The full semantic version string. */ public final static String VERSION = Integer.toString(MAJOR) + '.' + Integer.toString(MINOR) + '.' + Integer.toString(PATCH) - + preReleaseWithPrefix() + buildMetaWithPrefix(); + + ((!PRERELEASE.isEmpty()) ? "-" + PRERELEASE : "") + + ((!BUILDMETA.isEmpty()) ? "+" + BUILDMETA : ""); /** * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. */ - private ReleaseInfo() - throws UnsupportedOperationException { + private ReleaseInfo() { throw new UnsupportedOperationException("Illegal constructor call."); } - - /** - * Returns the build metadata with {@value #BUILDMETA_PREFIX} prefix. - * - * @return The build metadata, if any. - */ - public static String buildMetaWithPrefix() { - return buildMetaWithPrefix(BUILDMETA_PREFIX); - } - - /** - * Returns the build metadata. - * - * @param prefix Prefix to prepend. - * @return The build metadata, if any. - */ - public static String buildMetaWithPrefix(final String prefix) { - if (BUILDMETA.length() > 0 && prefix.length() > 0) { - return prefix + BUILDMETA; - } else { - return BUILDMETA; - } - } - - /** - * Returns the pre-release version with {@value #PRERELEASE_PREFIX} prefix. - * - * @return The pre-release version, if any. - */ - public static String preReleaseWithPrefix() { - return preReleaseWithPrefix(PRERELEASE_PREFIX); - } - - /** - * Returns the pre-release version. - * - * @param prefix The prefix to prepend. - * @return The pre-release version, if any. - */ - public static String preReleaseWithPrefix(final String prefix) { - if (PRERELEASE.length() > 0 && prefix.length() > 0) { - return prefix + PRERELEASE; - } else { - return PRERELEASE; - } - } -} \ No newline at end of file +} From dabdf9ae34d903ed9f55b8a7a9a3937d19b0eec0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 09:53:29 -0700 Subject: [PATCH 206/842] Renamed to NoticeMessage. --- .../erik/mobibot/msg/{PrivateMessage.java => NoticeMessage.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/net/thauvin/erik/mobibot/msg/{PrivateMessage.java => NoticeMessage.java} (100%) diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java rename to src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java From 7085f3a294a31fdd829b2bf03108f8f672531e0a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 09:54:15 -0700 Subject: [PATCH 207/842] Renamed PrivateMessage to NoticeMessage. --- .../mobibot/modules/CurrencyConverter.java | 6 ++-- .../erik/mobibot/modules/StockQuote.java | 24 ++++++------- .../net/thauvin/erik/mobibot/msg/Message.java | 35 ++++++++++++------- .../erik/mobibot/msg/NoticeMessage.java | 8 ++--- .../erik/mobibot/msg/PublicMessage.java | 2 +- .../mobibot/modules/CurrentConverterTest.java | 4 +-- 6 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index ecf59ff..bd5664f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -36,7 +36,7 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.PrivateMessage; +import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; import org.jdom2.Document; import org.jdom2.Element; @@ -157,7 +157,7 @@ public final class CurrencyConverter extends AbstractModule { i++; } - return new PrivateMessage(buff.toString()); + return new NoticeMessage(buff.toString()); } } return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); @@ -205,7 +205,7 @@ public final class CurrencyConverter extends AbstractModule { if (msg.isError()) { helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); } - bot.send(msg.isPrivate() ? sender : bot.getChannel(), msg.getMessage()); + bot.send(msg.isNotice() ? sender : bot.getChannel(), msg.getMessage()); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 0eb67f0..115916e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -35,7 +35,7 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.PrivateMessage; +import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -107,11 +107,11 @@ public final class StockQuote extends AbstractModule { throw new ModuleException(debugMessage, Utils.unescapeXml(error)); } } catch (JSONException ignore) { - // Do nothing. + // Do nothing. } final JSONObject quote = json.getJSONObject("Global Quote"); - + if (quote.isEmpty()) { messages.add(new ErrorMessage("Invalid symbol.")); return messages; @@ -122,13 +122,13 @@ public final class StockQuote extends AbstractModule { messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); messages.add(new PublicMessage(" Previous: " + Utils.unescapeXml(quote.getString("08. previous close")))); - messages.add(new PrivateMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); - messages.add(new PrivateMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); - messages.add(new PrivateMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); - messages.add(new PrivateMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); - messages.add(new PrivateMessage(" Latest: " + messages.add(new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); + messages.add(new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); + messages.add(new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); + messages.add(new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); + messages.add(new NoticeMessage(" Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); - messages.add(new PrivateMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) + messages.add(new NoticeMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']')); } catch (IOException e) { throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); @@ -165,11 +165,7 @@ public final class StockQuote extends AbstractModule { final ArrayList<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); for (Message msg : messages) { - if (msg.isPrivate() || msg.isError()) { - bot.send(sender, msg.getMessage()); - } else { - bot.send(bot.getChannel(), msg.getMessage()); - } + bot.send(msg.isNoticeOrError() ? sender : bot.getChannel(), msg.getMessage()); } } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 4217d37..3e799f5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -40,7 +40,7 @@ package net.thauvin.erik.mobibot.msg; */ public class Message { private boolean isError; - private boolean isPrivate; + private boolean isNotice; private String msg = ""; /** @@ -53,12 +53,12 @@ public class Message { /** * Creates a new message. * - * @param message The message. - * @param isPrivate The private flag. + * @param message The message. + * @param isNotice The notice flag. */ - public Message(String message, boolean isPrivate, boolean isError) { + public Message(String message, boolean isNotice, boolean isError) { msg = message; - this.isPrivate = isPrivate; + this.isNotice = isNotice; this.isError = isError; } @@ -99,20 +99,29 @@ public class Message { } /** - * Returns the private message flag. + * Returns the message notice flag. * - * @return The private flag. + * @return The notice flag. */ - public boolean isPrivate() { - return isPrivate; + public boolean isNotice() { + return isNotice; } /** - * Set the private message flag. + * Set the message notice flag. * - * @param isPrivate The private flag. + * @param isNotice The notice flag. */ - public void setPrivate(boolean isPrivate) { - this.isPrivate = isPrivate; + public void setNotice(boolean isNotice) { + this.isNotice = isNotice; + } + + /** + * Returns <code>true</code> if the message is an error or a notice. + * + * @return <code>true</code> or <code>false</code> + */ + public boolean isNoticeOrError() { + return (isNotice || isError); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java index 45cdb4b..d8f016a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java @@ -32,15 +32,15 @@ package net.thauvin.erik.mobibot.msg; /** - * The <code>PrivateMessage</code> class. + * The <code>NoticeMessage</code> class. * * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2019-04-07 * @since 1.0 */ -public class PrivateMessage extends Message { - public PrivateMessage(String message) { +public class NoticeMessage extends Message { + public NoticeMessage(String message) { this.setMessage(message); - this.setPrivate(true); + this.setNotice(true); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java index 5fcc1e5..af10e94 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java @@ -41,6 +41,6 @@ package net.thauvin.erik.mobibot.msg; public class PublicMessage extends Message { public PublicMessage(String message) { this.setMessage(message); - this.setPrivate(false); + this.setNotice(false); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java index 4b0ebc3..fb2869d 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java @@ -57,8 +57,8 @@ public class CurrentConverterTest { public void testConvertCurrency() throws ModuleException { assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMessage()) .as("100 USD to EUR").startsWith("100.00 USD = "); - assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).isPrivate()) - .as(CurrencyConverter.CURRENCY_RATES_KEYWORD + " is private").isTrue(); + assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).isNotice()) + .as(CurrencyConverter.CURRENCY_RATES_KEYWORD + " is notice").isTrue(); assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).getMessage()) .as(CurrencyConverter.CURRENCY_RATES_KEYWORD).contains("USD: "); } From 267b858cbdd5f089ff7f9a2c28ee137e4b98d6df Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Apr 2019 09:54:42 -0700 Subject: [PATCH 208/842] Re-enabled SpotBugs. --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 169fc41..dbc0ad0 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,8 @@ dependencies { testImplementation 'org.testng:testng:6.14.3' testImplementation 'org.assertj:assertj-core:3.12.2' - //spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0' + spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' + compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.11' } From 1ea99bc62212ff577d77c170630dce7bbe759bb6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:20:20 -0700 Subject: [PATCH 209/842] Added TestNG config. --- build.gradle | 4 +++- src/test/resources/testng.xml | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/testng.xml diff --git a/build.gradle b/build.gradle index dbc0ad0..79fc849 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,9 @@ dependencies { } test { - useTestNG() + useTestNG() { + suites 'src/test/resources/testng.xml' + } } compileJava { diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml new file mode 100644 index 0000000..544049c --- /dev/null +++ b/src/test/resources/testng.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > +<suite name="tests"> + <test name="full"> + <packages> + <package name="net.thauvin.erik.mobibot.*"/> + </packages> + </test> +</suite> From 614e45ab636325c9816f8dc166345b4558c55f77 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:21:42 -0700 Subject: [PATCH 210/842] ErrorMessages now set the notice flag to true. --- .../java/net/thauvin/erik/mobibot/msg/ErrorMessage.java | 1 + src/main/java/net/thauvin/erik/mobibot/msg/Message.java | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java index bc42e6e..89c6306 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java @@ -42,5 +42,6 @@ public class ErrorMessage extends Message { public ErrorMessage(String message) { this.setMessage(message); this.setError(true); + this.setNotice(true); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 3e799f5..cfc9902 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -115,13 +115,4 @@ public class Message { public void setNotice(boolean isNotice) { this.isNotice = isNotice; } - - /** - * Returns <code>true</code> if the message is an error or a notice. - * - * @return <code>true</code> or <code>false</code> - */ - public boolean isNoticeOrError() { - return (isNotice || isError); - } } From 34028479e86790164d91df9ade7bba9f7753d423 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:22:09 -0700 Subject: [PATCH 211/842] Added cyan method. --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 25de366..d37e9bc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -152,6 +152,16 @@ public final class Utils { return s.substring(0, 1).toUpperCase() + s.substring(1); } + /** + * Meks the given string cyan + * + * @param s The string. + * @return The cyan string. + */ + public static String cyan(final String s) { + return Colors.CYAN + s + Colors.NORMAL; + } + /** * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. * @@ -198,7 +208,7 @@ public final class Utils { * Makes the given string green. * * @param s The string. - * @return The bold string. + * @return The green string. */ public static String green(final String s) { return Colors.DARK_GREEN + s + Colors.NORMAL; From 56b54912b35f99c0c61946958ef17a136ce9d9f4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:24:15 -0700 Subject: [PATCH 212/842] Code cleanup. --- .../erik/mobibot/modules/StockQuote.java | 92 ++++++++++--------- .../erik/mobibot/modules/ThreadedModule.java | 10 +- .../net/thauvin/erik/mobibot/modules/War.java | 8 +- .../erik/mobibot/modules/Weather2.java | 20 +--- 4 files changed, 57 insertions(+), 73 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 115916e..c51566d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -54,15 +54,14 @@ import java.util.ArrayList; * @since 1.0 */ public final class StockQuote extends AbstractModule { - + /** + * The Alpha Advantage property key. + */ static final String ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key"; // The Alpha Advantage URL. private static final String ALAPHADVANTAGE_URL = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE"; - - /** - * The quote command. - */ + // The quote command. private static final String STOCK_CMD = "stock"; /** @@ -73,7 +72,6 @@ public final class StockQuote extends AbstractModule { properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); } - /** * Get a stock quote. * @@ -90,46 +88,56 @@ public final class StockQuote extends AbstractModule { try { final Response response = client.newCall(request).execute(); - final JSONObject json = new JSONObject(response.body().string()); + if (response.body() != null) { + final JSONObject json = new JSONObject(response.body().string()); - try { - final String info = json.getString("Information"); - if (!info.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + try { + final String info = json.getString("Information"); + if (!info.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + } + } catch (JSONException ignore) { + // Do nothing. } - } catch (JSONException ignore) { - // Do nothing. - } - try { - final String error = json.getString("Error Message"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + try { + final String error = json.getString("Error Message"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing. } - } catch (JSONException ignore) { - // Do nothing. + + final JSONObject quote = json.getJSONObject("Global Quote"); + + if (quote.isEmpty()) { + messages.add(new ErrorMessage("Invalid symbol.")); + return messages; + } + + messages.add( + new PublicMessage("Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")))); + messages.add( + new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); + messages.add( + new PublicMessage(" Previous: " + + Utils.unescapeXml(quote.getString("08. previous close")))); + messages.add( + new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); + messages.add( + new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); + messages.add( + new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); + messages.add( + new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); + messages.add( + new NoticeMessage(" Latest: " + + Utils.unescapeXml(quote.getString("07. latest trading day")))); + messages.add( + new NoticeMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) + + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']')); } - - final JSONObject quote = json.getJSONObject("Global Quote"); - - if (quote.isEmpty()) { - messages.add(new ErrorMessage("Invalid symbol.")); - return messages; - } - - messages.add( - new PublicMessage("Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")))); - messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); - messages.add(new PublicMessage(" Previous: " - + Utils.unescapeXml(quote.getString("08. previous close")))); - messages.add(new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); - messages.add(new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); - messages.add(new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); - messages.add(new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); - messages.add(new NoticeMessage(" Latest: " - + Utils.unescapeXml(quote.getString("07. latest trading day")))); - messages.add(new NoticeMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) - + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']')); } catch (IOException e) { throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); } @@ -165,7 +173,7 @@ public final class StockQuote extends AbstractModule { final ArrayList<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); for (Message msg : messages) { - bot.send(msg.isNoticeOrError() ? sender : bot.getChannel(), msg.getMessage()); + bot.send(msg.isNotice() ? sender : bot.getChannel(), msg.getMessage()); } } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java index 85b3e3d..59e99ce 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java @@ -54,15 +54,7 @@ public abstract class ThreadedModule extends AbstractModule { } /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return isValidProperties(); - } - - /** - * Run the thread. + * Runs the thread. */ abstract void run(Mobibot bot, String sender, String args); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 71e606e..74a8f33 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -44,15 +44,11 @@ import java.security.SecureRandom; * @since 1.0 */ public final class War extends AbstractModule { - /** - * The war command - */ - public static final String WAR_CMD = "war"; - + // The war command + private static final String WAR_CMD = "war"; // The deck of card. private static final String[] WAR_DECK = new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; - // The suits for the deck of card. private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index a230c26..1b87363 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -50,14 +50,10 @@ import java.util.List; * @since 1.0 */ public class Weather2 extends AbstractModule { - - /** - * The weather command. - */ - public static final String WEATHER_CMD = "weather"; - // The OpenWeatherMap API Key property. private static final String OWM_API_KEY_PROP = "owm-api-key"; + // The weather command. + private static final String WEATHER_CMD = "weather"; /** * Creates a new {@link Weather2} instance. @@ -76,7 +72,7 @@ public class Weather2 extends AbstractModule { } private String fAndC(final Double d) { - final Double c = (d - 32) * 5 / 9; + final double c = (d - 32) * 5 / 9; return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; } @@ -104,14 +100,6 @@ public class Weather2 extends AbstractModule { + ". Zip codes are supported in most countries."); } - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return isValidProperties(); - } - /** * Fetches the weather data from a specific city. */ @@ -146,7 +134,7 @@ public class Weather2 extends AbstractModule { bot.send(sender, "Temperature: " + fAndC(main.getTemp()), isPrivate); } - if (main.hasHumidity()) { + if (main.hasHumidity() && (main.getHumidity() != null)) { bot.send(sender, "Humidity: " + Math.round(main.getHumidity()) + "%", isPrivate); } } From 8c2c351222b97892c519d3f7e7c941d8bd0ae94a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:24:49 -0700 Subject: [PATCH 213/842] Added method to get a sanitized message. --- .../erik/mobibot/modules/ModuleException.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 2bbf3b8..a77807a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -31,6 +31,11 @@ */ package net.thauvin.erik.mobibot.modules; +import okhttp3.HttpUrl; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * The <code>ModuleException</code> class. * @@ -41,6 +46,7 @@ package net.thauvin.erik.mobibot.modules; class ModuleException extends Exception { private static final long serialVersionUID = -3036774290621088107L; private final String debugMessage; + private final Pattern urlPattern = Pattern.compile("(https?://\\S+)"); /** * Creates a new exception. @@ -73,4 +79,43 @@ class ModuleException extends Exception { String getDebugMessage() { return debugMessage; } + + /** + * Return the sanitized (URL query parameters are replaced by char count) message. + * + * @return The sanitized message. + */ + String getSanitizedMessage() { + if (hasCause()) { + final String causeMessage = getCause().getMessage(); + final Matcher matcher = urlPattern.matcher(causeMessage); + if (matcher.find()) { + final HttpUrl url = HttpUrl.parse(matcher.group()); + if ((url != null) && (matcher.group().contains("?")) && (url.querySize() > 0)) { + final StringBuilder query = new StringBuilder(); + for (int i = 0, size = url.querySize(); i < size; i++) { + if (query.length() > 0) { + query.append("&"); + } + query.append(url.queryParameterName(i)).append('=') + .append('[').append(url.queryParameterValue(i).length()).append(']'); + } + return getDebugMessage() + "\nCaused by: " + getCause().getClass().getName() + ": " + + causeMessage.replace(matcher.group(), + matcher.group().substring(0, matcher.group().indexOf('?') + 1) + query); + } + } + } + + return getMessage(); + } + + /** + * Return <code>true</code> if the exception has a cause. + * + * @return <code>true</code> or <code>false</code> + */ + boolean hasCause() { + return getCause() != null; + } } From d85624b06730f5d4bf7ede576d2694e04e2b2b49 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:28:09 -0700 Subject: [PATCH 214/842] Improved the AbstractMoudule. --- .../erik/mobibot/modules/AbstractModule.java | 6 +++++- .../net/thauvin/erik/mobibot/modules/Dice.java | 14 ++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 8abafaf..c58830a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -106,7 +106,11 @@ public abstract class AbstractModule { * @return <code>true</code> or <code>false</code> */ public boolean isEnabled() { - return true; + if (hasProperties()) { + return isValidProperties(); + } else { + return true; + } } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index ec37090..6ad915d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -44,10 +44,8 @@ import java.security.SecureRandom; * @since 1.0 */ public final class Dice extends AbstractModule { - /** - * The dice command. - */ - public static final String DICE_CMD = "dice"; + // The dice command. + private static final String DICE_CMD = "dice"; /** * The default constructor. @@ -100,12 +98,4 @@ public final class Dice extends AbstractModule { bot.send(sender, "To roll the dice:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD)); } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return true; - } } From 3def574940019acbb2ce7c26d2eae9f6cafc46ac Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:29:17 -0700 Subject: [PATCH 215/842] Implemented googleSearch method for easier testing. --- .../erik/mobibot/modules/GoogleSearch.java | 87 +++++++++++++------ 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index a05775f..e12bc1d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -34,15 +34,19 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.NoticeMessage; import org.json.JSONArray; import org.json.JSONObject; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; /** * The GoogleSearch module. @@ -53,14 +57,11 @@ import java.nio.charset.StandardCharsets; */ public final class GoogleSearch extends ThreadedModule { // The Google API Key property. - private static final String GOOGLE_API_KEY_PROP = "google-api-key"; - + static final String GOOGLE_API_KEY_PROP = "google-api-key"; + // The Google Custom Search Engine ID property. + static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; // The Google command private static final String GOOGLE_CMD = "google"; - - // The Google Custom Search Engine ID property. - private static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; - // The tab indent (4 spaces). private static final String TAB_INDENT = " "; @@ -74,31 +75,24 @@ public final class GoogleSearch extends ThreadedModule { } /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - if (isEnabled()) { - bot.send(sender, "To search Google:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); - } else { - bot.send(sender, "The Google searching facility is disabled."); - } - } - - /** - * Searches Google. + * Performs a search on Google. + * + * @param query The search query. + * @param apiKey The Google API key. + * The Google CSE key. + * @param cseKey The Google search results. */ @SuppressFBWarnings(value = {"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) - void run(final Mobibot bot, final String sender, final String query) { + static ArrayList<Message> searchGoogle(String query, String apiKey, String cseKey) throws ModuleException { + final ArrayList<Message> results = new ArrayList<>(); try { final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); final URL url = new URL("https://www.googleapis.com/customsearch/v1?key=" - + properties.get(GOOGLE_API_KEY_PROP) + + apiKey + "&cx=" - + properties.get(GOOGLE_CSE_KEY_PROP) + + cseKey + "&q=" + q + "&filter=1&num=5&alt=json"); @@ -117,13 +111,50 @@ public final class GoogleSearch extends ThreadedModule { for (int i = 0; i < ja.length(); i++) { final JSONObject j = ja.getJSONObject(i); - bot.send(sender, Utils.unescapeXml(j.getString("title"))); - bot.send(sender, TAB_INDENT + Utils.green(j.getString("link"))); + results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); + results.add(new NoticeMessage(j.getString("link"))); } } - } catch (Exception e) { - bot.getLogger().warn("Unable to search in Google for: " + query, e); - bot.send(sender, "An error has occurred searching in Google: " + e.getMessage()); + } catch (IOException e) { + throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e); + } + + return results; + } + + /** + * {@inheritDoc} + */ + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (isEnabled()) { + bot.send(sender, "To search Google:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); + } else { + bot.send(sender, "The Google searching facility is disabled."); + } + } + + /** + * Searches Google. + */ + void run(final Mobibot bot, final String sender, final String query) { + try { + final ArrayList<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), + properties.get(GOOGLE_CSE_KEY_PROP)); + + int i = 0; + for (Message msg : results) { + if (i % 2 == 0) { + bot.send(sender, Utils.green(TAB_INDENT + msg.getMessage())); + } else { + bot.send(sender, msg.getMessage()); + } + i++; + } + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(sender, e.getMessage()); } } } From 844507ea9e5da5523747b973273767400eb6f5aa Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:30:01 -0700 Subject: [PATCH 216/842] Code cleanup. --- .../erik/mobibot/modules/CurrencyConverter.java | 10 ++-------- .../java/net/thauvin/erik/mobibot/modules/Joke.java | 12 +++++------- .../net/thauvin/erik/mobibot/modules/Twitter.java | 7 ++----- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index bd5664f..73e26ad 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -60,20 +60,14 @@ import java.util.TreeMap; * @since 1.0 */ public final class CurrencyConverter extends AbstractModule { - /** - * The rates keyword. - */ + // The rates keyword. static final String CURRENCY_RATES_KEYWORD = "rates"; - // The currency command. private static final String CURRENCY_CMD = "currency"; - // The exchange rates. private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); - // The exchange rates table URL. private static final String EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"; - // The last exchange rates table publication date. private static String pubDate = ""; @@ -203,7 +197,7 @@ public final class CurrencyConverter extends AbstractModule { try { final Message msg = convertCurrency(query); if (msg.isError()) { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false); } bot.send(msg.isNotice() ? sender : bot.getChannel(), msg.getMessage()); } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 7157814..074369b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -32,9 +32,9 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.PublicMessage; -import org.jibble.pircbot.Colors; import org.json.JSONObject; import java.io.BufferedReader; @@ -51,14 +51,11 @@ import java.nio.charset.StandardCharsets; * @since 1.0 */ public final class Joke extends AbstractModule { - /** - * The joke command. - */ - public static final String JOKE_CMD = "joke"; - // The ICNDB URL. private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; + // The joke command. + private static final String JOKE_CMD = "joke"; /** * Creates a new {@link Joke} instance. @@ -87,6 +84,7 @@ public final class Joke extends AbstractModule { final JSONObject json = new JSONObject(sb.toString()); + //noinspection RegExpRedundantEscape return new PublicMessage( json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") .replaceAll("\\\"", "\"")); @@ -118,7 +116,7 @@ public final class Joke extends AbstractModule { */ private void run(final Mobibot bot, final String sender) { try { - bot.send(bot.getChannel(), Colors.CYAN + randomJoke().getMessage() + Colors.NORMAL); + bot.send(bot.getChannel(), Utils.cyan(randomJoke().getMessage())); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 6a1dd9f..14d508a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -44,16 +44,13 @@ import twitter4j.conf.ConfigurationBuilder; * @since 1.0 */ public final class Twitter extends ThreadedModule { - /** - * The twitter command. - */ - public static final String TWITTER_CMD = "twitter"; - // The property keys. private static final String CONSUMER_KEY_PROP = "twitter-consumerKey"; private static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; private static final String TOKEN_PROP = "twitter-token"; private static final String TOKEN_SECRET_PROP = "twitter-tokenSecret"; + // The twitter command. + private static final String TWITTER_CMD = "twitter"; /** * Creates a new {@link Twitter} instance. From 43ba8e37748ba7d77955dbb4ff8039e13e914a48 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:31:47 -0700 Subject: [PATCH 217/842] Implemented LocalProperties for testing. --- .../erik/mobibot/modules/LocalProperties.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java new file mode 100644 index 0000000..8dbe467 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -0,0 +1,80 @@ +/* + * LocalProperties.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.BeforeSuite; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +/** + * The <code>LocalProperties</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-08 + * @since 1.0 + */ +class LocalProperties { + private static Properties localProperties = new Properties(); + + static String getProperty(final String key) { + if (localProperties.containsKey(key)) { + return localProperties.getProperty(key); + } else { + final String env = System.getenv(keyToEnv(key)); + if (env != null) { + localProperties.setProperty(key, env); + } + return env; + } + } + + private static String keyToEnv(String key) { + return key.replaceAll("-", "_").toUpperCase(); + } + + @BeforeSuite + static void loadLocalProperties() { + final Path localProps = Paths.get("local.properties"); + if (Files.exists(localProps)) { + try (final InputStream stream = Files.newInputStream(localProps)) { + localProperties.load(stream); + } catch (IOException ignore) { + // Do nothing + } + } + } +} From 8843d2cfa6bad70812d64ec2cc350d4672137844 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:32:15 -0700 Subject: [PATCH 218/842] Implemented tests for ModuleException. --- .../mobibot/modules/ModuleExceptionTest.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java new file mode 100644 index 0000000..1d9cee5 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java @@ -0,0 +1,76 @@ +/* + * ModuleExceptionTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.lang.reflect.Method; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>ModuleExceptionTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-09 + * @since 1.0 + */ +public class ModuleExceptionTest { + @DataProvider(name = "dp") + Object[][] createData(Method m) { + System.out.println(m.getName()); // print test method name + return new Object[][]{new Object[]{new ModuleException("debugMessage", "message", + new IOException("Secret URL http://foo.com?apiKey=sec&userID=me"))}, + new Object[]{new ModuleException("debugMessage", "message")} + }; + } + + @Test(dataProvider = "dp") + final void testGetDebugMessage(ModuleException e) { + assertThat(e.getDebugMessage()).as("get debug message").isEqualTo("debugMessage"); + } + + @Test(dataProvider = "dp") + final void testGetMessage(ModuleException e) { + assertThat(e.getMessage()).as("get message").isEqualTo("message"); + } + + @Test(dataProvider = "dp") + final void testGetStanitizedMessage(ModuleException e) { + if (e.hasCause()) { + assertThat(e.getSanitizedMessage()).as("get sanitzed url") + .contains("http://foo.com?apiKey=[3]&userID=[2]"); + } + } +} From 799cf697d429b6785c18f655bf34428258a1679c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:32:40 -0700 Subject: [PATCH 219/842] Implemented GoogleSearch tests. --- .../mobibot/modules/GoogleSearchTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java new file mode 100644 index 0000000..6cac311 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -0,0 +1,75 @@ +/* + * GoogleSearchTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.msg.Message; +import org.testng.annotations.Test; + +import java.util.ArrayList; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>GoogleSearchTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-08 + * @since 1.0 + */ +public class GoogleSearchTest { + @Test + public void testGoogleSearchImpl() { + AbstractModuleTest.testAbstractModule(new GoogleSearch()); + } + + @Test + public void testSearchGoogle() throws ModuleException { + final String apiKey = LocalProperties.getProperty(GoogleSearch.GOOGLE_API_KEY_PROP); + final String cseKey = LocalProperties.getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP); + try { + ArrayList<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey); + assertThat(messages).as("mobibot results not empty").isNotEmpty(); + assertThat(messages.get(0).getMessage()).as("found mobitopia").contains("mobibot"); + + messages = GoogleSearch.searchGoogle("aapl", apiKey, cseKey); + assertThat(messages).as("aapl results not empty").isNotEmpty(); + assertThat(messages.get(0).getMessage()).as("found apple").containsIgnoringCase("apple"); + } catch (ModuleException e) { + // Avoid displaying api keys in CI logs. + if ("true".equals(System.getenv("CI"))) { + throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage()); + } else { + throw e; + } + } + } +} From 2d5fc4c0cb2aac3500c6057eaaf9240f441535f7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 02:33:07 -0700 Subject: [PATCH 220/842] Improved tests. --- .../mobibot/modules/AbstractModuleTest.java | 4 +- .../erik/mobibot/modules/JokeTest.java | 2 +- .../erik/mobibot/modules/StockQuoteTest.java | 38 ++++++++----------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java index 5771ce5..618b5ba 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -43,8 +43,8 @@ import static org.assertj.core.api.Assertions.assertThat; final class AbstractModuleTest { static void testAbstractModule(AbstractModule module) { final String name = module.getClass().getName(); - - assertThat(module.isEnabled()).as(name + ": enabled").isTrue(); + + assertThat(module.isEnabled()).as(name + ": enabled").isNotEqualTo(module.hasProperties()); assertThat(module.getCommands().size()).as(name + ": commands > 0").isGreaterThan(0); if (!module.hasProperties()) { assertThat(module.getPropertyKeys().size()).as(name + ": no properties").isEqualTo(0); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java index 90fbd4d..edc821d 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -51,6 +51,6 @@ public class JokeTest { @Test public void testRamdomJoke() throws ModuleException { assertThat(Joke.randomJoke().getMessage().length() > 0).as("randomJoke() > 0").isTrue(); - assertThat(Joke.randomJoke().getMessage()).as("randomJoke()").contains("Chuck"); + assertThat(Joke.randomJoke().getMessage()).as("randomJoke()").containsIgnoringCase("chuck"); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index d3c9a69..796f672 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -34,13 +34,7 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Properties; import static org.assertj.core.api.Assertions.assertThat; @@ -53,25 +47,23 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class StockQuoteTest { @Test - public void testGetQuote() throws ModuleException, IOException { - String apiKey = System.getenv("ALPHA_ADVANTAGE"); - if (apiKey == null) { - final Path localProps = Paths.get("local.properties"); - if (Files.exists(localProps)) { - try (final InputStream stream = Files.newInputStream(localProps)) { - final Properties p = new Properties(); - p.load(stream); - apiKey = p.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); - } + public void testGetQuote() throws ModuleException { + final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); + try { + ArrayList<Message> messages = StockQuote.getQuote("AAPL", apiKey); + assertThat(messages).as("response not empty").isNotEmpty(); + assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL"); + + messages = StockQuote.getQuote("012", apiKey); + assertThat(messages.get(0).isError()).as("invalid symbol error").isTrue(); + } catch (ModuleException e) { + // Avoid displaying api keys in CI logs. + if ("true".equals(System.getenv("CI"))) { + throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage()); + } else { + throw e; } } - - ArrayList<Message> messages = StockQuote.getQuote("AAPL", apiKey); - assertThat(messages).as("response not empty").isNotEmpty(); - assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL"); - - messages = StockQuote.getQuote("012", apiKey); - assertThat(messages.get(0).isError()).as("invalid symbol error").isTrue(); } @Test From 3a2954b2cfe7dc4c54d712c8037cdfaa743383a8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:55:02 -0700 Subject: [PATCH 221/842] Reworked and added tests. --- .../erik/mobibot/modules/Weather2.java | 127 +++++++++++------- .../erik/mobibot/modules/Weather2Test.java | 76 +++++++++++ 2 files changed, 152 insertions(+), 51 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 1b87363..282210e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -39,7 +39,13 @@ import net.aksingh.owmjapis.model.param.Weather; import net.aksingh.owmjapis.model.param.Wind; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.msg.ErrorMessage; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.NoticeMessage; +import net.thauvin.erik.mobibot.msg.PublicMessage; +import org.jibble.pircbot.Colors; +import java.util.ArrayList; import java.util.List; /** @@ -49,9 +55,12 @@ import java.util.List; * @created 2017-04-02 * @since 1.0 */ -public class Weather2 extends AbstractModule { - // The OpenWeatherMap API Key property. - private static final String OWM_API_KEY_PROP = "owm-api-key"; +public class Weather2 extends ThreadedModule { + /** + * The OpenWeatherMap API Key property. + */ + public static final String OWM_API_KEY_PROP = "owm-api-key"; + // The weather command. private static final String WEATHER_CMD = "weather"; @@ -63,51 +72,33 @@ public class Weather2 extends AbstractModule { properties.put(OWM_API_KEY_PROP, ""); } - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); - } - - private String fAndC(final Double d) { + private static String fAndC(final Double d) { final double c = (d - 32) * 5 / 9; return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; } - private OWM.Country getCountry(String countryCode) { - for (OWM.Country c : OWM.Country.values()) { + private static OWM.Country getCountry(final String countryCode) { + for (final OWM.Country c : OWM.Country.values()) { if (c.name().equalsIgnoreCase(countryCode)) { return c; } - } return OWM.Country.UNITED_STATES; } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To display weather information:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); - bot.send(sender, "For example:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); - bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") - + ". Zip codes are supported in most countries."); - } + static ArrayList<Message> getWeather(final String query, final String apiKey) throws ModuleException { + if (!Utils.isValidString(apiKey)) { + throw new ModuleException(Utils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing."); + } + + final OWM owm = new OWM(apiKey); + final ArrayList<Message> messages = new ArrayList<>(); - /** - * Fetches the weather data from a specific city. - */ - private void run(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - final OWM owm = new OWM(properties.get(OWM_API_KEY_PROP)); owm.setUnit(OWM.Unit.IMPERIAL); - if (Utils.isValidString(args)) { - final String[] argv = args.split(","); + + if (Utils.isValidString(query)) { + final String[] argv = query.split(","); if (argv.length >= 1 && argv.length <= 2) { final String country; @@ -126,23 +117,23 @@ public class Weather2 extends AbstractModule { cwd = owm.currentWeatherByCityName(city, getCountry(country)); } if (cwd.hasCityName()) { - bot.send(sender, "City: " + cwd.getCityName() + " [" + country + "]", isPrivate); + messages.add(new NoticeMessage("City: " + cwd.getCityName() + " [" + country + "]")); final Main main = cwd.getMainData(); if (main != null) { if (main.hasTemp()) { - bot.send(sender, "Temperature: " + fAndC(main.getTemp()), isPrivate); + messages.add(new NoticeMessage("Temperature: " + fAndC(main.getTemp()))); } if (main.hasHumidity() && (main.getHumidity() != null)) { - bot.send(sender, "Humidity: " + Math.round(main.getHumidity()) + "%", isPrivate); + messages.add(new PublicMessage("Humidity: " + Math.round(main.getHumidity()) + "%")); } } if (cwd.hasWindData()) { final Wind w = cwd.getWindData(); if (w != null && w.hasSpeed()) { - bot.send(sender, "Wind: " + wind(w.getSpeed()), isPrivate); + messages.add(new PublicMessage("Wind: " + wind(w.getSpeed()))); } } @@ -150,37 +141,71 @@ public class Weather2 extends AbstractModule { final StringBuilder condition = new StringBuilder("Condition: "); final List<Weather> list = cwd.getWeatherList(); if (list != null) { - for (Weather w : list) { + for (final Weather w : list) { if (condition.indexOf(",") == -1) { condition.append(Utils.capitalize(w.getDescription())); } else { condition.append(", ").append(w.getDescription()); } } - bot.send(sender, condition.toString(), isPrivate); + messages.add(new PublicMessage(condition.toString())); } } - - bot.send(sender, Utils.green("https://openweathermap.org/city/" + cwd.getCityId()), isPrivate); - - return; + messages.add(new NoticeMessage("https://openweathermap.org/city/" + + cwd.getCityId(), Colors.GREEN)); } } catch (APIException | NullPointerException e) { - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Unable to perform weather lookup: " + args, e); - } - - bot.send(bot.getChannel(), "Unable to perform weather lookup: " + e.getMessage()); + throw new ModuleException("getWeather(" + query + ')', "Unable to perform weather lookup.", e); } } } - helpResponse(bot, sender, args, isPrivate); + if (messages.isEmpty()) { + messages.add(new ErrorMessage("Invalid syntax.")); + } + + return messages; } - private String wind(final Double w) { + private static String wind(final Double w) { final double kmh = w * 1.60934; return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; } + + /** + * {@inheritDoc} + */ + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To display weather information:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); + bot.send(sender, "For example:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); + bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") + + ". Zip codes are supported in most countries."); + } + + /** + * Fetches the weather data from a specific city. + */ + void run(final Mobibot bot, final String sender, final String args) { + if (Utils.isValidString(args)) { + try { + final ArrayList<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); + if (messages.get(0).isError()) { + helpResponse(bot, sender, args, true); + } else { + for (final Message msg : messages) { + bot.send(msg.isNotice() ? bot.getChannel() : sender, msg.getMessage(), msg.getColor()); + } + } + } catch (ModuleException e) { + bot.getLogger().debug(e.getDebugMessage(), e); + bot.send(bot.getChannel(), e.getMessage()); + } + } else { + helpResponse(bot, sender, args, true); + } + } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java new file mode 100644 index 0000000..bcd4a66 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -0,0 +1,76 @@ +/* + * Weather2Test.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.msg.Message; +import org.testng.annotations.Test; + +import java.util.ArrayList; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>Weather2Test</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-09 + * @since 1.0 + */ +public class Weather2Test extends LocalProperties { + @Test + public void testWeather() throws ModuleException { + ArrayList<Message> messages = Weather2.getWeather("98204", + LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); + assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett"); + messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); + assertThat(messages.get(0).getMessage()).as("is UK").contains("UK"); + + try { + Weather2.getWeather("test", ""); + } catch (Exception e) { + assertThat(e).as("no API key").isInstanceOf(ModuleException.class); + assertThat(e).as("no API key exception has no cause").hasNoCause(); + } + + try { + Weather2.getWeather("", "apikey"); + } catch (Exception e) { + assertThat(e).as("no query").isInstanceOf(ModuleException.class); + assertThat(e).as("no query exception has no cause").hasNoCause(); + } + } + + @Test + public void testWeather2Impl() { + AbstractModuleTest.testAbstractModule(new Weather2()); + } +} From f5863f88ac8c332c5ea08362608a897788c08031 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:55:32 -0700 Subject: [PATCH 222/842] Added color property. --- .../erik/mobibot/msg/ErrorMessage.java | 8 +++- .../net/thauvin/erik/mobibot/msg/Message.java | 46 +++++++++++++++++-- .../erik/mobibot/msg/NoticeMessage.java | 8 +++- .../erik/mobibot/msg/PublicMessage.java | 8 +++- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java index 89c6306..9bd0ea7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java @@ -39,9 +39,15 @@ package net.thauvin.erik.mobibot.msg; * @since 1.0 */ public class ErrorMessage extends Message { - public ErrorMessage(String message) { + public ErrorMessage(final String message) { this.setMessage(message); this.setError(true); this.setNotice(true); } + public ErrorMessage(final String message, final String color) { + this.setMessage(message); + this.setError(true); + this.setNotice(true); + this.setColor(color); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index cfc9902..84d49ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -31,6 +31,8 @@ */ package net.thauvin.erik.mobibot.msg; +import org.jibble.pircbot.Colors; + /** * The <code>Message</code> class. * @@ -39,6 +41,7 @@ package net.thauvin.erik.mobibot.msg; * @since 1.0 */ public class Message { + private String color = Colors.NORMAL; private boolean isError; private boolean isNotice; private String msg = ""; @@ -55,13 +58,48 @@ public class Message { * * @param message The message. * @param isNotice The notice flag. + * @param isError The error flag. */ - public Message(String message, boolean isNotice, boolean isError) { + public Message(final String message, final boolean isNotice, final boolean isError) { msg = message; this.isNotice = isNotice; this.isError = isError; } + + /** + * Creates a new message. + * + * @param message The message. + * @param isNotice The notice flag. + * @param isError The error flag + * @param color The color. + */ + public Message(final String message, final boolean isNotice, final boolean isError, final String color) { + msg = message; + this.isNotice = isNotice; + this.isError = isError; + this.color = color; + } + + /** + * Returns the color. + * + * @return The color, + */ + public String getColor() { + return color; + } + + /** + * Set the color + * + * @param color The new color. + */ + public void setColor(final String color) { + this.color = color; + } + /** * Returns the message. * @@ -76,7 +114,7 @@ public class Message { * * @param message The new message. */ - public void setMessage(String message) { + public void setMessage(final String message) { msg = message; } @@ -94,7 +132,7 @@ public class Message { * * @param error The error flag. */ - public void setError(boolean error) { + public void setError(final boolean error) { isError = error; } @@ -112,7 +150,7 @@ public class Message { * * @param isNotice The notice flag. */ - public void setNotice(boolean isNotice) { + public void setNotice(final boolean isNotice) { this.isNotice = isNotice; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java index d8f016a..80bfe9e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java @@ -39,8 +39,14 @@ package net.thauvin.erik.mobibot.msg; * @since 1.0 */ public class NoticeMessage extends Message { - public NoticeMessage(String message) { + public NoticeMessage(final String message) { this.setMessage(message); this.setNotice(true); } + + public NoticeMessage(final String message, final String color) { + this.setMessage(message); + this.setNotice(true); + this.setColor(color); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java index af10e94..b47da23 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java @@ -39,8 +39,14 @@ package net.thauvin.erik.mobibot.msg; * @since 1.0 */ public class PublicMessage extends Message { - public PublicMessage(String message) { + public PublicMessage(final String message) { this.setMessage(message); this.setNotice(false); } + + public PublicMessage(final String message, final String color) { + this.setMessage(message); + this.setNotice(false); + this.setColor(color); + } } From 518703b826db5d00d80179082cfa1ff936d8a903 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:55:45 -0700 Subject: [PATCH 223/842] Added colorize method. --- .../java/net/thauvin/erik/mobibot/Utils.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index d37e9bc..4c7d8d5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -73,7 +73,7 @@ public final class Utils { * @return The bold string. */ public static String bold(final String s) { - return Colors.BOLD + s + Colors.BOLD; + return colorize(s, Colors.BOLD); } /** @@ -152,6 +152,20 @@ public final class Utils { return s.substring(0, 1).toUpperCase() + s.substring(1); } + /** + * Colorize a string. + * + * @param s The string. + * @param color The color. + * @return The colorized string. + */ + public static String colorize(final String s, final String color) { + if (color.equals(Colors.BOLD) || color.equals(Colors.REVERSE)) { + return color + s + color; + } + return color + s + Colors.NORMAL; + } + /** * Meks the given string cyan * @@ -159,7 +173,7 @@ public final class Utils { * @return The cyan string. */ public static String cyan(final String s) { - return Colors.CYAN + s + Colors.NORMAL; + return colorize(s, Colors.CYAN); } /** @@ -211,7 +225,7 @@ public final class Utils { * @return The green string. */ public static String green(final String s) { - return Colors.DARK_GREEN + s + Colors.NORMAL; + return colorize(s, Colors.DARK_GREEN); } /** @@ -276,7 +290,7 @@ public final class Utils { * @return The reverse color string. */ public static String reverseColor(final String s) { - return Colors.REVERSE + s + Colors.REVERSE; + return colorize(s, Colors.REVERSE); } /** From bdf0ca300643c9b6f02fe94cca3552ecbf2d7d3e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:56:21 -0700 Subject: [PATCH 224/842] Reworked and added test. --- .../erik/mobibot/modules/GoogleSearch.java | 111 ++++++++++-------- .../mobibot/modules/GoogleSearchTest.java | 23 +++- 2 files changed, 84 insertions(+), 50 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index e12bc1d..1fd4e6c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -83,43 +83,52 @@ public final class GoogleSearch extends ThreadedModule { * @param cseKey The Google search results. */ @SuppressFBWarnings(value = {"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) - static ArrayList<Message> searchGoogle(String query, String apiKey, String cseKey) throws ModuleException { - final ArrayList<Message> results = new ArrayList<>(); - try { - final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); - - final URL url = - new URL("https://www.googleapis.com/customsearch/v1?key=" - + apiKey - + "&cx=" - + cseKey - + "&q=" - + q - + "&filter=1&num=5&alt=json"); - final URLConnection conn = url.openConnection(); - - final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = - new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - } - - final JSONObject json = new JSONObject(sb.toString()); - final JSONArray ja = json.getJSONArray("items"); - - for (int i = 0; i < ja.length(); i++) { - final JSONObject j = ja.getJSONObject(i); - results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); - results.add(new NoticeMessage(j.getString("link"))); - } - } - } catch (IOException e) { - throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e); + static ArrayList<Message> searchGoogle(final String query, final String apiKey, final String cseKey) + throws ModuleException { + if (!Utils.isValidString(apiKey) || !Utils.isValidString(cseKey)) { + throw new ModuleException(Utils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing."); } - return results; + if (Utils.isValidString(query)) { + final ArrayList<Message> results = new ArrayList<>(); + try { + final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); + + final URL url = + new URL("https://www.googleapis.com/customsearch/v1?key=" + + apiKey + + "&cx=" + + cseKey + + "&q=" + + q + + "&filter=1&num=5&alt=json"); + final URLConnection conn = url.openConnection(); + + final StringBuilder sb = new StringBuilder(); + try (final BufferedReader reader = + new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + + final JSONObject json = new JSONObject(sb.toString()); + final JSONArray ja = json.getJSONArray("items"); + + for (int i = 0; i < ja.length(); i++) { + final JSONObject j = ja.getJSONObject(i); + results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); + results.add(new NoticeMessage(j.getString("link"))); + } + } + } catch (IOException e) { + throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e); + } + + return results; + } else { + throw new ModuleException("Invalid query."); + } } /** @@ -131,7 +140,7 @@ public final class GoogleSearch extends ThreadedModule { bot.send(sender, "To search Google:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); } else { - bot.send(sender, "The Google searching facility is disabled."); + bot.send(sender, "The Google search module is disabled."); } } @@ -139,22 +148,26 @@ public final class GoogleSearch extends ThreadedModule { * Searches Google. */ void run(final Mobibot bot, final String sender, final String query) { - try { - final ArrayList<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), - properties.get(GOOGLE_CSE_KEY_PROP)); + if (Utils.isValidString(query)) { + try { + final ArrayList<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), + properties.get(GOOGLE_CSE_KEY_PROP)); - int i = 0; - for (Message msg : results) { - if (i % 2 == 0) { - bot.send(sender, Utils.green(TAB_INDENT + msg.getMessage())); - } else { - bot.send(sender, msg.getMessage()); + int i = 0; + for (final Message msg : results) { + if (i % 2 == 0) { + bot.send(sender, Utils.green(TAB_INDENT + msg.getMessage())); + } else { + bot.send(sender, msg.getMessage()); + } + i++; } - i++; + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(sender, e.getMessage()); } - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); + } else { + helpResponse(bot, sender, query, true); } } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 6cac311..d222eec 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2019-04-08 * @since 1.0 */ -public class GoogleSearchTest { +public class GoogleSearchTest extends LocalProperties { @Test public void testGoogleSearchImpl() { AbstractModuleTest.testAbstractModule(new GoogleSearch()); @@ -63,6 +63,27 @@ public class GoogleSearchTest { messages = GoogleSearch.searchGoogle("aapl", apiKey, cseKey); assertThat(messages).as("aapl results not empty").isNotEmpty(); assertThat(messages.get(0).getMessage()).as("found apple").containsIgnoringCase("apple"); + + try { + GoogleSearch.searchGoogle("test", "", "apiKey"); + } catch (Exception e) { + assertThat(e).as("no API key").isInstanceOf(ModuleException.class); + assertThat(e).as("no API key exception has no cause").hasNoCause(); + } + + try { + GoogleSearch.searchGoogle("test", "apiKey", ""); + } catch (Exception e) { + assertThat(e).as("no CSE API key").isInstanceOf(ModuleException.class); + assertThat(e).as("no CSE API key exception has no cause").hasNoCause(); + } + + try { + GoogleSearch.searchGoogle("", "apikey", "apiKey"); + } catch (Exception e) { + assertThat(e).as("no query").isInstanceOf(ModuleException.class); + assertThat(e).as("no query exception has no cause").hasNoCause(); + } } catch (ModuleException e) { // Avoid displaying api keys in CI logs. if ("true".equals(System.getenv("CI"))) { From 0ce160c9051ce01744dca95fbfcc0086280ef1b1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:57:03 -0700 Subject: [PATCH 225/842] Added new constructor. --- .../erik/mobibot/modules/ModuleException.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index a77807a..9dcd6a4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -48,6 +48,16 @@ class ModuleException extends Exception { private final String debugMessage; private final Pattern urlPattern = Pattern.compile("(https?://\\S+)"); + /** + * Creates a new exception. + * + * @param message The exception message. + */ + ModuleException(final String message) { + super(message); + this.debugMessage = message; + } + /** * Creates a new exception. * @@ -55,7 +65,7 @@ class ModuleException extends Exception { * @param message The exception message. * @param cause The cause. */ - ModuleException(String debugMessage, String message, Throwable cause) { + ModuleException(final String debugMessage, final String message, final Throwable cause) { super(message, cause); this.debugMessage = debugMessage; } @@ -66,7 +76,7 @@ class ModuleException extends Exception { * @param debugMessage The debug message. * @param message The exception message. */ - ModuleException(String debugMessage, String message) { + ModuleException(final String debugMessage, final String message) { super(message); this.debugMessage = debugMessage; } From 70f6781e563415f7bd13ac1ffdb48f865af52304 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:58:13 -0700 Subject: [PATCH 226/842] Now extends ThreadedModule --- .../mobibot/modules/CurrencyConverter.java | 8 +- .../thauvin/erik/mobibot/modules/Joke.java | 12 +- .../erik/mobibot/modules/StockQuote.java | 153 +++++++++--------- .../erik/mobibot/modules/WorldTime.java | 2 +- 4 files changed, 83 insertions(+), 92 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 73e26ad..88a7f8f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -59,7 +59,7 @@ import java.util.TreeMap; * @created Feb 11, 2004 * @since 1.0 */ -public final class CurrencyConverter extends AbstractModule { +public final class CurrencyConverter extends ThreadedModule { // The rates keyword. static final String CURRENCY_RATES_KEYWORD = "rates"; // The currency command. @@ -78,7 +78,7 @@ public final class CurrencyConverter extends AbstractModule { commands.add(CURRENCY_CMD); } - static Message convertCurrency(String query) throws ModuleException { + static Message convertCurrency(final String query) throws ModuleException { if (EXCHANGE_RATES.isEmpty()) { try { final SAXBuilder builder = new SAXBuilder(); @@ -168,7 +168,7 @@ public final class CurrencyConverter extends AbstractModule { } } - new Thread(() -> run(bot, sender, args)).start(); + super.commandResponse(bot, sender, args, isPrivate); } /** @@ -191,7 +191,7 @@ public final class CurrencyConverter extends AbstractModule { * Converts the specified currencies. */ @SuppressFBWarnings(value = "REDOS") - private void run(final Mobibot bot, final String sender, final String query) { + void run(final Mobibot bot, final String sender, final String query) { if (Utils.isValidString(sender) && Utils.isValidString(query)) { if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { try { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 074369b..5f40429 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -50,7 +50,7 @@ import java.nio.charset.StandardCharsets; * @created 2014-04-20 * @since 1.0 */ -public final class Joke extends AbstractModule { +public final class Joke extends ThreadedModule { // The ICNDB URL. private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; @@ -94,14 +94,6 @@ public final class Joke extends AbstractModule { } } - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - new Thread(() -> run(bot, sender)).start(); - } - /** * {@inheritDoc} */ @@ -114,7 +106,7 @@ public final class Joke extends AbstractModule { /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> */ - private void run(final Mobibot bot, final String sender) { + void run(final Mobibot bot, final String sender, String arg) { try { bot.send(bot.getChannel(), Utils.cyan(randomJoke().getMessage())); } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index c51566d..e8f7a14 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -53,7 +53,7 @@ import java.util.ArrayList; * @created Feb 7, 2004 * @since 1.0 */ -public final class StockQuote extends AbstractModule { +public final class StockQuote extends ThreadedModule { /** * The Alpha Advantage property key. */ @@ -79,81 +79,78 @@ public final class StockQuote extends AbstractModule { * @return The stock quote. * @throws ModuleException If an errors occurs. */ - static ArrayList<Message> getQuote(String symbol, String apiKey) throws ModuleException { - final String debugMessage = "getQuote(" + symbol + ')'; - final ArrayList<Message> messages = new ArrayList<>(); - final OkHttpClient client = new OkHttpClient(); - final Request request = - new Request.Builder().url(ALAPHADVANTAGE_URL + "&symbol=" + symbol + "&apikey=" + apiKey).build(); + static ArrayList<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { + if (!Utils.isValidString(apiKey)) { + throw new ModuleException(Utils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); + } - try { - final Response response = client.newCall(request).execute(); - if (response.body() != null) { - final JSONObject json = new JSONObject(response.body().string()); + if (Utils.isValidString(symbol)) { + final String debugMessage = "getQuote(" + symbol + ')'; + final ArrayList<Message> messages = new ArrayList<>(); + final OkHttpClient client = new OkHttpClient(); + final Request request = + new Request.Builder().url(ALAPHADVANTAGE_URL + "&symbol=" + symbol + "&apikey=" + apiKey).build(); - try { - final String info = json.getString("Information"); - if (!info.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + try { + final Response response = client.newCall(request).execute(); + if (response.body() != null) { + final JSONObject json = new JSONObject(response.body().string()); + + try { + final String info = json.getString("Information"); + if (!info.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + } + } catch (JSONException ignore) { + // Do nothing. } - } catch (JSONException ignore) { - // Do nothing. - } - try { - final String error = json.getString("Error Message"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + try { + final String error = json.getString("Error Message"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing. } - } catch (JSONException ignore) { - // Do nothing. + + final JSONObject quote = json.getJSONObject("Global Quote"); + + if (quote.isEmpty()) { + messages.add(new ErrorMessage("Invalid symbol.")); + return messages; + } + + messages.add( + new PublicMessage("Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")))); + messages.add( + new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); + messages.add( + new PublicMessage(" Previous: " + + Utils.unescapeXml(quote.getString("08. previous close")))); + messages.add( + new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); + messages.add( + new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); + messages.add( + new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); + messages.add( + new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); + messages.add( + new NoticeMessage(" Latest: " + + Utils.unescapeXml(quote.getString("07. latest trading day")))); + messages.add( + new NoticeMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) + + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']')); } - - final JSONObject quote = json.getJSONObject("Global Quote"); - - if (quote.isEmpty()) { - messages.add(new ErrorMessage("Invalid symbol.")); - return messages; - } - - messages.add( - new PublicMessage("Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")))); - messages.add( - new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); - messages.add( - new PublicMessage(" Previous: " - + Utils.unescapeXml(quote.getString("08. previous close")))); - messages.add( - new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); - messages.add( - new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); - messages.add( - new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); - messages.add( - new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); - messages.add( - new NoticeMessage(" Latest: " - + Utils.unescapeXml(quote.getString("07. latest trading day")))); - messages.add( - new NoticeMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) - + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']')); + } catch (IOException e) { + throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); } - } catch (IOException e) { - throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); - } - return messages; - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - if (args.length() > 0) { - new Thread(() -> run(bot, sender, args)).start(); + return messages; } else { - helpResponse(bot, sender, args, isPrivate); + throw new ModuleException("Invalid symbol."); } + } /** @@ -168,17 +165,19 @@ public final class StockQuote extends AbstractModule { /** * Returns the specified stock quote from Alpha Advantage. */ - private void run(final Mobibot bot, final String sender, final String symbol) { - try { - final ArrayList<Message> messages = - getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); - for (Message msg : messages) { - bot.send(msg.isNotice() ? sender : bot.getChannel(), msg.getMessage()); + void run(final Mobibot bot, final String sender, final String symbol) { + if (Utils.isValidString(symbol)) { + try { + final ArrayList<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); + for (final Message msg : messages) { + bot.send(msg.isNotice() ? sender : bot.getChannel(), msg.getMessage()); + } + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(bot.getChannel(), e.getMessage()); } - - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, "An error has occurred retrieving a stock quote."); + } else { + helpResponse(bot, sender, symbol, true); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 2c9a78a..0922f1e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -163,7 +163,7 @@ public final class WorldTime extends AbstractModule { return String.format("%c%03d", '@', beats); } - static Message worldTime(String query) { + static Message worldTime(final String query) { final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim().toUpperCase()))); final String response; From b1641eba06b7303103bacaea584e420c40c61685 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:58:48 -0700 Subject: [PATCH 227/842] Added send method with color parameter. --- .../net/thauvin/erik/mobibot/Mobibot.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index d6f7fa4..f7c7948 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -153,6 +153,8 @@ public class Mobibot extends PircBot { private final String logsDir; // The recap array. private final List<String> recap = new ArrayList<>(0); + // The tell object. + private final Tell tell; // The backlogs URL. private String backLogsUrl = ""; // The default tags/categories. @@ -167,8 +169,6 @@ public class Mobibot extends PircBot { private String identPwd = ""; // The pinboard posts handler. private Pinboard pinboard = null; - // The tell object. - private Tell tell; // Today's date. private String today = Utils.today(); @@ -1406,7 +1406,7 @@ public class Mobibot extends PircBot { /** * Sends a private message or notice. * - * @param sender The nick of the person who sent the message. + * @param sender The channel or nick of the person who sent the message. * @param message The actual message. * @param isPrivate Set to <code>true</code> if the response should be a private message, otherwise a notice is * sent. @@ -1430,15 +1430,27 @@ public class Mobibot extends PircBot { } /** - * Sends a private notice. + * Sends a message. * - * @param sender The nick of the person who sent the message. + * @param who The channel or nick of the person who sent the message. * @param message The actual message. */ - public final void send(final String sender, final String message) { - send(sender, message, false); + public final void send(final String who, final String message) { + send(who, message, false); } + /** + * Sends a message. + * + * @param who The channel or nick of the person who sent the message. + * @param message The actual message. + * @param color The message's color. + */ + public final void send(final String who, final String message, final String color) { + send(who, Utils.colorize(message, color), false); + } + + /** * Sets the feed URL. * From 76e7651f7cf3e0afc8c7d6df2f79902f1625e287 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:59:13 -0700 Subject: [PATCH 228/842] Improved tests. --- .../net/thauvin/erik/mobibot/UtilsTest.java | 37 +++++++++++++------ .../mobibot/modules/AbstractModuleTest.java | 2 +- .../erik/mobibot/modules/LocalProperties.java | 26 ++++++------- .../erik/mobibot/modules/LookupTest.java | 2 +- .../mobibot/modules/ModuleExceptionTest.java | 8 ++-- .../erik/mobibot/modules/StockQuoteTest.java | 17 ++++++++- 6 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index 80f875b..ba469d7 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -62,18 +62,31 @@ public class UtilsTest { } @Test - public void testBold() throws Exception { + public void testBold() { assertThat(Utils.bold(1)).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD); assertThat(Utils.bold(ASCII)).as("bold(ascii").isEqualTo(Colors.BOLD + ASCII + Colors.BOLD); } @Test - public void testCapitalize() throws Exception { + public void testCapitalize() { assertThat(Utils.capitalize("this is a test.")).isEqualTo("This is a test."); } @Test - public void testEnsureDir() throws Exception { + public void testColorize() { + assertThat(Utils.colorize(ASCII, Colors.REVERSE)).as("reverse") + .isEqualTo(Colors.REVERSE + ASCII + Colors.REVERSE); + assertThat(Utils.colorize(ASCII, Colors.RED)).as("red") + .isEqualTo(Colors.RED + ASCII + Colors.NORMAL); + } + + @Test + public void testCyan() { + assertThat(Utils.cyan(ASCII)).isEqualTo(Colors.CYAN + ASCII + Colors.NORMAL); + } + + @Test + public void testEnsureDir() { assertThat(Utils.ensureDir("dir", false)).as("ensureDir(dir, false)") .isEqualTo("dir" + File.separatorChar); assertThat(Utils.ensureDir("https://erik.thauvin.net", true)) @@ -81,18 +94,18 @@ public class UtilsTest { } @Test - public void testGetIntProperty() throws Exception { + public void testGetIntProperty() { assertThat(Utils.getIntProperty("10", 1)).as("getIntProperty(10, 1)").isEqualTo(10); assertThat(Utils.getIntProperty("a", 1)).as("getIntProperty(a, 1)").isEqualTo(1); } @Test - public void testGreen() throws Exception { + public void testGreen() { assertThat(Utils.green(ASCII)).isEqualTo(Colors.DARK_GREEN + ASCII + Colors.NORMAL); } @Test - public void testIsValidString() throws Exception { + public void testIsValidString() { assertThat(Utils.isValidString(ASCII)).as("isValidString(ascii)").isTrue(); assertThat(Utils.isValidString("")).as("isValidString(empty)").isFalse(); assertThat(Utils.isValidString(" ")).as("isValidString( )").isFalse(); @@ -101,13 +114,13 @@ public class UtilsTest { } @Test - public void testIsoLocalDate() throws Exception { + public void testIsoLocalDate() { assertThat(Utils.isoLocalDate(cal.getTime())).as("isoLocalDate(date)").isEqualTo("1952-02-17"); assertThat(Utils.isoLocalDate(localDateTime)).as("isoLocalDate(localDate)").isEqualTo("1952-02-17"); } @Test - public void testPlural() throws Exception { + public void testPlural() { final String week = "week"; final String weeks = "weeks"; @@ -118,23 +131,23 @@ public class UtilsTest { } @Test - public void testReverseColor() throws Exception { + public void testReverseColor() { assertThat(Utils.reverseColor(ASCII)).isEqualTo(Colors.REVERSE + ASCII + Colors.REVERSE); } @Test - public void testToday() throws Exception { + public void testToday() { assertThat(Utils.today()).isEqualTo(Utils.isoLocalDate(LocalDateTime.now())); } @Test - public void testUnescapeXml() throws Exception { + public void testUnescapeXml() { assertThat(Utils.unescapeXml("<a name="test & ''">")) .isEqualTo("<a name=\"test & ''\">"); } @Test - public void testUtcDateTime() throws Exception { + public void testUtcDateTime() { assertThat(Utils.utcDateTime(cal.getTime())).as("utcDateTime(date)").isEqualTo("1952-02-17 12:30"); assertThat(Utils.utcDateTime(localDateTime)).as("utcDateTime(localDate)") .isEqualTo("1952-02-17 12:30"); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java index 618b5ba..94e3fca 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ final class AbstractModuleTest { - static void testAbstractModule(AbstractModule module) { + static void testAbstractModule(final AbstractModule module) { final String name = module.getClass().getName(); assertThat(module.isEnabled()).as(name + ": enabled").isNotEqualTo(module.hasProperties()); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java index 8dbe467..3510285 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -1,5 +1,5 @@ /* - * LocalProperties.java + * properties.java * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -41,37 +41,37 @@ import java.nio.file.Paths; import java.util.Properties; /** - * The <code>LocalProperties</code> class. + * The <code>properties</code> class. * * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> * @created 2019-04-08 * @since 1.0 */ class LocalProperties { - private static Properties localProperties = new Properties(); + private static final Properties localProps = new Properties(); static String getProperty(final String key) { - if (localProperties.containsKey(key)) { - return localProperties.getProperty(key); + if (localProps.containsKey(key)) { + return localProps.getProperty(key); } else { final String env = System.getenv(keyToEnv(key)); if (env != null) { - localProperties.setProperty(key, env); + localProps.setProperty(key, env); } return env; } } - private static String keyToEnv(String key) { + private static String keyToEnv(final String key) { return key.replaceAll("-", "_").toUpperCase(); } - @BeforeSuite - static void loadLocalProperties() { - final Path localProps = Paths.get("local.properties"); - if (Files.exists(localProps)) { - try (final InputStream stream = Files.newInputStream(localProps)) { - localProperties.load(stream); + @BeforeSuite(alwaysRun = true) + public void loadProperties() { + final Path localPath = Paths.get("local.properties"); + if (Files.exists(localPath)) { + try (final InputStream stream = Files.newInputStream(localPath)) { + localProps.load(stream); } catch (IOException ignore) { // Do nothing } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index bd85a7a..eefe3e9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -60,7 +60,7 @@ public class LookupTest { public void testWhois() throws Exception { final String[] result = Lookup.whois("17.178.96.59", Lookup.WHOIS_HOST); - assertThat(Arrays.asList(result).stream().anyMatch(m -> m.contains("Apple Inc."))) + assertThat(Arrays.stream(result).anyMatch(m -> m.contains("Apple Inc."))) .as("whois(17.178.96.59/Apple Inc.").isTrue(); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java index 1d9cee5..b3b7a71 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java @@ -48,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class ModuleExceptionTest { @DataProvider(name = "dp") - Object[][] createData(Method m) { + Object[][] createData(final Method m) { System.out.println(m.getName()); // print test method name return new Object[][]{new Object[]{new ModuleException("debugMessage", "message", new IOException("Secret URL http://foo.com?apiKey=sec&userID=me"))}, @@ -57,17 +57,17 @@ public class ModuleExceptionTest { } @Test(dataProvider = "dp") - final void testGetDebugMessage(ModuleException e) { + final void testGetDebugMessage(final ModuleException e) { assertThat(e.getDebugMessage()).as("get debug message").isEqualTo("debugMessage"); } @Test(dataProvider = "dp") - final void testGetMessage(ModuleException e) { + final void testGetMessage(final ModuleException e) { assertThat(e.getMessage()).as("get message").isEqualTo("message"); } @Test(dataProvider = "dp") - final void testGetStanitizedMessage(ModuleException e) { + final void testGetStanitizedMessage(final ModuleException e) { if (e.hasCause()) { assertThat(e.getSanitizedMessage()).as("get sanitzed url") .contains("http://foo.com?apiKey=[3]&userID=[2]"); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 796f672..7fbc87a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2019-04-07 * @since 1.0 */ -public class StockQuoteTest { +public class StockQuoteTest extends LocalProperties { @Test public void testGetQuote() throws ModuleException { final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); @@ -56,6 +56,21 @@ public class StockQuoteTest { messages = StockQuote.getQuote("012", apiKey); assertThat(messages.get(0).isError()).as("invalid symbol error").isTrue(); + + try { + StockQuote.getQuote("test", ""); + } catch (Exception e) { + assertThat(e).as("no API key").isInstanceOf(ModuleException.class); + assertThat(e).as("no API key exception has no cause").hasNoCause(); + } + + try { + StockQuote.getQuote("", "apikey"); + } catch (Exception e) { + assertThat(e).as("no symbol").isInstanceOf(ModuleException.class); + assertThat(e).as("no symbol exception has no cause").hasNoCause(); + } + } catch (ModuleException e) { // Avoid displaying api keys in CI logs. if ("true".equals(System.getenv("CI"))) { From 723e5d712c1f8c3f4ca39045bf7867446dd92f72 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 15:59:41 -0700 Subject: [PATCH 229/842] Code cleanup. --- src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java | 2 +- src/main/java/net/thauvin/erik/mobibot/EntryLink.java | 10 +++++----- src/main/java/net/thauvin/erik/mobibot/Tell.java | 2 +- .../java/net/thauvin/erik/mobibot/TellMessagesMgr.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index 19756d8..d2299ae 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -125,7 +125,7 @@ final class EntriesMgr { final String today; - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { + try (final InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); today = Utils.isoLocalDate(feed.getPublishedDate()); diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 94395f9..8820227 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -58,22 +58,22 @@ public class EntryLink implements Serializable { private final List<SyndCategory> tags = new CopyOnWriteArrayList<>(); // The channel - private String channel = ""; + private String channel; // The creation date private Date date = Calendar.getInstance().getTime(); // The link's URL - private String link = ""; + private String link; // The author's login private String login = ""; // The author's nickname - private String nick = ""; + private String nick; // The link's title - private String title = ""; + private String title; /** * Creates a new entry. @@ -184,7 +184,7 @@ public class EntryLink implements Serializable { * @return The comments. */ public final EntryComment[] getComments() { - return (comments.toArray(new EntryComment[comments.size()])); + return (comments.toArray(new EntryComment[0])); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index fd5b98d..de149c8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -144,7 +144,7 @@ public class Tell { helpResponse(sender); } else if (cmds.startsWith(Commands.VIEW_CMD)) { if (bot.isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD)) { - if (messages.isEmpty()) { + if (!messages.isEmpty()) { for (final TellMessage message : messages) { bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + " [ID: " + message.getId() + ", " diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index eb46a1c..8d6cdde 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -89,7 +89,7 @@ final class TellMessagesMgr { public static List<TellMessage> load(final String file, final Logger logger) { try { - try (ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { + try (final ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { if (logger.isDebugEnabled()) { logger.debug("Loading the messages."); } @@ -117,7 +117,7 @@ final class TellMessagesMgr { public static void save(final String file, final List<TellMessage> messages, final Logger logger) { try { - try (ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { + try (final ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { if (logger.isDebugEnabled()) { logger.debug("Saving the messages."); } From 07944f6530758bbc8c4a54dcaf56d864022e9845 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 23:01:42 -0700 Subject: [PATCH 230/842] Implemented PrivateMessage class. --- .../net/thauvin/erik/mobibot/msg/Message.java | 55 ++++++++++++++----- .../erik/mobibot/msg/PrivateMessage.java | 50 +++++++++++++++++ .../erik/mobibot/msg/PublicMessage.java | 2 - 3 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 84d49ef..b86de8a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -44,6 +44,7 @@ public class Message { private String color = Colors.NORMAL; private boolean isError; private boolean isNotice; + private boolean isPrivate; private String msg = ""; /** @@ -56,36 +57,44 @@ public class Message { /** * Creates a new message. * - * @param message The message. - * @param isNotice The notice flag. - * @param isError The error flag. + * @param message The message. + * @param isNotice The notice flag. + * @param isError The error flag. + * @param isPrivate The Private message */ - public Message(final String message, final boolean isNotice, final boolean isError) { + public Message(final String message, final boolean isNotice, final boolean isError, final boolean isPrivate) { msg = message; this.isNotice = isNotice; this.isError = isError; + this.isPrivate = isPrivate; } /** * Creates a new message. * - * @param message The message. - * @param isNotice The notice flag. - * @param isError The error flag - * @param color The color. + * @param message The message. + * @param isNotice The notice flag. + * @param isError The error flag. + * @param isPrivate The Private message + * @param color The color. */ - public Message(final String message, final boolean isNotice, final boolean isError, final String color) { + public Message(final String message, + final boolean isNotice, + final boolean isError, + final boolean isPrivate, + final String color) { msg = message; this.isNotice = isNotice; this.isError = isError; + this.isPrivate = isPrivate; this.color = color; } /** - * Returns the color. + * Returns the message color. * - * @return The color, + * @return The color. */ public String getColor() { return color; @@ -119,7 +128,7 @@ public class Message { } /** - * Returns the error flag. + * Returns the message error flag. * * @return The error flag. */ @@ -128,7 +137,7 @@ public class Message { } /** - * Sets the error flag. + * Sets the message error flag. * * @param error The error flag. */ @@ -146,11 +155,29 @@ public class Message { } /** - * Set the message notice flag. + * Sets the message notice flag. * * @param isNotice The notice flag. */ public void setNotice(final boolean isNotice) { this.isNotice = isNotice; } + + /** + * Returns the message private flag. + * + * @return The private flag. + */ + public boolean isPrivate() { + return isPrivate; + } + + /** + * Sets the message private flag. + * + * @param aPrivate The private flag. + */ + public void setPrivate(boolean aPrivate) { + isPrivate = aPrivate; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java new file mode 100644 index 0000000..b8a3541 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java @@ -0,0 +1,50 @@ +/* + * PrivateMessage.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg; + +/** + * The <code>PrivateMessage</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-09 + * @since 1.0 + */ +public class PrivateMessage extends Message { + public PrivateMessage(final String message) { + this.setMessage(message); + } + + public PrivateMessage(final String message, final String color) { + this.setMessage(message); + this.setColor(color); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java index b47da23..d7556b1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java @@ -41,12 +41,10 @@ package net.thauvin.erik.mobibot.msg; public class PublicMessage extends Message { public PublicMessage(final String message) { this.setMessage(message); - this.setNotice(false); } public PublicMessage(final String message, final String color) { this.setMessage(message); - this.setNotice(false); this.setColor(color); } } From 9b2866e30c176b4ea30643554b0a1cef53fab009 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 23:03:45 -0700 Subject: [PATCH 231/842] Code cleanup. --- .../thauvin/erik/mobibot/modules/Dice.java | 4 +-- .../thauvin/erik/mobibot/modules/Lookup.java | 27 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 6ad915d..8c8085b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -71,8 +71,8 @@ public final class Dice extends AbstractModule { final int playerTotal = i + y; bot.send(bot.getChannel(), - sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils - .bold(playerTotal)); + sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + + Utils.bold(playerTotal)); i = r.nextInt(6) + 1; y = r.nextInt(6) + 1; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 3f6d8e2..014b9ad 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -116,25 +116,24 @@ public final class Lookup extends AbstractModule { * @return The IP whois data, if any. * @throws java.io.IOException If a connection error occurs. */ - @SuppressWarnings("SameParameterValue") public static String[] whois(final String query, final String host) throws IOException { - final WhoisClient whois = new WhoisClient(); + final WhoisClient whoisClient = new WhoisClient(); final String[] lines; try { - whois.setDefaultTimeout(Mobibot.CONNECT_TIMEOUT); - whois.connect(host); - whois.setSoTimeout(Mobibot.CONNECT_TIMEOUT); - whois.setSoLinger(false, 0); + whoisClient.setDefaultTimeout(Mobibot.CONNECT_TIMEOUT); + whoisClient.connect(host); + whoisClient.setSoTimeout(Mobibot.CONNECT_TIMEOUT); + whoisClient.setSoLinger(false, 0); if (host.equals(WHOIS_HOST)) { - lines = whois.query("n - " + query).split("\n"); + lines = whoisClient.query("n - " + query).split("\n"); } else { - lines = whois.query(query).split("\n"); + lines = whoisClient.query(query).split("\n"); } } finally { - whois.disconnect(); + whoisClient.disconnect(); } return lines; @@ -152,7 +151,7 @@ public final class Lookup extends AbstractModule { public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (args.matches("(\\S.)+(\\S)+")) { try { - bot.send(bot.getChannel(), Lookup.lookup(args)); + bot.send(Lookup.lookup(args)); } catch (UnknownHostException ignore) { if (args.matches( "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + @@ -167,21 +166,21 @@ public final class Lookup extends AbstractModule { line = rawLine.trim(); if ((line.length() > 0) && (line.charAt(0) != '#')) { - bot.send(bot.getChannel(), line); + bot.send(line); } } } else { - bot.send(bot.getChannel(), "Unknown host."); + bot.send("Unknown host."); } } catch (IOException ioe) { if (bot.getLogger().isDebugEnabled()) { bot.getLogger().debug("Unable to perform whois IP lookup: " + args, ioe); } - bot.send(bot.getChannel(), "Unable to perform whois IP lookup: " + ioe.getMessage()); + bot.send("Unable to perform whois IP lookup: " + ioe.getMessage()); } } else { - bot.send(bot.getChannel(), "Unknown host."); + bot.send("Unknown host."); } } } else { From 2b4f1af63cda8ff52912f22715100b63d4f72e90 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 23:03:58 -0700 Subject: [PATCH 232/842] Don't colorize Colors.NORMAL. --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 4c7d8d5..648ad7d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -159,10 +159,13 @@ public final class Utils { * @param color The color. * @return The colorized string. */ - public static String colorize(final String s, final String color) { - if (color.equals(Colors.BOLD) || color.equals(Colors.REVERSE)) { + static String colorize(final String s, final String color) { + if (!Utils.isValidString(color) || color.equals(Colors.NORMAL)) { + return s; + } else if (color.equals(Colors.BOLD) || color.equals(Colors.REVERSE)) { return color + s + color; } + return color + s + Colors.NORMAL; } From a4907ce111e3b353e275f8bd0797be40de273a52 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 9 Apr 2019 23:04:38 -0700 Subject: [PATCH 233/842] Implemented new send methods. --- .../net/thauvin/erik/mobibot/Mobibot.java | 42 +++++++++++++++++-- .../net/thauvin/erik/mobibot/Pinboard.java | 17 ++++---- .../thauvin/erik/mobibot/modules/Calc.java | 2 +- .../mobibot/modules/CurrencyConverter.java | 2 +- .../erik/mobibot/modules/GoogleSearch.java | 13 ++---- .../thauvin/erik/mobibot/modules/Joke.java | 2 +- .../erik/mobibot/modules/StockQuote.java | 4 +- .../net/thauvin/erik/mobibot/modules/War.java | 2 +- .../erik/mobibot/modules/Weather2.java | 4 +- .../erik/mobibot/modules/WorldTime.java | 2 +- 10 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index f7c7948..c0e9dc3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -34,12 +34,14 @@ package net.thauvin.erik.mobibot; import com.rometools.rome.io.FeedException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.modules.*; +import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.*; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; +import org.jibble.pircbot.Colors; import org.jibble.pircbot.PircBot; import org.jibble.pircbot.User; import org.jsoup.Jsoup; @@ -796,7 +798,7 @@ public class Mobibot extends PircBot { private void infoResponse(final String sender, final boolean isPrivate) { for (final String info : INFO_STRS) { if (info.startsWith("https://")) { - send(sender, Utils.green(info), isPrivate); + send(sender, info, Colors.DARK_GREEN, isPrivate); } else { send(sender, info, isPrivate); } @@ -1429,20 +1431,54 @@ public class Mobibot extends PircBot { } } + /** + * Sends a notice to the channel. + * + * @param notice The notice message. + */ + public final void send(final String notice) { + send(getChannel(), notice); + + } + /** * Sends a message. * - * @param who The channel or nick of the person who sent the message. + * @param who The channel or nick of the person who sent the command. * @param message The actual message. */ public final void send(final String who, final String message) { send(who, message, false); } + /** * Sends a message. * - * @param who The channel or nick of the person who sent the message. + * @param who The channel or nick of the person who sent the command. + * @param message The message. + */ + public final void send(final String who, final Message message) { + send(message.isNotice() ? who : getChannel(), message.getMessage(), message.getColor(), message.isPrivate()); + } + + /** + * Sends a message. + * + * @param who The channel or nick of the person who sent the command. + * @param message The actual message. + * @param color The message's color. + * @param isPrivate The private flag. + */ + public final void send(final String who, final String message, final String color, boolean isPrivate) { + send(who, Utils.colorize(message, color), isPrivate); + } + + + /** + * Sends a message. + * + * @param who The channel or nick of the person who sent the command. * @param message The actual message. * @param color The message's color. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index f8e9b61..5682456 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -60,7 +60,7 @@ class Pinboard { * @param apiToken The API end point. * @param ircServer The IRC server. */ - public Pinboard(final Mobibot bot, final String apiToken, final String ircServer) { + Pinboard(final Mobibot bot, final String apiToken, final String ircServer) { pinboard = new PinboardPoster(apiToken); this.ircServer = ircServer; @@ -78,11 +78,10 @@ class Pinboard { * * @param entry The entry to add. */ - public final void addPost(final EntryLink entry) { + final void addPost(final EntryLink entry) { final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override - protected Boolean doInBackground() - throws Exception { + protected Boolean doInBackground() { return pinboard.addPin(entry.getLink(), entry.getTitle(), postedBy(entry), @@ -99,13 +98,12 @@ class Pinboard { * * @param entry The entry to delete. */ - public final void deletePost(final EntryLink entry) { + final void deletePost(final EntryLink entry) { final String link = entry.getLink(); final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override - protected Boolean doInBackground() - throws Exception { + protected Boolean doInBackground() { return pinboard.deletePin(link); } }; @@ -139,11 +137,10 @@ class Pinboard { * @param oldUrl The old post URL. * @param entry The entry to add. */ - public final void updatePost(final String oldUrl, final EntryLink entry) { + final void updatePost(final String oldUrl, final EntryLink entry) { final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override - protected Boolean doInBackground() - throws Exception { + protected Boolean doInBackground() { if (!oldUrl.equals(entry.getLink())) { pinboard.deletePin(oldUrl); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 2c2a513..a862652 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -81,7 +81,7 @@ public class Calc extends AbstractModule { @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (Utils.isValidString(args)) { - bot.send(bot.getChannel(), calc(args)); + bot.send(calc(args)); } else { helpResponse(bot, sender, args, isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 88a7f8f..3e9ac77 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -199,7 +199,7 @@ public final class CurrencyConverter extends ThreadedModule { if (msg.isError()) { helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false); } - bot.send(msg.isNotice() ? sender : bot.getChannel(), msg.getMessage()); + bot.send(sender, msg); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 1fd4e6c..9a4af37 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -36,6 +36,7 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; +import org.jibble.pircbot.Colors; import org.json.JSONArray; import org.json.JSONObject; @@ -118,7 +119,8 @@ public final class GoogleSearch extends ThreadedModule { for (int i = 0; i < ja.length(); i++) { final JSONObject j = ja.getJSONObject(i); results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); - results.add(new NoticeMessage(j.getString("link"))); + results.add( + new NoticeMessage(TAB_INDENT + j.getString("link"), Colors.DARK_GREEN)); } } } catch (IOException e) { @@ -152,15 +154,8 @@ public final class GoogleSearch extends ThreadedModule { try { final ArrayList<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), properties.get(GOOGLE_CSE_KEY_PROP)); - - int i = 0; for (final Message msg : results) { - if (i % 2 == 0) { - bot.send(sender, Utils.green(TAB_INDENT + msg.getMessage())); - } else { - bot.send(sender, msg.getMessage()); - } - i++; + bot.send(sender, msg); } } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 5f40429..5b01d17 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -108,7 +108,7 @@ public final class Joke extends ThreadedModule { */ void run(final Mobibot bot, final String sender, String arg) { try { - bot.send(bot.getChannel(), Utils.cyan(randomJoke().getMessage())); + bot.send(Utils.cyan(randomJoke().getMessage())); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index e8f7a14..3805592 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -170,11 +170,11 @@ public final class StockQuote extends ThreadedModule { try { final ArrayList<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); for (final Message msg : messages) { - bot.send(msg.isNotice() ? sender : bot.getChannel(), msg.getMessage()); + bot.send(sender, msg); } } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(bot.getChannel(), e.getMessage()); + bot.send(e.getMessage()); } } else { helpResponse(bot, sender, symbol, true); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 74a8f33..3f6349b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -86,7 +86,7 @@ public final class War extends AbstractModule { break; } - bot.send(bot.getChannel(), "This means " + Utils.bold("WAR") + '!'); + bot.send("This means " + Utils.bold("WAR") + '!'); } if (i < y) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 282210e..51c176e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -197,12 +197,12 @@ public class Weather2 extends ThreadedModule { helpResponse(bot, sender, args, true); } else { for (final Message msg : messages) { - bot.send(msg.isNotice() ? bot.getChannel() : sender, msg.getMessage(), msg.getColor()); + bot.send(sender, msg); } } } catch (ModuleException e) { bot.getLogger().debug(e.getDebugMessage(), e); - bot.send(bot.getChannel(), e.getMessage()); + bot.send(e.getMessage()); } } else { helpResponse(bot, sender, args, true); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 0922f1e..01842c5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -200,7 +200,7 @@ public final class WorldTime extends AbstractModule { if (msg.isError()) { bot.send(sender, msg.getMessage()); } else { - bot.send(bot.getChannel(), msg.getMessage()); + bot.send(msg.getMessage()); } } } From 32e827d50afb759abdaf5b693bce4bc04773e67e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 Apr 2019 00:18:27 -0700 Subject: [PATCH 234/842] Fixed public messages. --- .../net/thauvin/erik/mobibot/modules/Weather2.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 51c176e..5c8f649 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -117,23 +117,23 @@ public class Weather2 extends ThreadedModule { cwd = owm.currentWeatherByCityName(city, getCountry(country)); } if (cwd.hasCityName()) { - messages.add(new NoticeMessage("City: " + cwd.getCityName() + " [" + country + "]")); + messages.add(new PublicMessage("City: " + cwd.getCityName() + " [" + country + "]")); final Main main = cwd.getMainData(); if (main != null) { if (main.hasTemp()) { - messages.add(new NoticeMessage("Temperature: " + fAndC(main.getTemp()))); + messages.add(new PublicMessage("Temperature: " + fAndC(main.getTemp()))); } if (main.hasHumidity() && (main.getHumidity() != null)) { - messages.add(new PublicMessage("Humidity: " + Math.round(main.getHumidity()) + "%")); + messages.add(new NoticeMessage("Humidity: " + Math.round(main.getHumidity()) + "%")); } } if (cwd.hasWindData()) { final Wind w = cwd.getWindData(); if (w != null && w.hasSpeed()) { - messages.add(new PublicMessage("Wind: " + wind(w.getSpeed()))); + messages.add(new NoticeMessage("Wind: " + wind(w.getSpeed()))); } } @@ -148,7 +148,7 @@ public class Weather2 extends ThreadedModule { condition.append(", ").append(w.getDescription()); } } - messages.add(new PublicMessage(condition.toString())); + messages.add(new NoticeMessage(condition.toString())); } } messages.add(new NoticeMessage("https://openweathermap.org/city/" From 6460976a9cba938c23943806084dae14f5c5d33c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 Apr 2019 00:20:10 -0700 Subject: [PATCH 235/842] 0.7.3-beta+260 --- .idea/modules/mobibot.iml | 6 ++---- mobibot.ipr | 16 ---------------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- version.properties | 4 ++-- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 9fd394e..a223542 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+151" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+204" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -31,10 +31,8 @@ <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20180813" level="project" /> - <orderEntry type="library" name="Gradle: org.ostermiller:utils:1.07.00" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.11.3" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.patriques:alphavantage4j:1.4" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.11" level="project" /> @@ -46,7 +44,7 @@ <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> diff --git a/mobibot.ipr b/mobibot.ipr index 2bb2f68..0a57b34 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -717,22 +717,6 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.3/871302b15d8cee9bfb393c4f1d0386b17646d8d1/jsoup-1.11.3-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.ostermiller:utils:1.07.00"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/a8828217b2dd0507fbe9e9d0b2981acfb908b590/utils-1.07.00.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.ostermiller/utils/1.07.00/586774ee4b8409b6835621bae2186d9b54d1c36a/utils-1.07.00-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.patriques:alphavantage4j:1.4"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.patriques/alphavantage4j/1.4/36248e503759c89bd402aa5188ae6cc81596656/alphavantage4j-1.4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> <library name="Gradle: org.slf4j:slf4j-api:1.7.25"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index b8d456e..dd3f500 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,12 +14,12 @@ import java.time.*; public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1554712215517L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1554878934411L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "172"; + public final static String BUILDMETA = "260"; /** * The full semantic version string. diff --git a/version.properties b/version.properties index 4cad559..31d415a 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Mon Apr 08 01:30:11 PDT 2019 -version.buildmeta=172 +#Tue Apr 09 23:48:43 PDT 2019 +version.buildmeta=260 version.major=0 version.minor=7 version.patch=3 From 3f5d0117197373506772e4dc5b16e0d519108195 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 Apr 2019 00:48:37 -0700 Subject: [PATCH 236/842] Fixed command response, no args. --- .../java/net/thauvin/erik/mobibot/modules/Joke.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 5b01d17..cbd8084 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -51,11 +51,11 @@ import java.nio.charset.StandardCharsets; * @since 1.0 */ public final class Joke extends ThreadedModule { + // The joke command. + private static final String JOKE_CMD = "joke"; // The ICNDB URL. private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; - // The joke command. - private static final String JOKE_CMD = "joke"; /** * Creates a new {@link Joke} instance. @@ -94,6 +94,14 @@ public final class Joke extends ThreadedModule { } } + /** + * {@inheritDoc} + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + new Thread(() -> run(bot, sender, args)).start(); + } + /** * {@inheritDoc} */ From d08f9a2d0f1b54ade528a2c7098556abd6631fad Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 10 Apr 2019 00:50:12 -0700 Subject: [PATCH 237/842] 0.7.3-beta+26 --- src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java | 4 ++-- version.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index dd3f500..5c48762 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,12 +14,12 @@ import java.time.*; public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1554878934411L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1554881826135L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "260"; + public final static String BUILDMETA = "261"; /** * The full semantic version string. diff --git a/version.properties b/version.properties index 31d415a..d553894 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Tue Apr 09 23:48:43 PDT 2019 -version.buildmeta=260 +#Wed Apr 10 00:37:04 PDT 2019 +version.buildmeta=261 version.major=0 version.minor=7 version.patch=3 From 4b772f0564d208da68e849bf17c69b22d4b7877d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 13 Apr 2019 04:50:41 -0700 Subject: [PATCH 238/842] Cleanup. --- .../thauvin/erik/mobibot/modules/ModuleException.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 9dcd6a4..9bf8788 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -101,14 +101,11 @@ class ModuleException extends Exception { final Matcher matcher = urlPattern.matcher(causeMessage); if (matcher.find()) { final HttpUrl url = HttpUrl.parse(matcher.group()); - if ((url != null) && (matcher.group().contains("?")) && (url.querySize() > 0)) { + if ((url != null) && (matcher.group().contains("?"))) { final StringBuilder query = new StringBuilder(); for (int i = 0, size = url.querySize(); i < size; i++) { - if (query.length() > 0) { - query.append("&"); - } - query.append(url.queryParameterName(i)).append('=') - .append('[').append(url.queryParameterValue(i).length()).append(']'); + query.append(url.queryParameterName(i)).append('=').append('[') + .append(url.queryParameterValue(i).length()).append(']').append('&'); } return getDebugMessage() + "\nCaused by: " + getCause().getClass().getName() + ": " + causeMessage.replace(matcher.group(), @@ -116,7 +113,6 @@ class ModuleException extends Exception { } } } - return getMessage(); } From 8bbeb8d7c8a009bc0ea0407ea04a8fad1c534555 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 14 Apr 2019 19:03:46 -0700 Subject: [PATCH 239/842] Better query parameters handling in getSanitizedMessage(). --- .../erik/mobibot/modules/ModuleException.java | 16 ++++++++-------- ...erterTest.java => CurrencyConverterTest.java} | 0 .../mobibot/modules/ModuleExceptionTest.java | 16 ++++++++++++---- 3 files changed, 20 insertions(+), 12 deletions(-) rename src/test/java/net/thauvin/erik/mobibot/modules/{CurrentConverterTest.java => CurrencyConverterTest.java} (100%) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 9bf8788..9c3009c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -46,7 +46,7 @@ import java.util.regex.Pattern; class ModuleException extends Exception { private static final long serialVersionUID = -3036774290621088107L; private final String debugMessage; - private final Pattern urlPattern = Pattern.compile("(https?://\\S+)"); + private final Pattern urlPattern = Pattern.compile("(https?://\\S+)(\\?\\S+)"); /** * Creates a new exception. @@ -100,20 +100,20 @@ class ModuleException extends Exception { final String causeMessage = getCause().getMessage(); final Matcher matcher = urlPattern.matcher(causeMessage); if (matcher.find()) { - final HttpUrl url = HttpUrl.parse(matcher.group()); - if ((url != null) && (matcher.group().contains("?"))) { - final StringBuilder query = new StringBuilder(); + final HttpUrl url = HttpUrl.parse(matcher.group(1)+matcher.group(2)); + if (url != null){ + final StringBuilder query = new StringBuilder("?"); for (int i = 0, size = url.querySize(); i < size; i++) { + if (i > 0) query.append('&'); query.append(url.queryParameterName(i)).append('=').append('[') - .append(url.queryParameterValue(i).length()).append(']').append('&'); + .append(url.queryParameterValue(i).length()).append(']'); } return getDebugMessage() + "\nCaused by: " + getCause().getClass().getName() + ": " - + causeMessage.replace(matcher.group(), - matcher.group().substring(0, matcher.group().indexOf('?') + 1) + query); + + causeMessage.replace(matcher.group(2), query); } } } - return getMessage(); + return getDebugMessage() + "\nCaused by: " + getCause().getClass().getName() + ": " + getCause().getMessage(); } /** diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/CurrentConverterTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java index b3b7a71..aa639e3 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java @@ -49,9 +49,12 @@ import static org.assertj.core.api.Assertions.assertThat; public class ModuleExceptionTest { @DataProvider(name = "dp") Object[][] createData(final Method m) { - System.out.println(m.getName()); // print test method name return new Object[][]{new Object[]{new ModuleException("debugMessage", "message", new IOException("Secret URL http://foo.com?apiKey=sec&userID=me"))}, + new Object[]{new ModuleException("debugMessage", "message", + new IOException("URL http://foobar.com"))}, + new Object[]{new ModuleException("debugMessage", "message", + new IOException("URL http://foobar.com?"))}, new Object[]{new ModuleException("debugMessage", "message")} }; } @@ -67,10 +70,15 @@ public class ModuleExceptionTest { } @Test(dataProvider = "dp") - final void testGetStanitizedMessage(final ModuleException e) { + final void testGetSanitizedMessage(final ModuleException e) { if (e.hasCause()) { - assertThat(e.getSanitizedMessage()).as("get sanitzed url") - .contains("http://foo.com?apiKey=[3]&userID=[2]"); + if (e.getSanitizedMessage().contains("Secret")) { + assertThat(e.getSanitizedMessage()).as("get sanitized url") + .contains("http://foo.com?apiKey=[3]&userID=[2]"); + } else { + assertThat(e.getSanitizedMessage()).as("get sanitized url") + .contains("http://foobar.com"); + } } } } From 316061e69b7c70a3a79fd22182a4bd585e1e827f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 14 Apr 2019 19:04:41 -0700 Subject: [PATCH 240/842] Renamed. --- .../net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index fb2869d..a134ca4 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2019-04-07 * @since 1.0 */ -public class CurrentConverterTest { +public class CurrencyConverterTest { @Test public void testCurrencyConvertererImpl() { AbstractModuleTest.testAbstractModule(new CurrencyConverter()); From 7c724db67eb302f55ee638937e918bcc1b149fc6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 14 Apr 2019 19:05:02 -0700 Subject: [PATCH 241/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/modules/Calc.java | 4 +--- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.java | 5 ++++- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index a862652..22810fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -46,9 +46,7 @@ import java.text.DecimalFormat; * @since 1.0 */ public class Calc extends AbstractModule { - /** - * The Calc command. - */ + // The Calc command. private static final String CALC_CMD = "calc"; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 3e9ac77..d45fa32 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -60,8 +60,11 @@ import java.util.TreeMap; * @since 1.0 */ public final class CurrencyConverter extends ThreadedModule { - // The rates keyword. + /** + * The rates keyword. + */ static final String CURRENCY_RATES_KEYWORD = "rates"; + // The currency command. private static final String CURRENCY_CMD = "currency"; // The exchange rates. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 5c8f649..6fffb11 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -59,7 +59,7 @@ public class Weather2 extends ThreadedModule { /** * The OpenWeatherMap API Key property. */ - public static final String OWM_API_KEY_PROP = "owm-api-key"; + static final String OWM_API_KEY_PROP = "owm-api-key"; // The weather command. private static final String WEATHER_CMD = "weather"; From 64ffd95aba335b3b9a7c0f4fdd5b258321914b66 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 17 Apr 2019 00:23:02 -0700 Subject: [PATCH 242/842] Checkstyle fixes. --- build.gradle | 8 + config/checkstyle/checkstyle.xml | 267 ++++++++++++++++++ mobibot.ipr | 18 +- .../net/thauvin/erik/mobibot/Commands.java | 1 + .../net/thauvin/erik/mobibot/EntriesMgr.java | 23 +- .../thauvin/erik/mobibot/EntryComment.java | 1 + .../net/thauvin/erik/mobibot/EntryLink.java | 19 +- .../net/thauvin/erik/mobibot/FeedReader.java | 1 + .../net/thauvin/erik/mobibot/Mobibot.java | 135 +++++---- .../net/thauvin/erik/mobibot/Pinboard.java | 3 +- .../java/net/thauvin/erik/mobibot/Tell.java | 12 +- .../net/thauvin/erik/mobibot/TellMessage.java | 1 + .../thauvin/erik/mobibot/TellMessagesMgr.java | 19 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 7 +- .../java/net/thauvin/erik/mobibot/Utils.java | 7 +- .../erik/mobibot/modules/AbstractModule.java | 7 +- .../thauvin/erik/mobibot/modules/Calc.java | 1 + .../mobibot/modules/CurrencyConverter.java | 5 +- .../thauvin/erik/mobibot/modules/Dice.java | 1 + .../erik/mobibot/modules/GoogleSearch.java | 1 + .../thauvin/erik/mobibot/modules/Joke.java | 3 +- .../thauvin/erik/mobibot/modules/Lookup.java | 7 +- .../erik/mobibot/modules/ModuleException.java | 9 +- .../thauvin/erik/mobibot/modules/Ping.java | 1 + .../erik/mobibot/modules/StockQuote.java | 1 + .../erik/mobibot/modules/ThreadedModule.java | 1 + .../thauvin/erik/mobibot/modules/Twitter.java | 1 + .../net/thauvin/erik/mobibot/modules/War.java | 1 + .../erik/mobibot/modules/Weather2.java | 6 +- .../erik/mobibot/modules/WorldTime.java | 11 +- .../erik/mobibot/msg/ErrorMessage.java | 13 + .../net/thauvin/erik/mobibot/msg/Message.java | 9 +- .../erik/mobibot/msg/NoticeMessage.java | 12 + .../erik/mobibot/msg/PrivateMessage.java | 1 + .../erik/mobibot/msg/PublicMessage.java | 1 + .../net/thauvin/erik/mobibot/UtilsTest.java | 1 + .../mobibot/modules/AbstractModuleTest.java | 1 + .../erik/mobibot/modules/CalcTest.java | 1 + .../modules/CurrencyConverterTest.java | 1 + .../mobibot/modules/GoogleSearchTest.java | 1 + .../erik/mobibot/modules/JokeTest.java | 1 + .../erik/mobibot/modules/LocalProperties.java | 1 + .../erik/mobibot/modules/LookupTest.java | 1 + .../mobibot/modules/ModuleExceptionTest.java | 1 + .../erik/mobibot/modules/PingTest.java | 4 +- .../erik/mobibot/modules/StockQuoteTest.java | 1 + .../erik/mobibot/modules/Weather2Test.java | 1 + .../erik/mobibot/modules/WordTimeTest.java | 1 + 48 files changed, 512 insertions(+), 118 deletions(-) create mode 100644 config/checkstyle/checkstyle.xml diff --git a/build.gradle b/build.gradle index 79fc849..77a712c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'checkstyle' id 'application' id 'idea' id 'jacoco' @@ -128,6 +129,13 @@ tasks.withType(SpotBugsTask) { excludeFilter = file("$projectDir/config/spotbugs/excludeFilter.xml") } +tasks.withType(Checkstyle) { + reports { + xml.enabled = false + html.enabled = true + } +} + task copyToDeploy(type: Copy) { from('properties') from jar diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000..c426276 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,267 @@ +<?xml version="1.0"?> +<!DOCTYPE module PUBLIC + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + +<!-- + Checkstyle configuration that checks the Google coding conventions from Google Java Style + that can be found at https://google.github.io/styleguide/javaguide.html. + + Checkstyle is very configurable. Be sure to read the documentation at + http://checkstyle.sf.net (or in your downloaded distribution). + + To completely disable a check, just comment it out or delete it from the file. + + Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov. + --> + +<module name="Checker"> + <property name="charset" value="UTF-8"/> + + <property name="severity" value="warning"/> + + <property name="fileExtensions" value="java, properties, xml"/> + <!-- Excludes all 'module-info.java' files --> + <!-- See https://checkstyle.org/config_filefilters.html --> + <module name="BeforeExecutionExclusionFileFilter"> + <property name="fileNamePattern" value="module\-info\.java$"/> + </module> + <!-- Checks for whitespace --> + <!-- See http://checkstyle.sf.net/config_whitespace.html --> + <module name="FileTabCharacter"> + <property name="eachLine" value="true"/> + </module> + + <module name="SuppressWarningsFilter"/> + + <module name="TreeWalker"> + <module name="SuppressWarningsHolder"/> + <module name="OuterTypeFilename"/> + <module name="IllegalTokenText"> + <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/> + <property name="format" + value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/> + <property name="message" + value="Consider using special escape sequence instead of octal value or Unicode escaped value."/> + </module> + <module name="AvoidEscapedUnicodeCharacters"> + <property name="allowEscapesForControlCharacters" value="true"/> + <property name="allowByTailComment" value="true"/> + <property name="allowNonPrintableEscapes" value="true"/> + </module> + <module name="LineLength"> + <property name="max" value="120"/> + <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> + </module> + <module name="AvoidStarImport"/> + <module name="OneTopLevelClass"/> + <module name="NoLineWrap"/> + <module name="EmptyBlock"> + <property name="option" value="TEXT"/> + <property name="tokens" + value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/> + </module> + <module name="NeedBraces"/> + <module name="LeftCurly"/> + <module name="RightCurly"> + <property name="id" value="RightCurlySame"/> + <property name="tokens" + value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, + LITERAL_DO"/> + </module> + <module name="RightCurly"> + <property name="id" value="RightCurlyAlone"/> + <property name="option" value="alone"/> + <property name="tokens" + value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, + INSTANCE_INIT"/> + </module> + <module name="WhitespaceAround"> + <property name="allowEmptyConstructors" value="true"/> + <property name="allowEmptyLambdas" value="true"/> + <property name="allowEmptyMethods" value="true"/> + <property name="allowEmptyTypes" value="true"/> + <property name="allowEmptyLoops" value="true"/> + <message key="ws.notFollowed" + value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> + <message key="ws.notPreceded" + value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/> + </module> + <module name="OneStatementPerLine"/> + <module name="MultipleVariableDeclarations"/> + <module name="ArrayTypeStyle"/> + <module name="MissingSwitchDefault"/> + <module name="FallThrough"/> + <module name="UpperEll"/> + <module name="ModifierOrder"/> + <module name="EmptyLineSeparator"> + <property name="allowNoEmptyLineBetweenFields" value="true"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapDot"/> + <property name="tokens" value="DOT"/> + <property name="option" value="nl"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapComma"/> + <property name="tokens" value="COMMA"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 --> + <property name="id" value="SeparatorWrapEllipsis"/> + <property name="tokens" value="ELLIPSIS"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 --> + <property name="id" value="SeparatorWrapArrayDeclarator"/> + <property name="tokens" value="ARRAY_DECLARATOR"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapMethodRef"/> + <property name="tokens" value="METHOD_REF"/> + <property name="option" value="nl"/> + </module> + <module name="PackageName"> + <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> + <message key="name.invalidPattern" + value="Package name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="TypeName"> + <message key="name.invalidPattern" + value="Type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="MemberName"> + <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/> + <message key="name.invalidPattern" + value="Member name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="ParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="LambdaParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="CatchParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Catch parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="LocalVariableName"> + <property name="tokens" value="VARIABLE_DEF"/> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Local variable name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="ClassTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Class type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="MethodTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Method type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="InterfaceTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Interface type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="NoFinalizer"/> + <module name="GenericWhitespace"> + <message key="ws.followed" + value="GenericWhitespace ''{0}'' is followed by whitespace."/> + <message key="ws.preceded" + value="GenericWhitespace ''{0}'' is preceded with whitespace."/> + <message key="ws.illegalFollow" + value="GenericWhitespace ''{0}'' should followed by whitespace."/> + <message key="ws.notPreceded" + value="GenericWhitespace ''{0}'' is not preceded with whitespace."/> + </module> + <module name="Indentation"> + <property name="basicOffset" value="4"/> + <property name="braceAdjustment" value="0"/> + <property name="caseIndent" value="4"/> + <property name="throwsIndent" value="4"/> + <property name="lineWrappingIndentation" value="4"/> + <property name="arrayInitIndent" value="4"/> + </module> + <module name="AbbreviationAsWordInName"> + <property name="ignoreFinal" value="false"/> + <property name="allowedAbbreviationLength" value="1"/> + </module> + <module name="OverloadMethodsDeclarationOrder"/> + <module name="VariableDeclarationUsageDistance"/> + <module name="CustomImportOrder"> + <property name="customImportOrderRules" + value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/> + <property name="specialImportsRegExp" value="^javax\."/> + <property name="standardPackageRegExp" value="^java\."/> + <property name="sortImportsInGroupAlphabetically" value="true"/> + <property name="separateLineBetweenGroups" value="false"/> + </module> + <module name="MethodParamPad"/> + <module name="NoWhitespaceBefore"> + <property name="tokens" + value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/> + <property name="allowLineBreaks" value="true"/> + </module> + <module name="ParenPad"/> + <module name="OperatorWrap"> + <property name="option" value="NL"/> + <property name="tokens" + value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, + LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/> + </module> + <module name="AnnotationLocation"> + <property name="id" value="AnnotationLocationMostCases"/> + <property name="tokens" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/> + </module> + <module name="AnnotationLocation"> + <property name="id" value="AnnotationLocationVariables"/> + <property name="tokens" value="VARIABLE_DEF"/> + <property name="allowSamelineMultipleAnnotations" value="true"/> + </module> + <module name="NonEmptyAtclauseDescription"/> + <module name="JavadocTagContinuationIndentation"/> + <module name="SummaryJavadoc"> + <property name="forbiddenSummaryFragments" + value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/> + </module> + <module name="JavadocParagraph"/> + <module name="AtclauseOrder"> + <property name="tagOrder" value="@param, @return, @throws, @deprecated"/> + <property name="target" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/> + </module> + <module name="JavadocMethod"> + <property name="scope" value="public"/> + <property name="allowMissingParamTags" value="true"/> + <property name="allowMissingThrowsTags" value="true"/> + <property name="allowMissingReturnTag" value="true"/> + <property name="minLineCount" value="2"/> + <property name="allowedAnnotations" value="Override, Test"/> + <property name="allowThrowsTagsForSubclasses" value="true"/> + </module> + <module name="MethodName"> + <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/> + <message key="name.invalidPattern" + value="Method name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="SingleLineJavadoc"> + <property name="ignoreInlineTags" value="false"/> + </module> + <module name="EmptyCatchBlock"> + <property name="exceptionVariableName" value="expected"/> + </module> + <module name="CommentsIndentation"/> + </module> +</module> diff --git a/mobibot.ipr b/mobibot.ipr index 0a57b34..fbc1a69 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1,5 +1,20 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> + <component name="CheckStyle-IDEA"> + <option name="configuration"> + <map> + <entry key="active-configuration" value="LOCAL_FILE:$PROJECT_DIR$/config/checkstyle/checkstyle.xml:Erik's Checks" /> + <entry key="checkstyle-version" value="8.19" /> + <entry key="copy-libs" value="true" /> + <entry key="location-0" value="BUNDLED:(bundled):Sun Checks" /> + <entry key="location-1" value="BUNDLED:(bundled):Google Checks" /> + <entry key="location-2" value="LOCAL_FILE:$PROJECT_DIR$/config/checkstyle/checkstyle.xml:Erik's Checks" /> + <entry key="scan-before-checkin" value="false" /> + <entry key="scanscope" value="JavaOnlyWithTests" /> + <entry key="suppress-errors" value="false" /> + </map> + </option> + </component> <component name="ClientPropertiesManager"> <properties class="javax.swing.AbstractButton"> <property name="hideActionText" class="java.lang.Boolean" /> @@ -47,7 +62,6 @@ <element module="Source" copyright="Copyright" /> </module2copyright> <LanguageOptions name="JAVA"> - <option name="fileTypeOverride" value="3" /> <option name="addBlankAfter" value="false" /> </LanguageOptions> <LanguageOptions name="__TEMPLATE__"> @@ -55,7 +69,7 @@ </LanguageOptions> </component> <component name="DependencyValidationManager"> - <scope name="Source" pattern="(file[mobibot]:src/generated/java//*.java||file[mobibot]:src/main/java//*.java||file[mobibot]:src/test/java//*.java)&&!file[mobibot]:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> + <scope name="Copryight" pattern="(file[mobibot]:src/generated/java//*.java||file[mobibot]:src/main/java//*.java||file[mobibot]:src/test/java//*.java)&&!file[mobibot]:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> </component> <component name="Encoding"> <file url="PROJECT" charset="UTF-8" /> diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index a6487a1..b0a825d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java index d2299ae..a85efd8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java @@ -29,14 +29,25 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; -import com.rometools.rome.feed.synd.*; +import com.rometools.rome.feed.synd.SyndContent; +import com.rometools.rome.feed.synd.SyndContentImpl; +import com.rometools.rome.feed.synd.SyndEntry; +import com.rometools.rome.feed.synd.SyndEntryImpl; +import com.rometools.rome.feed.synd.SyndFeed; +import com.rometools.rome.feed.synd.SyndFeedImpl; import com.rometools.rome.io.FeedException; import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.SyndFeedOutput; -import java.io.*; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; @@ -125,7 +136,8 @@ final class EntriesMgr { final String today; - try (final InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { + try (final InputStreamReader reader = new InputStreamReader( + new FileInputStream(file), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); today = Utils.isoLocalDate(feed.getPublishedDate()); @@ -263,13 +275,14 @@ final class EntriesMgr { history.remove(0); } } - + try (final Writer fw = new OutputStreamWriter( new FileOutputStream(bot.getLogsDir() + NAV_XML), StandardCharsets.UTF_8)) { rss = new SyndFeedImpl(); rss.setFeedType("rss_2.0"); rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); - rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " + bot.getChannel()); + rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " + + bot.getChannel()); rss.setLink(bot.getBacklogsUrl()); rss.setPublishedDate(Calendar.getInstance().getTime()); diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java index d665179..0088f9f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryComment.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import java.io.Serializable; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java index 8820227..9a4405d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/EntryLink.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import com.rometools.rome.feed.synd.SyndCategory; @@ -328,6 +329,15 @@ public class EntryLink implements Serializable { } } + /** + * Sets the tags. + * + * @param tags The tags. + */ + private void setTags(final List<SyndCategory> tags) { + this.tags.addAll(tags); + } + /** * Returns the comment's title. * @@ -377,15 +387,6 @@ public class EntryLink implements Serializable { } } - /** - * Sets the tags. - * - * @param tags The tags. - */ - private void setTags(final List<SyndCategory> tags) { - this.tags.addAll(tags); - } - /** * Returns a string representation of the object. * diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index e85f2c2..3ea1fcb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import com.rometools.rome.feed.synd.SyndEntry; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c0e9dc3..eeafd9d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -29,14 +29,33 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import com.rometools.rome.io.FeedException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.modules.*; +import net.thauvin.erik.mobibot.modules.AbstractModule; +import net.thauvin.erik.mobibot.modules.Calc; +import net.thauvin.erik.mobibot.modules.CurrencyConverter; +import net.thauvin.erik.mobibot.modules.Dice; +import net.thauvin.erik.mobibot.modules.GoogleSearch; +import net.thauvin.erik.mobibot.modules.Joke; +import net.thauvin.erik.mobibot.modules.Lookup; +import net.thauvin.erik.mobibot.modules.Ping; +import net.thauvin.erik.mobibot.modules.StockQuote; +import net.thauvin.erik.mobibot.modules.Twitter; +import net.thauvin.erik.mobibot.modules.War; +import net.thauvin.erik.mobibot.modules.Weather2; +import net.thauvin.erik.mobibot.modules.WorldTime; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.semver.Version; -import org.apache.commons.cli.*; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -47,10 +66,20 @@ import org.jibble.pircbot.User; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; import java.time.Clock; import java.time.LocalDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.StringTokenizer; /** * Implements the #mobitopia bot. @@ -162,7 +191,7 @@ public class Mobibot extends PircBot { // The default tags/categories. private String defaultTags = ""; // The feed URL. - private String feedURL = ""; + private String feedUrl = ""; // The ident message. private String identMsg = ""; // The ident nick. @@ -237,7 +266,7 @@ public class Mobibot extends PircBot { // Set the URLs setWeblogUrl(getVersion()); - setFeedURL(p.getProperty("feed", "")); + setFeedUrl(p.getProperty("feed", "")); setBacklogsUrl(Utils.ensureDir(p.getProperty("backlogs", weblogUrl), true)); // Set the pinboard authentication @@ -443,8 +472,8 @@ public class Mobibot extends PircBot { * @param sender The nick of the person who sent the private message. */ private void feedResponse(final String sender) { - if (Utils.isValidString(feedURL)) { - new Thread(new FeedReader(this, sender, feedURL)).start(); + if (Utils.isValidString(feedUrl)) { + new Thread(new FeedReader(this, sender, feedUrl)).start(); } else { send(sender, "There is no weblog setup for this channel."); } @@ -1009,9 +1038,7 @@ public class Mobibot extends PircBot { send(sender, Utils.bold("Duplicate") + " >> " + Utils.buildLink(dupIndex, entry)); } } - } - // mobibot: <command> - else if (message.matches(getNickPattern() + ":.*")) { + } else if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> isCommand = true; final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); @@ -1023,45 +1050,27 @@ public class Mobibot extends PircBot { args = cmds[1].trim(); } - // mobibot: help - if (cmd.startsWith(Commands.HELP_CMD)) { + + if (cmd.startsWith(Commands.HELP_CMD)) { // mobibot: help helpResponse(sender, args); - } - // mobibot: recap - else if (cmd.equals(Commands.RECAP_CMD)) { + } else if (cmd.equals(Commands.RECAP_CMD)) { // mobibot: recap recapResponse(sender, false); - } - // mobibot: users - else if (cmd.equals(Commands.USERS_CMD)) { + } else if (cmd.equals(Commands.USERS_CMD)) { // mobibot: users usersResponse(sender, false); - } - // mobibot: info - else if (cmd.equals(Commands.INFO_CMD)) { + } else if (cmd.equals(Commands.INFO_CMD)) { // mobibot: info infoResponse(sender, false); - } - // mobbiot: version - else if (cmd.equals(Commands.VERSION_CMD)) { + } else if (cmd.equals(Commands.VERSION_CMD)) { // mobbiot: version versionResponse(sender, false); - } - // mobibot: <channel> - else if (cmd.equalsIgnoreCase(channel.substring(1))) { + } else if (cmd.equalsIgnoreCase(channel.substring(1))) { // mobibot: <channel> feedResponse(sender); - } - // mobibot: view - else if (cmd.startsWith(Commands.VIEW_CMD)) { + } else if (cmd.startsWith(Commands.VIEW_CMD)) { // mobibot: view viewResponse(sender, args, false); - } - // mobibot: tell - else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) { + } else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) { // mobibot: tell tell.response(sender, args); - } - // mobibot: ignore - else if (cmd.startsWith(Commands.IGNORE_CMD)) { + } else if (cmd.startsWith(Commands.IGNORE_CMD)) { // mobibot: ignore ignoreResponse(sender, args); - } - // modules - else { - for (final AbstractModule module : MODULES) { + } else { + for (final AbstractModule module : MODULES) { // modules for (final String c : module.getCommands()) { if (cmd.startsWith(c)) { module.commandResponse(this, sender, args, false); @@ -1069,9 +1078,7 @@ public class Mobibot extends PircBot { } } } - } - // L1:<comment>, L1:-, L1:|<title>, etc. - else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { + } else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { // L1:<comment>, L1:-, L1:|<title>, etc. isCommand = true; final String[] cmds = message.substring(1).split(":", 2); @@ -1112,9 +1119,7 @@ public class Mobibot extends PircBot { } else { send(sender, "Please ask a channel op to remove this entry for you."); } - } - // L1:|<title> - else if (cmd.charAt(0) == '|') { + } else if (cmd.charAt(0) == '|') { // L1:|<title> if (cmd.length() > 1) { final EntryLink entry = entries.get(index); entry.setTitle(cmd.substring(1).trim()); @@ -1126,9 +1131,7 @@ public class Mobibot extends PircBot { send(channel, Utils.buildLink(index, entry)); saveEntries(false); } - } - // L1:=<url> - else if (cmd.charAt(0) == '=') { + } else if (cmd.charAt(0) == '=') { // L1:=<url> final EntryLink entry = entries.get(index); if (entry.getLogin().equals(login) || isOp(sender)) { @@ -1149,9 +1152,7 @@ public class Mobibot extends PircBot { } else { send(sender, "Please ask a channel op to change this link for you."); } - } - // L1:?<author> - else if (cmd.charAt(0) == '?') { + } else if (cmd.charAt(0) == '?') { // L1:?<author> if (isOp(sender)) { if (cmd.length() > 1) { final EntryLink entry = entries.get(index); @@ -1172,9 +1173,7 @@ public class Mobibot extends PircBot { } } } - } - // L1T:<+-tag> - else if (message.matches(Commands.LINK_CMD + "[0-9]+T:.*")) { + } else if (message.matches(Commands.LINK_CMD + "[0-9]+T:.*")) { // L1T:<+-tag> isCommand = true; final String[] cmds = message.substring(1).split("T:", 2); @@ -1206,9 +1205,7 @@ public class Mobibot extends PircBot { } } } - } - // L1.1:<command> - else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { + } else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { // L1.1:<command> isCommand = true; final String[] cmds = message.substring(1).split("[.:]", 3); @@ -1225,15 +1222,11 @@ public class Mobibot extends PircBot { if (cmd.length() == 0) { final EntryComment comment = entry.getComment(cindex); send(channel, Utils.buildComment(index, cindex, comment)); - } - // L1.1:- - else if ("-".equals(cmd)) { + } else if ("-".equals(cmd)) { // L1.1:- entry.deleteComment(cindex); send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); saveEntries(false); - } - // L1.1:?<author> - else if (cmd.charAt(0) == '?') { + } else if (cmd.charAt(0) == '?') { // L1.1:?<author> if (isOp(sender)) { if (cmd.length() > 1) { final EntryComment comment = entry.getComment(cindex); @@ -1490,10 +1483,10 @@ public class Mobibot extends PircBot { /** * Sets the feed URL. * - * @param feedURL The feed URL. + * @param feedUrl The feed URL. */ - private void setFeedURL(final String feedURL) { - this.feedURL = feedURL; + private void setFeedUrl(final String feedUrl) { + this.feedUrl = feedUrl; } /** @@ -1652,9 +1645,9 @@ public class Mobibot extends PircBot { entry = entries.get(i); if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase().contains(lcArgs)) || - (entry.getTitle().toLowerCase().contains(lcArgs)) || - (entry.getNick().toLowerCase().contains(lcArgs))) { + if ((entry.getLink().toLowerCase().contains(lcArgs)) + || (entry.getTitle().toLowerCase().contains(lcArgs)) + || (entry.getNick().toLowerCase().contains(lcArgs))) { if (sent > MAX_ENTRIES) { send(sender, "To view more, try: " + Utils diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 5682456..d7b5274 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -29,11 +29,12 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import net.thauvin.erik.pinboard.PinboardPoster; -import javax.swing.*; +import javax.swing.SwingWorker; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/Tell.java index de149c8..35d0a8c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/Tell.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import java.util.List; @@ -81,6 +82,13 @@ public class Tell { // The serialized object file. private final String serializedObject; + /** + * Creates a new instance. + * + * @param bot The bot. + * @param maxDays Max days. + * @param maxSize Max size. + */ public Tell(final Mobibot bot, final String maxDays, final String maxSize) { this.bot = bot; this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS); @@ -264,7 +272,9 @@ public class Tell { } } - if (clean()) save(); + if (clean()) { + save(); + } } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java index bb4c607..2b3c193 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessage.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import java.io.Serializable; diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java index 8d6cdde..47c104b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java @@ -29,11 +29,21 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import org.apache.logging.log4j.Logger; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; import java.time.Clock; import java.time.LocalDateTime; import java.util.ArrayList; @@ -52,12 +62,12 @@ final class TellMessagesMgr { * * @throws UnsupportedOperationException If the constructor is called. */ - private TellMessagesMgr() { + private TellMessagesMgr() { throw new UnsupportedOperationException("Illegal constructor call."); } /** - * Cleans the messages queue + * Cleans the messages queue. * * @param tellMessages The messages list. * @param tellMaxDays The maximum number of days to keep messages for. @@ -117,7 +127,8 @@ final class TellMessagesMgr { public static void save(final String file, final List<TellMessage> messages, final Logger logger) { try { - try (final ObjectOutput output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { + try (final ObjectOutput output = new ObjectOutputStream( + new BufferedOutputStream(new FileOutputStream(file)))) { if (logger.isDebugEnabled()) { logger.debug("Saving the messages."); } diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 6745d30..e84758c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -17,7 +17,7 @@ import java.io.InputStreamReader; * Then execute: * <p> * <code> - * java -cp "mobibot.jar:lib/*"net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> + * java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> * </code> * </p> * and follow the prompts/instructions. @@ -28,6 +28,11 @@ import java.io.InputStreamReader; * @since 1.0 */ public final class TwitterOAuth { + /** + * Twitter OAuth Client Registration. + * + * @param args The consumerKey and consumerSecret should be passed as arguments. + */ @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING") public static void main(final String[] args) throws Exception { diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 648ad7d..e754c69 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import org.jibble.pircbot.Colors; @@ -170,7 +171,7 @@ public final class Utils { } /** - * Meks the given string cyan + * Meks the given string cyan. * * @param s The string. * @return The cyan string. @@ -323,7 +324,7 @@ public final class Utils { } /** - * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code> + * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>. * * @param date The date. * @return The fromatted date. @@ -333,7 +334,7 @@ public final class Utils { } /** - * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code> + * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>. * * @param date The date. * @return The formatted date. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index c58830a..af9fbe3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -29,12 +29,17 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * The <code>Module</code> abstract class. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 22810fa..12880df 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.objecthunter.exp4j.Expression; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index d45fa32..a35d588 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -149,7 +150,9 @@ public final class CurrencyConverter extends ThreadedModule { int i = 0; for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { - if (i > 0) buff.append(", "); + if (i > 0) { + buff.append(", "); + } buff.append(rate.getKey()).append(": ").append(rate.getValue()); i++; } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 8c8085b..8c45f48 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 9a4af37..4894afa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index cbd8084..2e6a840 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; @@ -112,7 +113,7 @@ public final class Joke extends ThreadedModule { } /** - * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> + * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. */ void run(final Mobibot bot, final String sender, String arg) { try { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 014b9ad..ace6bca 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; @@ -55,7 +56,7 @@ public final class Lookup extends AbstractModule { private static final String LOOKUP_CMD = "lookup"; /** - * The default constructor + * The default constructor. */ public Lookup() { commands.add(LOOKUP_CMD); @@ -154,8 +155,8 @@ public final class Lookup extends AbstractModule { bot.send(Lookup.lookup(args)); } catch (UnknownHostException ignore) { if (args.matches( - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) { + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) { try { final String[] lines = Lookup.whois(args); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 9c3009c..35710b9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import okhttp3.HttpUrl; @@ -100,11 +101,13 @@ class ModuleException extends Exception { final String causeMessage = getCause().getMessage(); final Matcher matcher = urlPattern.matcher(causeMessage); if (matcher.find()) { - final HttpUrl url = HttpUrl.parse(matcher.group(1)+matcher.group(2)); - if (url != null){ + final HttpUrl url = HttpUrl.parse(matcher.group(1) + matcher.group(2)); + if (url != null) { final StringBuilder query = new StringBuilder("?"); for (int i = 0, size = url.querySize(); i < size; i++) { - if (i > 0) query.append('&'); + if (i > 0) { + query.append('&'); + } query.append(url.queryParameterName(i)).append('=').append('[') .append(url.queryParameterValue(i).length()).append(']'); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index a8a075d..bb554f5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 3805592..aae368d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java index 59e99ce..99917f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 14d508a..ef067a6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 3f6349b..b6c3442 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 6fffb11..2f51d3a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.aksingh.owmjapis.api.APIException; @@ -72,7 +73,8 @@ public class Weather2 extends ThreadedModule { properties.put(OWM_API_KEY_PROP, ""); } - private static String fAndC(final Double d) { + @SuppressWarnings("AvoidEscapedUnicodeCharacters") + private static String getTemps(final Double d) { final double c = (d - 32) * 5 / 9; return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; } @@ -122,7 +124,7 @@ public class Weather2 extends ThreadedModule { final Main main = cwd.getMainData(); if (main != null) { if (main.hasTemp()) { - messages.add(new PublicMessage("Temperature: " + fAndC(main.getTemp()))); + messages.add(new PublicMessage("Temperature: " + getTemps(main.getTemp()))); } if (main.hasHumidity() && (main.getHumidity() != null)) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 01842c5..d0b5c0b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; @@ -55,7 +56,7 @@ public final class WorldTime extends AbstractModule { private static final String BEATS_KEYWORD = ".beats"; // The supported countries. private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); - + /** * The time command. */ @@ -138,10 +139,10 @@ public final class WorldTime extends AbstractModule { COUNTRIES_MAP.put("ZULU", "Zulu"); COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); - - ZoneId.getAvailableZoneIds().stream().filter( - tz -> !tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)).forEach( - tz -> COUNTRIES_MAP.put(tz, tz)); + + ZoneId.getAvailableZoneIds().stream().filter(tz -> + !tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)).forEach(tz -> + COUNTRIES_MAP.put(tz, tz)); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java index 9bd0ea7..a558dc0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.msg; /** @@ -39,11 +40,23 @@ package net.thauvin.erik.mobibot.msg; * @since 1.0 */ public class ErrorMessage extends Message { + /** + * Creates a new error message. + * + * @param message The error message. + */ public ErrorMessage(final String message) { this.setMessage(message); this.setError(true); this.setNotice(true); } + + /** + * Creates a new error message. + * + * @param message The message. + * @param color The message color. + */ public ErrorMessage(final String message, final String color) { this.setMessage(message); this.setError(true); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index b86de8a..47c3f1e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.msg; import org.jibble.pircbot.Colors; @@ -101,7 +102,7 @@ public class Message { } /** - * Set the color + * Set the color. * * @param color The new color. */ @@ -175,9 +176,9 @@ public class Message { /** * Sets the message private flag. * - * @param aPrivate The private flag. + * @param isPrivate The private flag. */ - public void setPrivate(boolean aPrivate) { - isPrivate = aPrivate; + public void setPrivate(boolean isPrivate) { + this.isPrivate = isPrivate; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java index 80bfe9e..f4eb3db 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.msg; /** @@ -39,11 +40,22 @@ package net.thauvin.erik.mobibot.msg; * @since 1.0 */ public class NoticeMessage extends Message { + /** + * Creates a new notice. + * + * @param message The notice's message. + */ public NoticeMessage(final String message) { this.setMessage(message); this.setNotice(true); } + /** + * Create a new notice. + * + * @param message The ntoice's message. + * @param color The color. + */ public NoticeMessage(final String message, final String color) { this.setMessage(message); this.setNotice(true); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java index b8a3541..18bf7a2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.msg; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java index d7556b1..d8ab1b2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.msg; /** diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index ba469d7..43193b5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot; import org.jibble.pircbot.Colors; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java index 94e3fca..eb678c5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java index b70450b..efb3f81 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.Test; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index a134ca4..34425c9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.Test; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index d222eec..92d63ae 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.msg.Message; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java index edc821d..d62cd2e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.Test; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java index 3510285..2f96c16 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.BeforeSuite; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index eefe3e9..95288a5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.Test; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java index aa639e3..c058317 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.DataProvider; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java index 81c4427..8dc6c8e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java @@ -29,11 +29,13 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.Test; import static org.assertj.core.api.Assertions.assertThat; + /** * The <code>PingTest</code> class. * @@ -49,6 +51,6 @@ public class PingTest { @Test public void testPingsArray() { - assertThat(Ping.PINGS).as("Pings array is not empty.").isNotEmpty(); + assertThat(Ping.PINGS).as("Pings array is not empty.").isNotEmpty(); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 7fbc87a..fc2ccec 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.msg.Message; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index bcd4a66..97e395f 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.msg.Message; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java index 97d420a..de5cd1f 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java @@ -29,6 +29,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot.modules; import org.testng.annotations.Test; From 90af2d9f6e03fa7a24fdae5d772ad45344a3e044 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 17 Apr 2019 00:23:25 -0700 Subject: [PATCH 243/842] Fixed missing semicolon. --- version.mustache | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/version.mustache b/version.mustache index c984841..860bfe4 100644 --- a/version.mustache +++ b/version.mustache @@ -20,15 +20,7 @@ public final class {{className}} { public final static int PATCH = {{patch}}; public final static String PRERELEASE = "{{preRelease}}"; public final static String BUILDMETA = "{{buildMeta}}"; - - /** - * The full semantic version string. - */ - public final static String VERSION = Integer.toString(MAJOR) + '.' - + Integer.toString(MINOR) + '.' - + Integer.toString(PATCH) - + ((!PRERELEASE.isEmpty()) ? "-" + PRERELEASE : "") - + ((!BUILDMETA.isEmpty()) ? "+" + BUILDMETA : ""); + public final static String VERSION = "{{version}}"; /** * Disables the default constructor. From 7cede1d560a3beea9ce4efec20ddea3c2d7f4c66 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 17 Apr 2019 00:23:46 -0700 Subject: [PATCH 244/842] Dependencies update. --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 77a712c..b678563 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ dependencies { compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.14.0' + compile 'com.squareup.okhttp3:okhttp:3.14.1' compile 'com.rometools:rome:1.12.0' @@ -56,7 +56,7 @@ dependencies { compile 'org.twitter4j:twitter4j-core:4.0.7' compile 'net.thauvin.erik:pinboard-poster:1.0.0' - compile 'net.aksingh:owm-japis:2.5.2.3' + compile 'net.aksingh:owm-japis:2.5.3.0' testImplementation 'org.testng:testng:6.14.3' testImplementation 'org.assertj:assertj-core:3.12.2' From 474a31bdee583600051d82aadb7d6a958cfcdc2d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Apr 2019 20:24:17 -0700 Subject: [PATCH 245/842] Created tell and entries packages and Constants class. --- config/spotbugs/excludeFilter.xml | 6 +- .../net/thauvin/erik/mobibot/Constants.java | 59 +++++++++ .../net/thauvin/erik/mobibot/Mobibot.java | 99 +++++++++------ .../net/thauvin/erik/mobibot/Pinboard.java | 1 + .../java/net/thauvin/erik/mobibot/Utils.java | 68 +--------- .../mobibot/{ => entries}/EntriesMgr.java | 6 +- .../erik/mobibot/entries/EntriesUtils.java | 118 ++++++++++++++++++ .../mobibot/{ => entries}/EntryComment.java | 2 +- .../erik/mobibot/{ => entries}/EntryLink.java | 2 +- .../thauvin/erik/mobibot/{ => tell}/Tell.java | 6 +- .../erik/mobibot/{ => tell}/TellMessage.java | 2 +- .../mobibot/{ => tell}/TellMessagesMgr.java | 6 +- 12 files changed, 259 insertions(+), 116 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/Constants.java rename src/main/java/net/thauvin/erik/mobibot/{ => entries}/EntriesMgr.java (98%) create mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java rename src/main/java/net/thauvin/erik/mobibot/{ => entries}/EntryComment.java (98%) rename src/main/java/net/thauvin/erik/mobibot/{ => entries}/EntryLink.java (99%) rename src/main/java/net/thauvin/erik/mobibot/{ => tell}/Tell.java (98%) rename src/main/java/net/thauvin/erik/mobibot/{ => tell}/TellMessage.java (99%) rename src/main/java/net/thauvin/erik/mobibot/{ => tell}/TellMessagesMgr.java (96%) diff --git a/config/spotbugs/excludeFilter.xml b/config/spotbugs/excludeFilter.xml index eb80a26..4590e4c 100644 --- a/config/spotbugs/excludeFilter.xml +++ b/config/spotbugs/excludeFilter.xml @@ -4,7 +4,11 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd"> <Match> - <Package name="net.thauvin.erik.mobibot.*"/> + <Or> + <Package name="net.thauvin.erik.mobibot.*"/> + <Package name="net.thauvin.erik.mobibot.tell.*"/> + <Package name="net.thauvin.erik.mobibot.entries.*"/> + </Or> <Or> <Bug pattern="PATH_TRAVERSAL_IN"/> <Bug pattern="PATH_TRAVERSAL_OUT"/> diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java new file mode 100644 index 0000000..9dc0bf4 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -0,0 +1,59 @@ +/* + * Constants.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot; + +/** + * The <code>Constants</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-19 + * @since 1.0 + */ +public final class Constants { + /** + * The empty title string. + */ + public static final String NO_TITLE = "No Title"; + + /** + * The Twitter handle property key. + */ + public static final String TWITTER_HANDLE_PROP = "twitter-handle"; + + /** + * Disables the default constructor. + */ + private Constants() { + throw new UnsupportedOperationException("Illegal constructor call."); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index eeafd9d..4f34376 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -34,6 +34,10 @@ package net.thauvin.erik.mobibot; import com.rometools.rome.io.FeedException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.entries.EntriesMgr; +import net.thauvin.erik.mobibot.entries.EntriesUtils; +import net.thauvin.erik.mobibot.entries.EntryComment; +import net.thauvin.erik.mobibot.entries.EntryLink; import net.thauvin.erik.mobibot.modules.AbstractModule; import net.thauvin.erik.mobibot.modules.Calc; import net.thauvin.erik.mobibot.modules.CurrencyConverter; @@ -41,6 +45,7 @@ import net.thauvin.erik.mobibot.modules.Dice; import net.thauvin.erik.mobibot.modules.GoogleSearch; import net.thauvin.erik.mobibot.modules.Joke; import net.thauvin.erik.mobibot.modules.Lookup; +import net.thauvin.erik.mobibot.modules.ModuleException; import net.thauvin.erik.mobibot.modules.Ping; import net.thauvin.erik.mobibot.modules.StockQuote; import net.thauvin.erik.mobibot.modules.Twitter; @@ -48,6 +53,7 @@ import net.thauvin.erik.mobibot.modules.War; import net.thauvin.erik.mobibot.modules.Weather2; import net.thauvin.erik.mobibot.modules.WorldTime; import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.tell.Tell; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -96,9 +102,6 @@ public class Mobibot extends PircBot { */ public static final int CONNECT_TIMEOUT = 5000; - // The empty title string. - static final String NO_TITLE = "No Title"; - // The default port. private static final int DEFAULT_PORT = 6667; @@ -186,6 +189,10 @@ public class Mobibot extends PircBot { private final List<String> recap = new ArrayList<>(0); // The tell object. private final Tell tell; + // The Twitter handle for channel join notifications. + private final String twitterHandle; + // The Twitter module. + private final Twitter twitterModule; // The backlogs URL. private String backLogsUrl = ""; // The default tags/categories. @@ -202,7 +209,6 @@ public class Mobibot extends PircBot { private Pinboard pinboard = null; // Today's date. private String today = Utils.today(); - // The weblog URL. private String weblogUrl = ""; @@ -943,14 +949,6 @@ public class Mobibot extends PircBot { connect(); } - /** - * {@inheritDoc} - */ - @Override - protected void onJoin(final String channel, final String sender, final String login, final String hostname) { - tell.send(sender); - } - /** * {@inheritDoc} */ @@ -988,7 +986,7 @@ public class Mobibot extends PircBot { } final StringBuilder tags = new StringBuilder(defaultTags); - String title = NO_TITLE; + String title = Constants.NO_TITLE; if (cmds.length == 2) { final String[] data = cmds[1].trim().split(TAGS_MARKER, 2); @@ -1004,7 +1002,7 @@ public class Mobibot extends PircBot { } } - if (NO_TITLE.equals(title)) { + if (Constants.NO_TITLE.equals(title)) { try { final Document html = Jsoup.connect(link).userAgent("Mozilla").get(); final String htmlTitle = html.title(); @@ -1021,7 +1019,7 @@ public class Mobibot extends PircBot { final int index = entries.size() - 1; final EntryLink entry = entries.get(index); - send(channel, Utils.buildLink(index, entry)); + send(channel, EntriesUtils.buildLink(index, entry)); if (pinboard != null) { pinboard.addPost(entry); @@ -1029,13 +1027,14 @@ public class Mobibot extends PircBot { saveEntries(isBackup); - if (NO_TITLE.equals(entry.getTitle())) { + if (Constants.NO_TITLE.equals(entry.getTitle())) { send(sender, "Please specify a title, by typing:", true); send(sender, helpIndent(Commands.LINK_CMD + (index + 1) + ":|This is the title"), true); } } else { final EntryLink entry = entries.get(dupIndex); - send(sender, Utils.bold("Duplicate") + " >> " + Utils.buildLink(dupIndex, entry)); + send(sender, Utils.bold("Duplicate") + " >> " + + EntriesUtils.buildLink(dupIndex, entry)); } } } else if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> @@ -1090,17 +1089,17 @@ public class Mobibot extends PircBot { if (cmd.length() == 0) { final EntryLink entry = entries.get(index); - send(channel, Utils.buildLink(index, entry)); + send(channel, EntriesUtils.buildLink(index, entry)); if (entry.hasTags()) { - send(channel, Utils.buildTags(index, entry)); + send(channel, EntriesUtils.buildTags(index, entry)); } if (entry.hasComments()) { final EntryComment[] comments = entry.getComments(); for (int i = 0; i < comments.length; i++) { - send(channel, Utils.buildComment(index, i, comments[i])); + send(channel, EntriesUtils.buildComment(index, i, comments[i])); } } } else { @@ -1128,7 +1127,7 @@ public class Mobibot extends PircBot { pinboard.updatePost(entry.getLink(), entry); } - send(channel, Utils.buildLink(index, entry)); + send(channel, EntriesUtils.buildLink(index, entry)); saveEntries(false); } } else if (cmd.charAt(0) == '=') { // L1:=<url> @@ -1146,7 +1145,7 @@ public class Mobibot extends PircBot { pinboard.updatePost(oldLink, entry); } - send(channel, Utils.buildLink(index, entry)); + send(channel, EntriesUtils.buildLink(index, entry)); saveEntries(false); } } else { @@ -1157,7 +1156,7 @@ public class Mobibot extends PircBot { if (cmd.length() > 1) { final EntryLink entry = entries.get(index); entry.setNick(cmd.substring(1)); - send(channel, Utils.buildLink(index, entry)); + send(channel, EntriesUtils.buildLink(index, entry)); saveEntries(false); } } else { @@ -1168,7 +1167,7 @@ public class Mobibot extends PircBot { final int cindex = entry.addComment(cmd, sender); final EntryComment comment = entry.getComment(cindex); - send(sender, Utils.buildComment(index, cindex, comment)); + send(sender, EntriesUtils.buildComment(index, cindex, comment)); saveEntries(false); } } @@ -1192,14 +1191,14 @@ public class Mobibot extends PircBot { pinboard.updatePost(entry.getLink(), entry); } - send(channel, Utils.buildTags(index, entry)); + send(channel, EntriesUtils.buildTags(index, entry)); saveEntries(false); } else { send(sender, "Please ask a channel op to change the tags for you."); } } else { if (entry.hasTags()) { - send(channel, Utils.buildTags(index, entry)); + send(channel, EntriesUtils.buildTags(index, entry)); } else { send(sender, "The entry has no tags. Why don't add some?"); } @@ -1221,7 +1220,7 @@ public class Mobibot extends PircBot { // L1.1: if (cmd.length() == 0) { final EntryComment comment = entry.getComment(cindex); - send(channel, Utils.buildComment(index, cindex, comment)); + send(channel, EntriesUtils.buildComment(index, cindex, comment)); } else if ("-".equals(cmd)) { // L1.1:- entry.deleteComment(cindex); send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); @@ -1231,7 +1230,7 @@ public class Mobibot extends PircBot { if (cmd.length() > 1) { final EntryComment comment = entry.getComment(cindex); comment.setNick(cmd.substring(1)); - send(channel, Utils.buildComment(index, cindex, comment)); + send(channel, EntriesUtils.buildComment(index, cindex, comment)); saveEntries(false); } } else { @@ -1241,7 +1240,7 @@ public class Mobibot extends PircBot { entry.setComment(cindex, cmd, sender); final EntryComment comment = entry.getComment(cindex); - send(sender, Utils.buildComment(index, cindex, comment)); + send(sender, EntriesUtils.buildComment(index, cindex, comment)); saveEntries(false); } } @@ -1255,14 +1254,6 @@ public class Mobibot extends PircBot { tell.send(sender, true); } - /** - * {@inheritDoc} - */ - @Override - protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { - tell.send(newNick); - } - /** * {@inheritDoc} */ @@ -1373,6 +1364,36 @@ public class Mobibot extends PircBot { } } + /** + * {@inheritDoc} + */ + @Override + protected final void onAction(final String sender, + final String login, + final String hostname, + final String target, + final String action) { + if (target.equals(ircChannel)) { + storeRecap(sender, action, true); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onJoin(final String channel, final String sender, final String login, final String hostname) { + tell.send(sender); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { + tell.send(newNick); + } + /** * Responds with the last 10 public messages. * @@ -1657,7 +1678,7 @@ public class Mobibot extends PircBot { break; } - send(sender, Utils.buildLink(i, entry, true), isPrivate); + send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate); sent++; } } else { @@ -1670,7 +1691,7 @@ public class Mobibot extends PircBot { break; } - send(sender, Utils.buildLink(i, entry, true), isPrivate); + send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate); sent++; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index d7b5274..175fdc4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot; +import net.thauvin.erik.mobibot.entries.EntryLink; import net.thauvin.erik.pinboard.PinboardPoster; import javax.swing.SwingWorker; diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index e754c69..d9ca5be 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -74,73 +74,7 @@ public final class Utils { * @return The bold string. */ public static String bold(final String s) { - return colorize(s, Colors.BOLD); - } - - /** - * Builds an entry's comment for display on the channel. - * - * @param entryIndex The entry's index. - * @param commentIndex The comment's index. - * @param comment The {@link net.thauvin.erik.mobibot.EntryComment comment} object. - * @return The entry's comment. - */ - static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) { - return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " - + comment.getComment()); - } - - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. - * @return The entry's link. - * @see #buildLink(int, net.thauvin.erik.mobibot.EntryLink, boolean) - */ - static String buildLink(final int index, final EntryLink entry) { - return buildLink(index, entry, false); - } - - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. - * @param isView Set to true to display the number of comments. - * @return The entry's link. - */ - static String buildLink(final int index, final EntryLink entry, final boolean isView) { - final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); - - buff.append('[').append(entry.getNick()).append(']'); - - if (isView && entry.hasComments()) { - buff.append("[+").append(entry.getCommentsCount()).append(']'); - } - - buff.append(' '); - - if (Mobibot.NO_TITLE.equals(entry.getTitle())) { - buff.append(entry.getTitle()); - } else { - buff.append(bold(entry.getTitle())); - } - - buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )"); - - return buff.toString(); - } - - /** - * Build an entry's tags/categories for display on the channel. - * - * @param entryIndex The entry's index. - * @param entry The {@link net.thauvin.erik.mobibot.EntryLink entry} object. - * @return The entry's tags. - */ - static String buildTags(final int entryIndex, final EntryLink entry) { - return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replaceAll(",", ", ")); + return colorize(s, Colors.BOLD); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java similarity index 98% rename from src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java rename to src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index a85efd8..1e485b9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.entries; import com.rometools.rome.feed.synd.SyndContent; import com.rometools.rome.feed.synd.SyndContentImpl; @@ -41,6 +41,8 @@ import com.rometools.rome.feed.synd.SyndFeedImpl; import com.rometools.rome.io.FeedException; import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.SyndFeedOutput; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -60,7 +62,7 @@ import java.util.List; * @created 2014-04-28 * @since 1.0 */ -final class EntriesMgr { +public final class EntriesMgr { /** * The name of the file containing the current entries. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java new file mode 100644 index 0000000..c22bae2 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -0,0 +1,118 @@ +/* + * EntriesUtils.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.entries; + +import net.thauvin.erik.mobibot.Commands; +import net.thauvin.erik.mobibot.Constants; + +/** + * The <code>Utils</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-19 + * @since 1.0 + */ +public final class EntriesUtils { + /** + * Disables the default constructor. + */ + private EntriesUtils() { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Builds an entry's comment for display on the channel. + * + * @param entryIndex The entry's index. + * @param commentIndex The comment's index. + * @param comment The {@link EntryComment comment} object. + * @return The entry's comment. + */ + public static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) { + return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " + + comment.getComment()); + } + + /** + * Builds an entry's link for display on the channel. + * + * @param index The entry's index. + * @param entry The {@link EntryLink entry} object. + * @return The entry's link. + * @see #buildLink(int, EntryLink, boolean) + */ + public static String buildLink(final int index, final EntryLink entry) { + return buildLink(index, entry, false); + } + + /** + * Builds an entry's link for display on the channel. + * + * @param index The entry's index. + * @param entry The {@link EntryLink entry} object. + * @param isView Set to true to display the number of comments. + * @return The entry's link. + */ + public static String buildLink(final int index, final EntryLink entry, final boolean isView) { + final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); + + buff.append('[').append(entry.getNick()).append(']'); + + if (isView && entry.hasComments()) { + buff.append("[+").append(entry.getCommentsCount()).append(']'); + } + + buff.append(' '); + + if (Constants.NO_TITLE.equals(entry.getTitle())) { + buff.append(entry.getTitle()); + } else { + buff.append(net.thauvin.erik.mobibot.Utils.bold(entry.getTitle())); + } + + buff.append(" ( ").append(net.thauvin.erik.mobibot.Utils.green(entry.getLink())).append(" )"); + + return buff.toString(); + } + + /** + * Build an entry's tags/categories for display on the channel. + * + * @param entryIndex The entry's index. + * @param entry The {@link EntryLink entry} object. + * @return The entry's tags. + */ + public static String buildTags(final int entryIndex, final EntryLink entry) { + return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replaceAll(",", ", ")); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java similarity index 98% rename from src/main/java/net/thauvin/erik/mobibot/EntryComment.java rename to src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java index 0088f9f..825ed3c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.entries; import java.io.Serializable; import java.time.LocalDateTime; diff --git a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java similarity index 99% rename from src/main/java/net/thauvin/erik/mobibot/EntryLink.java rename to src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 9a4405d..021060f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.entries; import com.rometools.rome.feed.synd.SyndCategory; import com.rometools.rome.feed.synd.SyndCategoryImpl; diff --git a/src/main/java/net/thauvin/erik/mobibot/Tell.java b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java similarity index 98% rename from src/main/java/net/thauvin/erik/mobibot/Tell.java rename to src/main/java/net/thauvin/erik/mobibot/tell/Tell.java index 35d0a8c..57542df 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java @@ -30,7 +30,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.tell; + +import net.thauvin.erik.mobibot.Commands; +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java similarity index 99% rename from src/main/java/net/thauvin/erik/mobibot/TellMessage.java rename to src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java index 2b3c193..657fdaf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.tell; import java.io.Serializable; import java.time.Clock; diff --git a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java similarity index 96% rename from src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java rename to src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java index 47c104b..6c9ca27 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot; +package net.thauvin.erik.mobibot.tell; import org.apache.logging.log4j.Logger; @@ -93,7 +93,7 @@ final class TellMessagesMgr { * * @param file The serialized objects file. * @param logger The logger. - * @return The {@link net.thauvin.erik.mobibot.TellMessage} array. + * @return The {@link TellMessage} array. */ @SuppressWarnings("unchecked") public static List<TellMessage> load(final String file, final Logger logger) { @@ -121,7 +121,7 @@ final class TellMessagesMgr { * Saves the messages. * * @param file The serialized objects file. - * @param messages The {@link net.thauvin.erik.mobibot.TellMessage} array. + * @param messages The {@link TellMessage} array. * @param logger The logger. */ public static void save(final String file, final List<TellMessage> messages, final Logger logger) { From 8f408d1f4646ea4cc672ebff006e58e61d77dd22 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Apr 2019 21:02:01 -0700 Subject: [PATCH 246/842] Added twitter notification on join. --- properties/mobibot.properties | 3 + .../net/thauvin/erik/mobibot/Mobibot.java | 29 +++--- .../thauvin/erik/mobibot/modules/Twitter.java | 95 +++++++++++++++---- .../erik/mobibot/modules/TwitterTest.java | 77 +++++++++++++++ 4 files changed, 170 insertions(+), 34 deletions(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 545107d..1932bd0 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -34,6 +34,9 @@ tell-max-size=50 #twitter-token= #twitter-tokenSecret= +# Twitter handle to receive channel join notifications +#twitter-handle= + # # Create custom search engine at: https://cse.google.com/ # and get API key from: https://console.developers.google.com/ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 4f34376..1beb0fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -287,7 +287,11 @@ public class Mobibot extends PircBot { MODULES.add(new Lookup()); MODULES.add(new Ping()); MODULES.add(new StockQuote()); - MODULES.add(new Twitter()); + + twitterModule = new Twitter(); + MODULES.add(twitterModule); + twitterHandle = p.getProperty(Constants.TWITTER_HANDLE_PROP, ""); + MODULES.add(new War()); MODULES.add(new Weather2()); MODULES.add(new WorldTime()); @@ -919,19 +923,18 @@ public class Mobibot extends PircBot { */ public final void joinChannel() { joinChannel(ircChannel); - } - /** - * {@inheritDoc} - */ - @Override - protected final void onAction(final String sender, - final String login, - final String hostname, - final String target, - final String action) { - if (target.equals(ircChannel)) { - storeRecap(sender, action, true); + if (twitterModule.isEnabled() && Utils.isValidString(twitterHandle)) { + new Thread(() -> { + try { + twitterModule.post( + twitterHandle, + getName() + ReleaseInfo.VERSION + " has joined " + ircChannel, + true); + } catch (ModuleException e) { + logger.warn(e.getMessage(), e); + } + }).start(); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index ef067a6..465424b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -33,6 +33,9 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.NoticeMessage; +import twitter4j.DirectMessage; import twitter4j.Status; import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; @@ -46,10 +49,10 @@ import twitter4j.conf.ConfigurationBuilder; */ public final class Twitter extends ThreadedModule { // The property keys. - private static final String CONSUMER_KEY_PROP = "twitter-consumerKey"; - private static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; - private static final String TOKEN_PROP = "twitter-token"; - private static final String TOKEN_SECRET_PROP = "twitter-tokenSecret"; + static final String CONSUMER_KEY_PROP = "twitter-consumerKey"; + static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; + static final String TOKEN_PROP = "twitter-token"; + static final String TOKEN_SECRET_PROP = "twitter-tokenSecret"; // The twitter command. private static final String TWITTER_CMD = "twitter"; @@ -64,6 +67,50 @@ public final class Twitter extends ThreadedModule { properties.put(TOKEN_SECRET_PROP, ""); } + /** + * Post on Twitter. + * + * @param consumerKey The consumer key. + * @param consumerSecret The consumer secret. + * @param token The token. + * @param tokenSecret The token secret. + * @param handle The Twitter handle (dm) or nickname. + * @param message The message to post. + * @param isDm The direct message flag. + * @return The {@link Message} to send back. + * @throws ModuleException If an error occurs while posting. + */ + static Message twitterPost(final String consumerKey, + final String consumerSecret, + final String token, + final String tokenSecret, + final String handle, + final String message, + final boolean isDm) throws ModuleException { + try { + final ConfigurationBuilder cb = new ConfigurationBuilder(); + cb.setDebugEnabled(true) + .setOAuthConsumerKey(consumerKey) + .setOAuthConsumerSecret(consumerSecret) + .setOAuthAccessToken(token) + .setOAuthAccessTokenSecret(tokenSecret); + + final TwitterFactory tf = new TwitterFactory(cb.build()); + final twitter4j.Twitter twitter = tf.getInstance(); + + if (!isDm) { + final Status status = twitter.updateStatus(message + " (" + handle + ')'); + return new NoticeMessage("You message was posted to http://twitter.com/" + twitter.getScreenName() + + "/statuses/" + status.getId()); + } else { + final DirectMessage dm = twitter.sendDirectMessage(handle, message); + return new NoticeMessage(dm.getText()); + } + } catch (Exception e) { + throw new ModuleException("twitterPost(" + message + ")", "An error has occurred: " + e.getMessage(), e); + } + } + /** * {@inheritDoc} */ @@ -77,29 +124,35 @@ public final class Twitter extends ThreadedModule { } } + /** + * Post on Twitter. + * + * @param handle The Twitter handle (dm) or nickname. + * @param message The message to post. + * @param isDm The direct message flag. + * @return The {@link Message} to send back. + * @throws ModuleException If an error occurs while posting. + */ + public Message post(final String handle, final String message, final boolean isDm) throws ModuleException { + return twitterPost(properties.get(CONSUMER_KEY_PROP), + properties.get(CONSUMER_SECRET_PROP), + properties.get(TOKEN_PROP), + properties.get(TOKEN_SECRET_PROP), + handle, + message, + isDm); + } + /** * Posts to twitter. */ @Override void run(final Mobibot bot, final String sender, final String message) { try { - final ConfigurationBuilder cb = new ConfigurationBuilder(); - cb.setDebugEnabled(true) - .setOAuthConsumerKey(properties.get(CONSUMER_KEY_PROP)) - .setOAuthConsumerSecret(properties.get(CONSUMER_SECRET_PROP)) - .setOAuthAccessToken(properties.get(TOKEN_PROP)) - .setOAuthAccessTokenSecret(properties.get(TOKEN_SECRET_PROP)); - final TwitterFactory tf = new TwitterFactory(cb.build()); - final twitter4j.Twitter twitter = tf.getInstance(); - - final Status status = twitter.updateStatus(message + " (" + sender + ')'); - - bot.send(sender, - "You message was posted to http://twitter.com/" + twitter.getScreenName() + "/statuses/" + status - .getId()); - } catch (Exception e) { - bot.getLogger().warn("Unable to post to Twitter: " + message, e); - bot.send(sender, "An error has occurred: " + e.getMessage()); + bot.send(sender, post(sender, message, false).getMessage()); + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(sender, e.getMessage()); } } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java new file mode 100644 index 0000000..3293956 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -0,0 +1,77 @@ +/* + * TwitterTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.Constants; +import org.testng.annotations.Test; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>TwitterTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-19 + * @since 1.0 + */ +public class TwitterTest { + private String getCi() { + if ("true".equals(System.getenv("CIRCLECI"))) { + return "CircleCI"; + } else if ("true".equals(System.getenv("TRAVIS"))) { + return "Travis CI"; + } else { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + return "Unknown Host"; + } + } + } + + @Test + public void testPostTwitter() throws ModuleException { + final String msg = "Testing Twitter API from " + getCi(); + assertThat(Twitter.twitterPost( + LocalProperties.getProperty(Twitter.CONSUMER_KEY_PROP), + LocalProperties.getProperty(Twitter.CONSUMER_SECRET_PROP), + LocalProperties.getProperty(Twitter.TOKEN_PROP), + LocalProperties.getProperty(Twitter.TOKEN_SECRET_PROP), + LocalProperties.getProperty(Constants.TWITTER_HANDLE_PROP), + msg, + true).getMessage()).as("twitterPost(" + msg + ')').isEqualTo(msg); + } +} From 8e8e53018107c2a37f1d5d3631f0edacfd7f5dc1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Apr 2019 21:02:54 -0700 Subject: [PATCH 247/842] Cleanup. --- .gitignore | 100 ++++++++++++------ gradle/wrapper/gradle-wrapper.properties | 2 +- mobibot.ipr | 8 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 14 +-- .../erik/mobibot/modules/ModuleException.java | 2 +- .../erik/mobibot/msg/NoticeMessage.java | 4 +- .../net/thauvin/erik/mobibot/UtilsTest.java | 6 +- .../modules/CurrencyConverterTest.java | 2 +- .../erik/mobibot/modules/LocalProperties.java | 2 +- version.properties | 4 +- 10 files changed, 84 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 51bd2bf..72be2b8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,53 +2,85 @@ !.vscode/launch.json !.vscode/settings.json !.vscode/tasks.json +!gradle-wrapper.jar +!properties/* .classpath .DS_Store .gradle .history +.idea_modules/ +.idea/**/contentModel.xml +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/dataSources/ +.idea/**/dbnavigator.xml +.idea/**/dictionaries +.idea/**/dynamic.xml +.idea/**/gradle.xml +.idea/**/libraries +.idea/**/mongoSettings.xml +.idea/**/shelf +.idea/**/sqlDataSources.xml +.idea/**/tasks.xml +.idea/**/uiDesigner.xml +.idea/**/usage.statistics.xml +.idea/**/workspace.xml +.idea/caches/build_file_checksums.ser +.idea/httpRequests +.idea/replstate.xml .kobalt +.mtj.tmp/ +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar .nb-gradle .project +.scannerwork .settings .vscode/* +*.class *.code-workspace +*.ctxt +*.ear *.iws +*.jar +*.log +*.nar +*.rar *.sublime-* -**/.idea/**/dataSources.ids -**/.idea/**/dataSources.local.xml -**/.idea/**/dataSources/ -**/.idea/**/dbnavigator.xml -**/.idea/**/dictionaries -**/.idea/**/dynamic.xml -**/.idea/**/gradle.xml -**/.idea/**/libraries -**/.idea/**/shelf -**/.idea/**/sqlDataSources.xml -**/.idea/**/tasks.xml -**/.idea/**/uiDesigner.xml -**/.idea/**/usage.statistics.xml -**/.idea/**/workspace.xml -**/*.class -/bin -/build -/deploy -/dist -/fetcher.properties -/gen -/gradle.properties -/local.properties -/log4j2.xml -/logs -/mobibot.properties -/out -/proguard-project.txt -/project.properties -/target -/test-output -/weather.log -CVS -debug.log +*.tar.gz +*.war +*.zip +atlassian-ide-plugin.xml +bin/ +build/ +cmake-build-*/ +com_crashlytics_export_strings.xml +crashlytics-build.properties +crashlytics.properties +dependency-reduced-pom.xml +deploy/ +dist/ ehthumbs.db +fabric.properties +gen/ +gradle.properties +hs_err_pid* kobaltBuild kobaltw*-test +lib/kotlin* +libs/ +local.properties +log4j2.xml +logs/ +mobibot.properties +out/ +pom.xml.next +pom.xml.releaseBackup +pom.xml.tag +pom.xml.versionsBackup +proguard-project.txt +project.properties +release.properties +target/ +test-output Thumbs.db diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ea13fdf..5f1b120 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/mobibot.ipr b/mobibot.ipr index fbc1a69..5efd501 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -3,12 +3,12 @@ <component name="CheckStyle-IDEA"> <option name="configuration"> <map> - <entry key="active-configuration" value="LOCAL_FILE:$PROJECT_DIR$/config/checkstyle/checkstyle.xml:Erik's Checks" /> + <entry key="active-configuration" value="LOCAL_FILE:K:/java/mobibot/config/checkstyle/checkstyle.xml:Erik's Checks" /> <entry key="checkstyle-version" value="8.19" /> <entry key="copy-libs" value="true" /> <entry key="location-0" value="BUNDLED:(bundled):Sun Checks" /> <entry key="location-1" value="BUNDLED:(bundled):Google Checks" /> - <entry key="location-2" value="LOCAL_FILE:$PROJECT_DIR$/config/checkstyle/checkstyle.xml:Erik's Checks" /> + <entry key="location-2" value="LOCAL_FILE:K:/java/mobibot/config/checkstyle/checkstyle.xml:Erik's Checks" /> <entry key="scan-before-checkin" value="false" /> <entry key="scanscope" value="JavaOnlyWithTests" /> <entry key="suppress-errors" value="false" /> @@ -53,13 +53,13 @@ <property name="caretWidth" class="java.lang.Integer" /> </properties> </component> - <component name="CopyrightManager"> + <component name="CopyrightManager" default="Copyright"> <copyright> <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> <option name="myName" value="Copyright" /> </copyright> <module2copyright> - <element module="Source" copyright="Copyright" /> + <element module="Copryight" copyright="Copyright" /> </module2copyright> <LanguageOptions name="JAVA"> <option name="addBlankAfter" value="false" /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 5c48762..1842aa0 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,21 +14,13 @@ import java.time.*; public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1554881826135L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1555731762870L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "261"; - - /** - * The full semantic version string. - */ - public final static String VERSION = Integer.toString(MAJOR) + '.' - + Integer.toString(MINOR) + '.' - + Integer.toString(PATCH) - + ((!PRERELEASE.isEmpty()) ? "-" + PRERELEASE : "") - + ((!BUILDMETA.isEmpty()) ? "+" + BUILDMETA : ""); + public final static String BUILDMETA = "303"; + public final static String VERSION = "0.7.3-beta+303"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 35710b9..890936a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -44,7 +44,7 @@ import java.util.regex.Pattern; * @created 2019-04-07 * @since 1.0 */ -class ModuleException extends Exception { +public class ModuleException extends Exception { private static final long serialVersionUID = -3036774290621088107L; private final String debugMessage; private final Pattern urlPattern = Pattern.compile("(https?://\\S+)(\\?\\S+)"); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java index f4eb3db..9abcf09 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java @@ -1,5 +1,5 @@ /* - * PrivateMessage.java + * NoticeMessage.java * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -53,7 +53,7 @@ public class NoticeMessage extends Message { /** * Create a new notice. * - * @param message The ntoice's message. + * @param message The notice's message. * @param color The color. */ public NoticeMessage(final String message, final String color) { diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index 43193b5..3727f01 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -50,11 +50,11 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class UtilsTest { - static final String ASCII = + private static final String ASCII = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; - final Calendar cal = Calendar.getInstance(); - final LocalDateTime localDateTime = + private final Calendar cal = Calendar.getInstance(); + private final LocalDateTime localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0); @BeforeClass diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index 34425c9..b255c9a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -1,5 +1,5 @@ /* - * CurrencyConvertTest.java + * CurrencyConverterTest.java * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java index 2f96c16..b2d1686 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -1,5 +1,5 @@ /* - * properties.java + * LocalProperties.java * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/version.properties b/version.properties index d553894..a6c948e 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Wed Apr 10 00:37:04 PDT 2019 -version.buildmeta=261 +#Fri Apr 19 20:42:41 PDT 2019 +version.buildmeta=303 version.major=0 version.minor=7 version.patch=3 From 72dce163e96f413b79a39941f411ef6fef21ebe7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Apr 2019 22:55:14 -0700 Subject: [PATCH 248/842] Moved CONNECT_TIMEOUT to Constants. --- src/main/java/net/thauvin/erik/mobibot/Constants.java | 7 ++++++- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 10 ++++------ .../java/net/thauvin/erik/mobibot/modules/Lookup.java | 5 +++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index 9dc0bf4..c5b4509 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -41,7 +41,12 @@ package net.thauvin.erik.mobibot; */ public final class Constants { /** - * The empty title string. + * The connect/read timeout in ms. + */ + public static final int CONNECT_TIMEOUT = 5000; + + /** + * The empty title string. */ public static final String NO_TITLE = "No Title"; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 1beb0fa..a81d108 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -97,10 +97,6 @@ import java.util.StringTokenizer; @SuppressWarnings("WeakerAccess") @Version(properties = "version.properties", className = "ReleaseInfo") public class Mobibot extends PircBot { - /** - * The connect/read timeout in ms. - */ - public static final int CONNECT_TIMEOUT = 5000; // The default port. private static final int DEFAULT_PORT = 6667; @@ -221,8 +217,10 @@ public class Mobibot extends PircBot { * @param p The bot's properties. */ public Mobibot(final String nickname, final String channel, final String logsDirPath, final Properties p) { - System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(CONNECT_TIMEOUT)); - System.getProperties().setProperty("sun.net.client.defaultReadTimeout", String.valueOf(CONNECT_TIMEOUT)); + System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", + String.valueOf(Constants.CONNECT_TIMEOUT)); + System.getProperties().setProperty("sun.net.client.defaultReadTimeout", + String.valueOf(Constants.CONNECT_TIMEOUT)); setName(nickname); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index ace6bca..17f087f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Mobibot; import org.apache.commons.net.whois.WhoisClient; @@ -123,9 +124,9 @@ public final class Lookup extends AbstractModule { final String[] lines; try { - whoisClient.setDefaultTimeout(Mobibot.CONNECT_TIMEOUT); + whoisClient.setDefaultTimeout(Constants.CONNECT_TIMEOUT); whoisClient.connect(host); - whoisClient.setSoTimeout(Mobibot.CONNECT_TIMEOUT); + whoisClient.setSoTimeout(Constants.CONNECT_TIMEOUT); whoisClient.setSoLinger(false, 0); if (host.equals(WHOIS_HOST)) { From 6518969031a8bc54bb7ddf296ecbe69ce0186c52 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Apr 2019 22:55:43 -0700 Subject: [PATCH 249/842] Cleanup. --- .../java/net/thauvin/erik/mobibot/entries/EntriesUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java index c22bae2..a43d30a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.entries; import net.thauvin.erik.mobibot.Commands; import net.thauvin.erik.mobibot.Constants; +import net.thauvin.erik.mobibot.Utils; /** * The <code>Utils</code> class. @@ -97,10 +98,10 @@ public final class EntriesUtils { if (Constants.NO_TITLE.equals(entry.getTitle())) { buff.append(entry.getTitle()); } else { - buff.append(net.thauvin.erik.mobibot.Utils.bold(entry.getTitle())); + buff.append(Utils.bold(entry.getTitle())); } - buff.append(" ( ").append(net.thauvin.erik.mobibot.Utils.green(entry.getLink())).append(" )"); + buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )"); return buff.toString(); } From f38f9ce74e35d0934df3a969462e809c0c4b2c21 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Apr 2019 22:56:33 -0700 Subject: [PATCH 250/842] Added tests for EntryLink. --- .../erik/mobibot/entries/EntryLinkTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java new file mode 100644 index 0000000..fd3edac --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java @@ -0,0 +1,100 @@ +/* + * EntryLinkTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.entries; + +import com.rometools.rome.feed.synd.SyndCategory; +import org.testng.annotations.Test; + +import java.security.SecureRandom; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>EntryUtilsTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-04-19 + * @since 1.0 + */ +public class EntryLinkTest { + private final EntryLink entryLink = new EntryLink("https://www.mobitopia.org/", "Mobitopia", "Skynx", + "JimH", "#mobitopia", "tag1, tag2,tag3 TAG4 tag5"); + + + @Test + public void testAddDeleteComment() { + int i = 0; + for (; i < 5; i++) { + entryLink.addComment("c" + i, "u" + i); + } + + i = 0; + for (final EntryComment comment : entryLink.getComments()) { + assertThat(comment.getComment()).as("getComment(" + i + ')').isEqualTo("c" + i); + assertThat(comment.getNick()).as("getNick(" + i + ')').isEqualTo("u" + i); + i++; + } + + final SecureRandom r = new SecureRandom(); + for (i = 0; entryLink.getCommentsCount() > 0; i++) { + entryLink.deleteComment(r.nextInt(entryLink.getCommentsCount())); + } + assertThat(entryLink.hasComments()).as("hasComments()").isFalse(); + + entryLink.addComment("nothing", "nobody"); + entryLink.setComment(0, "something", "somebody"); + assertThat(entryLink.getComment(0).getNick()).as("getNick(somebody)").isEqualTo("somebody"); + assertThat(entryLink.getComment(0).getComment()).as("getComment(something)").isEqualTo("something"); + + } + + @Test + public void testTags() { + final List<SyndCategory> tags = entryLink.getTags(); + + int i = 0; + for (SyndCategory tag : tags) { + assertThat(tag.getName()).as("tag.getName(" + i + ')').isEqualTo("tag" + (i + 1)); + i++; + } + + entryLink.setTags("-tag5"); + entryLink.setTags("+mobitopia"); + entryLink.setTags("tag4"); + entryLink.setTags("-mobitopia"); + + assertThat(entryLink.getPinboardTags()).as("getPinboardTags()") + .isEqualTo(entryLink.getNick() + ",tag1,tag2,tag3,tag4,mobitopia"); + } +} From 8047266f13d14ce4c71e0e58afb2bde61142d4c4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Apr 2019 23:39:24 -0700 Subject: [PATCH 251/842] Cleanup. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../net/thauvin/erik/mobibot/Commands.java | 190 ++++++++---------- .../net/thauvin/erik/mobibot/FeedReader.java | 2 +- .../thauvin/erik/mobibot/modules/Calc.java | 4 +- .../mobibot/modules/CurrencyConverter.java | 41 ++-- .../erik/mobibot/modules/GoogleSearch.java | 5 +- .../thauvin/erik/mobibot/modules/Joke.java | 21 +- .../thauvin/erik/mobibot/modules/Ping.java | 4 +- .../erik/mobibot/modules/StockQuote.java | 4 +- .../thauvin/erik/mobibot/modules/Twitter.java | 6 +- .../erik/mobibot/modules/Weather2.java | 25 ++- .../erik/mobibot/modules/WorldTime.java | 11 + .../erik/mobibot/msg/ErrorMessage.java | 1 + .../erik/mobibot/msg/PrivateMessage.java | 1 + .../erik/mobibot/msg/PublicMessage.java | 1 + .../net/thauvin/erik/mobibot/tell/Tell.java | 20 +- .../erik/mobibot/tell/TellMessage.java | 16 +- .../erik/mobibot/tell/TellMessagesMgr.java | 2 +- version.properties | 4 +- 19 files changed, 186 insertions(+), 178 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 1842aa0..94e0a12 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public final static String PROJECT = "mobibot"; public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1555731762870L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1555742153981L), ZoneId.systemDefault()); public final static int MAJOR = 0; public final static int MINOR = 7; public final static int PATCH = 3; public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "303"; - public final static String VERSION = "0.7.3-beta+303"; + public final static String BUILDMETA = "306"; + public final static String VERSION = "0.7.3-beta+306"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index b0a825d..1518e3c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -40,120 +40,98 @@ package net.thauvin.erik.mobibot; * @since 1.0 */ public final class Commands { - /** - * The add (back)log command. - */ - public static final String ADDLOG_CMD = "addlog"; - - /** - * The cycle command. - */ - public static final String CYCLE_CMD = "cycle"; - - /** - * Debug command line argument. - */ - public static final String DEBUG_ARG = "debug"; - - /** - * The debug command. - */ - public static final String DEBUG_CMD = "debug"; - - /** - * The die command. - */ - public static final String DIE_CMD = "die"; - - /** - * Help command line argument. - */ - public static final String HELP_ARG = "help"; - - /** - * The help command. - */ - public static final String HELP_CMD = "help"; - - /** - * The help on posting keyword. - */ - public static final String HELP_POSTING_KEYWORD = "posting"; - - /** - * The help on tags keyword. - */ - public static final String HELP_TAGS_KEYWORD = "tags"; - - /** - * The ignore command. - */ - public static final String IGNORE_CMD = "ignore"; - - /** - * The ignore <code>me</code> keyword. - */ - public static final String IGNORE_ME_KEYWORD = "me"; - - /** - * The info command. - */ - public static final String INFO_CMD = "info"; - /** * The link command. */ public static final String LINK_CMD = "L"; - - /** - * The me command. - */ - public static final String ME_CMD = "me"; - - /** - * The msg command. - */ - public static final String MSG_CMD = "msg"; - - /** - * The nick command. - */ - public static final String NICK_CMD = "nick"; - - /** - * Properties command line argument. - */ - public static final String PROPS_ARG = "properties"; - - /** - * The recap command. - */ - public static final String RECAP_CMD = "recap"; - - /** - * The say command. - */ - public static final String SAY_CMD = "say"; - - /** - * The users command. - */ - public static final String USERS_CMD = "users"; - - /** - * Properties version line argument. - */ - public static final String VERSION_ARG = "version"; - - /** - * The version command. - */ - public static final String VERSION_CMD = "version"; - /** * The view command. */ public static final String VIEW_CMD = "view"; + /** + * The add (back)log command. + */ + static final String ADDLOG_CMD = "addlog"; + /** + * The cycle command. + */ + static final String CYCLE_CMD = "cycle"; + /** + * Debug command line argument. + */ + static final String DEBUG_ARG = "debug"; + /** + * The debug command. + */ + static final String DEBUG_CMD = "debug"; + /** + * The die command. + */ + static final String DIE_CMD = "die"; + /** + * Help command line argument. + */ + static final String HELP_ARG = "help"; + /** + * The help command. + */ + static final String HELP_CMD = "help"; + /** + * The help on posting keyword. + */ + static final String HELP_POSTING_KEYWORD = "posting"; + /** + * The help on tags keyword. + */ + static final String HELP_TAGS_KEYWORD = "tags"; + /** + * The ignore command. + */ + static final String IGNORE_CMD = "ignore"; + /** + * The ignore <code>me</code> keyword. + */ + static final String IGNORE_ME_KEYWORD = "me"; + /** + * The info command. + */ + static final String INFO_CMD = "info"; + /** + * The me command. + */ + static final String ME_CMD = "me"; + /** + * The msg command. + */ + static final String MSG_CMD = "msg"; + /** + * The nick command. + */ + static final String NICK_CMD = "nick"; + /** + * Properties command line argument. + */ + static final String PROPS_ARG = "properties"; + /** + * The recap command. + */ + static final String RECAP_CMD = "recap"; + /** + * The say command. + */ + static final String SAY_CMD = "say"; + /** + * The users command. + */ + static final String USERS_CMD = "users"; + /** + * Properties version line argument. + */ + static final String VERSION_ARG = "version"; + /** + * The version command. + */ + static final String VERSION_CMD = "version"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 3ea1fcb..53c3a15 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -72,7 +72,7 @@ class FeedReader implements Runnable { * @param sender The nick of the person who sent the message. * @param url The URL to fetch. */ - public FeedReader(final Mobibot bot, final String sender, final String url) { + FeedReader(final Mobibot bot, final String sender, final String url) { this.bot = bot; this.sender = sender; this.url = url; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 12880df..931fc65 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -58,7 +58,9 @@ public class Calc extends AbstractModule { } /** - * Calculate. + * Performs a calculation. + * + * <p>1 + 1 * 2</p> * * @param query The query. * @return The calculation result. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index a35d588..190d50d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -82,6 +82,15 @@ public final class CurrencyConverter extends ThreadedModule { commands.add(CURRENCY_CMD); } + /** + * Converts from a currency to another. + * + * <p>100 USD to EUR</p> + * + * @param query The query. + * @return The {@link Message} contained the converted currency. + * @throws ModuleException If an error occurs while converting. + */ static Message convertCurrency(final String query) throws ModuleException { if (EXCHANGE_RATES.isEmpty()) { try { @@ -177,22 +186,6 @@ public final class CurrencyConverter extends ThreadedModule { super.commandResponse(bot, sender, args, isPrivate); } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To convert from one currency to another:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); - - if (args.endsWith(CURRENCY_CMD)) { - bot.send(sender, "For a listing of currency rates:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); - bot.send(sender, "For a listing of supported currencies:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); - } - } - /** * Converts the specified currencies. */ @@ -215,4 +208,20 @@ public final class CurrencyConverter extends ThreadedModule { } } } + + /** + * {@inheritDoc} + */ + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To convert from one currency to another:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); + + if (args.endsWith(CURRENCY_CMD)) { + bot.send(sender, "For a listing of currency rates:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); + bot.send(sender, "For a listing of supported currencies:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 4894afa..866def3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -81,8 +81,9 @@ public final class GoogleSearch extends ThreadedModule { * * @param query The search query. * @param apiKey The Google API key. - * The Google CSE key. - * @param cseKey The Google search results. + * @param cseKey The Google CSE key. + * @return The {@link Message} array containing the search results. + * @throws ModuleException If an error occurs while searching. */ @SuppressFBWarnings(value = {"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) static ArrayList<Message> searchGoogle(final String query, final String apiKey, final String cseKey) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 2e6a840..95fe7ab 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -68,7 +68,8 @@ public final class Joke extends ThreadedModule { /** * Retrieves a random joke. * - * @return The new joke. + * @return The {@link Message} containing the new joke. + * @throws ModuleException If an error occurs while retrieving a new joke. */ static Message randomJoke() throws ModuleException { try { @@ -103,15 +104,6 @@ public final class Joke extends ThreadedModule { new Thread(() -> run(bot, sender, args)).start(); } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To retrieve a random joke:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD)); - } - /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. */ @@ -123,4 +115,13 @@ public final class Joke extends ThreadedModule { bot.send(sender, e.getMessage()); } } + + /** + * {@inheritDoc} + */ + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To retrieve a random joke:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD)); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index bb554f5..03b7945 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -51,7 +51,9 @@ public class Ping extends AbstractModule { */ private static final String PING_CMD = "ping"; - // The ping responses. + /** + * The ping responses. + */ static final List<String> PINGS = Arrays.asList( "is barely alive.", diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index aae368d..23f0f80 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -74,10 +74,10 @@ public final class StockQuote extends ThreadedModule { } /** - * Get a stock quote. + * Retrieves a stock quote. * * @param symbol The stock symbol. - * @return The stock quote. + * @return The {@link Message} array containing the stock quote. * @throws ModuleException If an errors occurs. */ static ArrayList<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 465424b..f28fd31 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -68,7 +68,7 @@ public final class Twitter extends ThreadedModule { } /** - * Post on Twitter. + * Posts on Twitter. * * @param consumerKey The consumer key. * @param consumerSecret The consumer secret. @@ -77,7 +77,7 @@ public final class Twitter extends ThreadedModule { * @param handle The Twitter handle (dm) or nickname. * @param message The message to post. * @param isDm The direct message flag. - * @return The {@link Message} to send back. + * @return The confirmation {@link Message}. * @throws ModuleException If an error occurs while posting. */ static Message twitterPost(final String consumerKey, @@ -125,7 +125,7 @@ public final class Twitter extends ThreadedModule { } /** - * Post on Twitter. + * Posts on Twitter. * * @param handle The Twitter handle (dm) or nickname. * @param message The message to post. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 2f51d3a..a9dae96 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -73,12 +73,6 @@ public class Weather2 extends ThreadedModule { properties.put(OWM_API_KEY_PROP, ""); } - @SuppressWarnings("AvoidEscapedUnicodeCharacters") - private static String getTemps(final Double d) { - final double c = (d - 32) * 5 / 9; - return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; - } - private static OWM.Country getCountry(final String countryCode) { for (final OWM.Country c : OWM.Country.values()) { if (c.name().equalsIgnoreCase(countryCode)) { @@ -89,6 +83,25 @@ public class Weather2 extends ThreadedModule { return OWM.Country.UNITED_STATES; } + @SuppressWarnings("AvoidEscapedUnicodeCharacters") + private static String getTemps(final Double d) { + final double c = (d - 32) * 5 / 9; + return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; + } + + /** + * Retrieves the weather data. + * + * <ul> + * <li>98204</li> + * <li>London, UK</li> + * </ul> + * + * @param query The query. + * @param apiKey The API key. + * @return The {@link Message} array containing the weather data. + * @throws ModuleException If an error occurs while retrieving the weather data. + */ static ArrayList<Message> getWeather(final String query, final String apiKey) throws ModuleException { if (!Utils.isValidString(apiKey)) { throw new ModuleException(Utils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing."); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index d0b5c0b..6931296 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -164,6 +164,17 @@ public final class WorldTime extends AbstractModule { return String.format("%c%03d", '@', beats); } + /** + * Returns the world time. + * + * <ul> + * <li>PST</li> + * <li>BEATS</li> + * </ul> + * + * @param query The query. + * @return The {@link Message} containing the world time. + */ static Message worldTime(final String query) { final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim().toUpperCase()))); final String response; diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java index a558dc0..8e6ff2f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java @@ -57,6 +57,7 @@ public class ErrorMessage extends Message { * @param message The message. * @param color The message color. */ + @SuppressWarnings("unused") public ErrorMessage(final String message, final String color) { this.setMessage(message); this.setError(true); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java index 18bf7a2..e1a48cf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java @@ -39,6 +39,7 @@ package net.thauvin.erik.mobibot.msg; * @created 2019-04-09 * @since 1.0 */ +@SuppressWarnings("unused") public class PrivateMessage extends Message { public PrivateMessage(final String message) { this.setMessage(message); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java index d8ab1b2..702f0a3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java @@ -44,6 +44,7 @@ public class PublicMessage extends Message { this.setMessage(message); } + @SuppressWarnings("unused") public PublicMessage(final String message, final String color) { this.setMessage(message); this.setColor(color); diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java index 57542df..66c6cc4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java @@ -47,42 +47,30 @@ import java.util.concurrent.CopyOnWriteArrayList; * @since 1.0 */ public class Tell { - /** - * The all keyword. - */ - public static final String TELL_ALL_KEYWORD = "all"; - /** * The tell command. */ public static final String TELL_CMD = "tell"; - /** - * The delete command. - */ - public static final String TELL_DEL_KEYWORD = "del"; - // The default maximum number of days to keep messages. private static final int DEFAULT_TELL_MAX_DAYS = 7; - // The default message max queue size. private static final int DEFAULT_TELL_MAX_SIZE = 50; - // The serialized object file extension. private static final String SER_EXT = ".ser"; + // The all keyword. + private static final String TELL_ALL_KEYWORD = "all"; + //T he delete command. + private static final String TELL_DEL_KEYWORD = "del"; // The bot instance. private final Mobibot bot; - // The maximum number of days to keep messages. private final int maxDays; - // The message maximum queue size. private final int maxSize; - // The messages queue. private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); - // The serialized object file. private final String serializedObject; diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java index 657fdaf..1c2cd4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java @@ -62,7 +62,7 @@ public class TellMessage implements Serializable { * @param recipient The recipient's nick. * @param message The message. */ - public TellMessage(final String sender, final String recipient, final String message) { + TellMessage(final String sender, final String recipient, final String message) { this.sender = sender; this.recipient = recipient; this.message = message; @@ -94,7 +94,7 @@ public class TellMessage implements Serializable { * * @return <code>true</code> if the message is queued. */ - public LocalDateTime getQueued() { + LocalDateTime getQueued() { return queued; } @@ -112,7 +112,7 @@ public class TellMessage implements Serializable { * * @return The recipient of the message. */ - public String getRecipient() { + String getRecipient() { return recipient; } @@ -131,7 +131,7 @@ public class TellMessage implements Serializable { * @param nick The nickname to match with. * @return <code>true</code> if the nickname matches. */ - public boolean isMatch(final String nick) { + boolean isMatch(final String nick) { return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); } @@ -141,7 +141,7 @@ public class TellMessage implements Serializable { * @param id The ID to match with. * @return <code>true</code> if the id matches. */ - public boolean isMatchId(final String id) { + boolean isMatchId(final String id) { return this.id.equals(id); } @@ -150,7 +150,7 @@ public class TellMessage implements Serializable { * * @return <code>true</code> if the sender has been notified. */ - public boolean isNotified() { + boolean isNotified() { return isNotified; } @@ -166,14 +166,14 @@ public class TellMessage implements Serializable { /** * Sets the notified flag. */ - public void setIsNotified() { + void setIsNotified() { isNotified = true; } /** * Sets the received flag. */ - public void setIsReceived() { + void setIsReceived() { received = LocalDateTime.now(Clock.systemUTC()); isReceived = true; } diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java index 6c9ca27..4579c4c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java @@ -73,7 +73,7 @@ final class TellMessagesMgr { * @param tellMaxDays The maximum number of days to keep messages for. * @return <code>True</code> if the queue was cleaned. */ - public static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) { + static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) { final LocalDateTime today = LocalDateTime.now(Clock.systemUTC()); boolean cleaned = false; diff --git a/version.properties b/version.properties index a6c948e..09b80cc 100644 --- a/version.properties +++ b/version.properties @@ -1,6 +1,6 @@ #Generated by the Semver Plugin for Gradle -#Fri Apr 19 20:42:41 PDT 2019 -version.buildmeta=303 +#Fri Apr 19 23:35:53 PDT 2019 +version.buildmeta=306 version.major=0 version.minor=7 version.patch=3 From e5398d73ec8c8c470d0164d7e8b8118387c0b3aa Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:15:06 -0700 Subject: [PATCH 252/842] Added repeat versions variables. --- build.gradle | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index b678563..7b837d1 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,12 @@ mainClassName = packageName + '.Mobibot' compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") + +ext { + log4j = '2.11.2' + spotbugs_annotations = '3.1.12' +} + repositories { mavenLocal() mavenCentral() @@ -38,9 +44,9 @@ dependencies { compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile 'org.apache.logging.log4j:log4j-api:2.11.2' - compile 'org.apache.logging.log4j:log4j-core:2.11.2' - compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.2' + compile "org.apache.logging.log4j:log4j-api:$log4j" + compile "org.apache.logging.log4j:log4j-core:$log4j" + compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j" compile 'commons-cli:commons-cli:1.4' @@ -62,8 +68,10 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.12.2' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' + spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.3.sb' - compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.11' + compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" + testCompileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" } test { From 7ff30c1ce8b8bf3c03ade6d5d46470d1627bee64 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:20:42 -0700 Subject: [PATCH 253/842] spotbugs cleanups. --- config/spotbugs/excludeFilter.xml | 14 ++++++++++---- .../net/thauvin/erik/mobibot/FeedReader.java | 5 ++--- .../net/thauvin/erik/mobibot/TwitterOAuth.java | 2 +- .../java/net/thauvin/erik/mobibot/Utils.java | 18 ++++++++---------- .../erik/mobibot/entries/EntriesUtils.java | 2 ++ .../erik/mobibot/entries/EntryLink.java | 6 ++++-- .../mobibot/modules/CurrencyConverter.java | 4 ++-- .../net/thauvin/erik/mobibot/modules/Joke.java | 2 +- .../net/thauvin/erik/mobibot/msg/Message.java | 2 +- .../net/thauvin/erik/mobibot/tell/Tell.java | 12 ++++++++---- .../erik/mobibot/entries/EntryLinkTest.java | 2 +- .../mobibot/modules/AbstractModuleTest.java | 4 ++++ .../erik/mobibot/modules/GoogleSearchTest.java | 2 ++ .../erik/mobibot/modules/StockQuoteTest.java | 3 +++ .../erik/mobibot/modules/TwitterTest.java | 2 ++ .../erik/mobibot/modules/Weather2Test.java | 2 ++ 16 files changed, 53 insertions(+), 29 deletions(-) diff --git a/config/spotbugs/excludeFilter.xml b/config/spotbugs/excludeFilter.xml index 4590e4c..e2ac6d0 100644 --- a/config/spotbugs/excludeFilter.xml +++ b/config/spotbugs/excludeFilter.xml @@ -12,13 +12,19 @@ <Or> <Bug pattern="PATH_TRAVERSAL_IN"/> <Bug pattern="PATH_TRAVERSAL_OUT"/> + </Or> <Confidence value="2"/> </Match> <Match> - <Class name="net.thauvin.erik.mobibot.Mobibot"/> - <Method name="main"/> - <Bug pattern="PATH_TRAVERSAL_OUT"/> - <Confidence value="1"/> + <Or> + <Class name="net.thauvin.erik.mobibot.Mobibot"/> + <Class name="net.thauvin.erik.mobibot.Pinboard"/> + <Class name="net.thauvin.erik.mobibot.FeedReader"/> + <Class name="net.thauvin.erik.mobibot.tell.Tell"/> + <Package name="net.thauvin.erik.mobibot.modules.*"/> + <Package name="net.thauvin.erik.mobibot.entries.*"/> + </Or> + <Bug pattern="FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY"/> </Match> </FindBugsFilter> diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 53c3a15..e313e3f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot; import com.rometools.rome.feed.synd.SyndEntry; -import com.rometools.rome.feed.synd.SyndEntryImpl; import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.XmlReader; @@ -87,10 +86,10 @@ class FeedReader implements Runnable { final SyndFeed feed = input.build(new XmlReader(new URL(url))); SyndEntry item; - final List items = feed.getEntries(); + final List<SyndEntry> items = feed.getEntries(); for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { - item = (SyndEntryImpl) items.get(i); + item = items.get(i); bot.send(sender, item.getTitle()); bot.send(sender, TAB_INDENT + Utils.green(item.getLink())); } diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index e84758c..f69fb8d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -33,7 +33,7 @@ public final class TwitterOAuth { * * @param args The consumerKey and consumerSecret should be passed as arguments. */ - @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING") + @SuppressFBWarnings({"DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE"}) public static void main(final String[] args) throws Exception { if (args.length == 2) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index d9ca5be..0b10b26 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -95,9 +95,9 @@ public final class Utils { * @return The colorized string. */ static String colorize(final String s, final String color) { - if (!Utils.isValidString(color) || color.equals(Colors.NORMAL)) { + if (!Utils.isValidString(color) || Colors.NORMAL.equals(color)) { return s; - } else if (color.equals(Colors.BOLD) || color.equals(Colors.REVERSE)) { + } else if (Colors.BOLD.equals(color) || Colors.REVERSE.equals(color)) { return color + s + color; } @@ -247,14 +247,12 @@ public final class Utils { * @return The unescaped string. */ public static String unescapeXml(final String str) { - String s = str.replaceAll("&", "&"); - s = s.replaceAll("<", "<"); - s = s.replaceAll(">", ">"); - s = s.replaceAll(""", "\""); - s = s.replaceAll("'", "'"); - s = s.replaceAll("'", "'"); - - return s; + return str.replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll(""", "\"") + .replaceAll("'", "'") + .replaceAll("'", "'"); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java index a43d30a..27526d3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.entries; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Commands; import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Utils; @@ -84,6 +85,7 @@ public final class EntriesUtils { * @param isView Set to true to display the number of comments. * @return The entry's link. */ + @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.") public static String buildLink(final int index, final EntryLink entry, final boolean isView) { final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 021060f..472367f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.entries; import com.rometools.rome.feed.synd.SyndCategory; import com.rometools.rome.feed.synd.SyndCategoryImpl; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.Serializable; import java.util.Calendar; @@ -291,6 +292,7 @@ public class EntryLink implements Serializable { * * @param tags The space-delimited tags. */ + @SuppressFBWarnings(value = "STT_STRING_PARSING_A_FIELD") public final void setTags(final String tags) { if (tags != null) { final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); @@ -310,7 +312,7 @@ public class EntryLink implements Serializable { if (mod == '-') { // Don't remove the channel tag, if any. - if (!tag.getName().equals(channel.substring(1))) { + if (!channel.substring(1).equals(tag.getName())) { this.tags.remove(tag); } } else if (mod == '+') { @@ -334,7 +336,7 @@ public class EntryLink implements Serializable { * * @param tags The tags. */ - private void setTags(final List<SyndCategory> tags) { + final void setTags(final List<SyndCategory> tags) { this.tags.addAll(tags); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 190d50d..c23ddd1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -130,7 +130,7 @@ public final class CurrencyConverter extends ThreadedModule { final String[] cmds = query.split(" "); if (cmds.length == 4) { - if (cmds[3].equals(cmds[1]) || cmds[0].equals("0")) { + if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) { return new ErrorMessage("You're kidding, right?"); } else { try { @@ -153,7 +153,7 @@ public final class CurrencyConverter extends ThreadedModule { "The supported currencies are: " + EXCHANGE_RATES.keySet().toString(), e); } } - } else if (query.equals(CURRENCY_RATES_KEYWORD)) { + } else if (CURRENCY_RATES_KEYWORD.equals(query)) { final StringBuilder buff = new StringBuilder('[' + pubDate + "]: "); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 95fe7ab..07cbdf9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -107,7 +107,7 @@ public final class Joke extends ThreadedModule { /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. */ - void run(final Mobibot bot, final String sender, String arg) { + void run(final Mobibot bot, final String sender, final String arg) { try { bot.send(Utils.cyan(randomJoke().getMessage())); } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 47c3f1e..6a9bf20 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -178,7 +178,7 @@ public class Message { * * @param isPrivate The private flag. */ - public void setPrivate(boolean isPrivate) { + public void setPrivate(final boolean isPrivate) { this.isPrivate = isPrivate; } } diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java index 66c6cc4..dec97fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.tell; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Commands; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; @@ -100,7 +101,8 @@ public class Tell { * * @return <code>true</code> if the queue was cleaned. */ - private boolean clean() { + @SuppressWarnings("WeakerAccess") + final boolean clean() { if (bot.getLogger().isDebugEnabled()) { bot.getLogger().debug("Cleaning the messages."); } @@ -138,12 +140,13 @@ public class Tell { * @param sender The sender's nick. * @param cmds The commands string. */ + @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.") public void response(final String sender, final String cmds) { final String arrow = " --> "; if (!Utils.isValidString(cmds)) { helpResponse(sender); } else if (cmds.startsWith(Commands.VIEW_CMD)) { - if (bot.isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD)) { + if (bot.isOp(sender) && (Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) { if (!messages.isEmpty()) { for (final TellMessage message : messages) { bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) @@ -201,7 +204,7 @@ public class Tell { final String id = split[1]; boolean deleted = false; - if (id.equalsIgnoreCase(TELL_ALL_KEYWORD)) { + if (TELL_ALL_KEYWORD.equalsIgnoreCase(id)) { for (final TellMessage message : messages) { if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) { messages.remove(message); @@ -272,7 +275,8 @@ public class Tell { /** * Saves the messages queue. */ - private void save() { + @SuppressWarnings("WeakerAccess") + final void save() { TellMessagesMgr.save(serializedObject, messages, bot.getLogger()); } diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java index fd3edac..fd0650d 100644 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java @@ -84,7 +84,7 @@ public class EntryLinkTest { final List<SyndCategory> tags = entryLink.getTags(); int i = 0; - for (SyndCategory tag : tags) { + for (final SyndCategory tag : tags) { assertThat(tag.getName()).as("tag.getName(" + i + ')').isEqualTo("tag" + (i + 1)); i++; } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java index eb678c5..b28d3f4 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -32,6 +32,8 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -41,7 +43,9 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2019-04-07 * @since 1.0 */ + final class AbstractModuleTest { + @SuppressFBWarnings("CE_CLASS_ENVY") static void testAbstractModule(final AbstractModule module) { final String name = module.getClass().getName(); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 92d63ae..c81b883 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; @@ -52,6 +53,7 @@ public class GoogleSearchTest extends LocalProperties { AbstractModuleTest.testAbstractModule(new GoogleSearch()); } + @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE") @Test public void testSearchGoogle() throws ModuleException { final String apiKey = LocalProperties.getProperty(GoogleSearch.GOOGLE_API_KEY_PROP); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index fc2ccec..b4fff14 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; @@ -46,7 +47,9 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2019-04-07 * @since 1.0 */ + public class StockQuoteTest extends LocalProperties { + @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE") @Test public void testGetQuote() throws ModuleException { final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java index 3293956..cf64b4d 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Constants; import org.testng.annotations.Test; @@ -48,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class TwitterTest { + @SuppressFBWarnings("MDM") private String getCi() { if ("true".equals(System.getenv("CIRCLECI"))) { return "CircleCI"; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index 97e395f..2ac1b0a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; @@ -47,6 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class Weather2Test extends LocalProperties { + @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") @Test public void testWeather() throws ModuleException { ArrayList<Message> messages = Weather2.getWeather("98204", From 48a6c29fc9452226c348f4b476fd3b9a27e7d325 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:22:06 -0700 Subject: [PATCH 254/842] Reworked deleting old message using removeIf instead of in a loop. --- .../thauvin/erik/mobibot/tell/TellMessagesMgr.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java index 4579c4c..fdddd99 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java @@ -75,17 +75,8 @@ final class TellMessagesMgr { */ static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) { final LocalDateTime today = LocalDateTime.now(Clock.systemUTC()); - boolean cleaned = false; - for (final TellMessage message : tellMessages) { - final LocalDateTime maxDate = message.getQueued().plusDays(tellMaxDays); - if (maxDate.isBefore(today)) { - tellMessages.remove(message); - cleaned = true; - } - } - - return cleaned; + return tellMessages.removeIf(o -> o.getQueued().plusDays(tellMaxDays).isBefore(today)); } /** From ee7111d91d97a81bdf66ee8e057e9a457312ecce Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:26:21 -0700 Subject: [PATCH 255/842] Now using java.nio newOutputStream and newInputStream. --- .../net/thauvin/erik/mobibot/Mobibot.java | 8 +++--- .../erik/mobibot/entries/EntriesMgr.java | 26 ++++++++++--------- .../erik/mobibot/tell/TellMessagesMgr.java | 9 ++++--- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index a81d108..641d569 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -73,11 +73,13 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.time.Clock; import java.time.LocalDateTime; import java.util.ArrayList; @@ -354,8 +356,8 @@ public class Mobibot extends PircBot { } else { final Properties p = new Properties(); - try (final FileInputStream fis = new FileInputStream( - line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties"))) { + try (final InputStream fis = Files.newInputStream(Paths.get( + line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { // Load the properties files p.load(fis); } catch (FileNotFoundException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index 1e485b9..3a3920f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -41,16 +41,17 @@ import com.rometools.rome.feed.synd.SyndFeedImpl; import com.rometools.rome.io.FeedException; import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.SyndFeedOutput; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -105,15 +106,15 @@ public final class EntriesMgr { final SyndFeedInput input = new SyndFeedInput(); try (final InputStreamReader reader = - new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { + new InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); - final List items = feed.getEntries(); + final List<SyndEntry> items = feed.getEntries(); SyndEntry item; for (int i = items.size() - 1; i >= 0; i--) { - item = (SyndEntryImpl) items.get(i); + item = items.get(i); history.add(item.getTitle()); } } @@ -139,12 +140,12 @@ public final class EntriesMgr { final String today; try (final InputStreamReader reader = new InputStreamReader( - new FileInputStream(file), StandardCharsets.UTF_8)) { + Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); today = Utils.isoLocalDate(feed.getPublishedDate()); - final List items = feed.getEntries(); + final List<SyndEntry> items = feed.getEntries(); SyndEntry item; SyndContent description; String[] comments; @@ -152,7 +153,7 @@ public final class EntriesMgr { EntryLink entry; for (int i = items.size() - 1; i >= 0; i--) { - item = (SyndEntryImpl) items.get(i); + item = items.get(i); author = item.getAuthor() .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); entry = new EntryLink(item.getLink(), @@ -188,6 +189,7 @@ public final class EntriesMgr { * @param history The history array. * @param isDayBackup Set the true if the daily backup file should also be created. */ + @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.") public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, @@ -204,7 +206,7 @@ public final class EntriesMgr { SyndEntry item; SyndContent description; try (final Writer fw = new OutputStreamWriter( - new FileOutputStream(bot.getLogsDir() + CURRENT_XML), StandardCharsets.UTF_8)) { + Files.newOutputStream(Paths.get(bot.getLogsDir() + CURRENT_XML)), StandardCharsets.UTF_8)) { rss.setFeedType("rss_2.0"); rss.setTitle(bot.getChannel() + " IRC Links"); rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); @@ -263,8 +265,8 @@ public final class EntriesMgr { } try (final Writer fw = new OutputStreamWriter( - new FileOutputStream( - bot.getLogsDir() + bot.getToday() + XML_EXT), StandardCharsets.UTF_8)) { + Files.newOutputStream(Paths.get( + bot.getLogsDir() + bot.getToday() + XML_EXT)), StandardCharsets.UTF_8)) { output.output(rss, fw); } @@ -279,7 +281,7 @@ public final class EntriesMgr { } try (final Writer fw = new OutputStreamWriter( - new FileOutputStream(bot.getLogsDir() + NAV_XML), StandardCharsets.UTF_8)) { + Files.newOutputStream(Paths.get(bot.getLogsDir() + NAV_XML)), StandardCharsets.UTF_8)) { rss = new SyndFeedImpl(); rss.setFeedType("rss_2.0"); rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java index fdddd99..a445815 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java @@ -36,14 +36,14 @@ import org.apache.logging.log4j.Logger; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.time.Clock; import java.time.LocalDateTime; import java.util.ArrayList; @@ -90,7 +90,8 @@ final class TellMessagesMgr { public static List<TellMessage> load(final String file, final Logger logger) { try { - try (final ObjectInput input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { + try (final ObjectInput input = new ObjectInputStream( + new BufferedInputStream(Files.newInputStream(Paths.get(file))))) { if (logger.isDebugEnabled()) { logger.debug("Loading the messages."); } @@ -119,7 +120,7 @@ final class TellMessagesMgr { try { try (final ObjectOutput output = new ObjectOutputStream( - new BufferedOutputStream(new FileOutputStream(file)))) { + new BufferedOutputStream(Files.newOutputStream(Paths.get(file))))) { if (logger.isDebugEnabled()) { logger.debug("Saving the messages."); } From 0732a0ff91f314b49c9b16648ffeae9199928b92 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:27:25 -0700 Subject: [PATCH 256/842] Now using an unmodifiableMap. --- .../erik/mobibot/modules/WorldTime.java | 164 +++++++++--------- 1 file changed, 85 insertions(+), 79 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 6931296..e9c0729 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; @@ -41,6 +42,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; +import java.util.Collections; import java.util.Map; import java.util.TreeMap; @@ -55,7 +57,7 @@ public final class WorldTime extends AbstractModule { // The beats (Internet Time) keyword. private static final String BEATS_KEYWORD = ".beats"; // The supported countries. - private static final Map<String, String> COUNTRIES_MAP = new TreeMap<>(); + private static final Map<String, String> COUNTRIES_MAP; /** * The time command. @@ -64,85 +66,88 @@ public final class WorldTime extends AbstractModule { static { // Initialize the countries map - COUNTRIES_MAP.put("AE", "Asia/Dubai"); - COUNTRIES_MAP.put("AF", "Asia/Kabul"); - COUNTRIES_MAP.put("AQ", "Antarctica/South_Pole"); - COUNTRIES_MAP.put("AT", "Europe/Vienna"); - COUNTRIES_MAP.put("AU", "Australia/Sydney"); - COUNTRIES_MAP.put("AKST", "America/Anchorage"); - COUNTRIES_MAP.put("AKDT", "America/Anchorage"); - COUNTRIES_MAP.put("BE", "Europe/Brussels"); - COUNTRIES_MAP.put("BR", "America/Sao_Paulo"); - COUNTRIES_MAP.put("CA", "America/Montreal"); - COUNTRIES_MAP.put("CDT", "America/Chicago"); - COUNTRIES_MAP.put("CET", "CET"); - COUNTRIES_MAP.put("CH", "Europe/Zurich"); - COUNTRIES_MAP.put("CN", "Asia/Shanghai"); - COUNTRIES_MAP.put("CST", "America/Chicago"); - COUNTRIES_MAP.put("CU", "Cuba"); - COUNTRIES_MAP.put("DE", "Europe/Berlin"); - COUNTRIES_MAP.put("DK", "Europe/Copenhagen"); - COUNTRIES_MAP.put("EDT", "America/New_York"); - COUNTRIES_MAP.put("EG", "Africa/Cairo"); - COUNTRIES_MAP.put("ER", "Africa/Asmara"); - COUNTRIES_MAP.put("ES", "Europe/Madrid"); - COUNTRIES_MAP.put("EST", "America/New_York"); - COUNTRIES_MAP.put("FI", "Europe/Helsinki"); - COUNTRIES_MAP.put("FR", "Europe/Paris"); - COUNTRIES_MAP.put("GB", "Europe/London"); - COUNTRIES_MAP.put("GMT", "GMT"); - COUNTRIES_MAP.put("GR", "Europe/Athens"); - COUNTRIES_MAP.put("HK", "Asia/Hong_Kong"); - COUNTRIES_MAP.put("HST", "Pacific/Honolulu"); - COUNTRIES_MAP.put("IE", "Europe/Dublin"); - COUNTRIES_MAP.put("IL", "Asia/Tel_Aviv"); - COUNTRIES_MAP.put("IN", "Asia/Kolkata"); - COUNTRIES_MAP.put("IQ", "Asia/Baghdad"); - COUNTRIES_MAP.put("IR", "Asia/Tehran"); - COUNTRIES_MAP.put("IS", "Atlantic/Reykjavik"); - COUNTRIES_MAP.put("IT", "Europe/Rome"); - COUNTRIES_MAP.put("JM", "Jamaica"); - COUNTRIES_MAP.put("JP", "Asia/Tokyo"); - COUNTRIES_MAP.put("LY", "Africa/Tripoli"); - COUNTRIES_MAP.put("MA", "Africa/Casablanca"); - COUNTRIES_MAP.put("MDT", "America/Denver"); - COUNTRIES_MAP.put("MH", "Kwajalein"); - COUNTRIES_MAP.put("MQ", "America/Martinique"); - COUNTRIES_MAP.put("MST", "America/Denver"); - COUNTRIES_MAP.put("MX", "America/Mexico_City"); - COUNTRIES_MAP.put("NL", "Europe/Amsterdam"); - COUNTRIES_MAP.put("NO", "Europe/Oslo"); - COUNTRIES_MAP.put("NP", "Asia/Katmandu"); - COUNTRIES_MAP.put("NZ", "Pacific/Auckland"); - COUNTRIES_MAP.put("PDT", "America/Los_Angeles"); - COUNTRIES_MAP.put("PH", "Asia/Manila"); - COUNTRIES_MAP.put("PK", "Asia/Karachi"); - COUNTRIES_MAP.put("PL", "Europe/Warsaw"); - COUNTRIES_MAP.put("PST", "America/Los_Angeles"); - COUNTRIES_MAP.put("PT", "Europe/Lisbon"); - COUNTRIES_MAP.put("PR", "America/Puerto_Rico"); - COUNTRIES_MAP.put("RU", "Europe/Moscow"); - COUNTRIES_MAP.put("SE", "Europe/Stockholm"); - COUNTRIES_MAP.put("SG", "Asia/Singapore"); - COUNTRIES_MAP.put("TH", "Asia/Bangkok"); - COUNTRIES_MAP.put("TM", "Asia/Ashgabat"); - COUNTRIES_MAP.put("TN", "Africa/Tunis"); - COUNTRIES_MAP.put("TR", "Europe/Istanbul"); - COUNTRIES_MAP.put("TW", "Asia/Taipei"); - COUNTRIES_MAP.put("UK", "Europe/London"); - COUNTRIES_MAP.put("US", "America/New_York"); - COUNTRIES_MAP.put("UTC", "UTC"); - COUNTRIES_MAP.put("VA", "Europe/Vatican"); - COUNTRIES_MAP.put("VE", "America/Caracas"); - COUNTRIES_MAP.put("VN", "Asia/Ho_Chi_Minh"); - COUNTRIES_MAP.put("ZA", "Africa/Johannesburg"); - COUNTRIES_MAP.put("ZULU", "Zulu"); - COUNTRIES_MAP.put("INTERNET", BEATS_KEYWORD); - COUNTRIES_MAP.put("BEATS", BEATS_KEYWORD); + final Map<String, String> countries = new TreeMap<>(); + countries.put("AE", "Asia/Dubai"); + countries.put("AF", "Asia/Kabul"); + countries.put("AQ", "Antarctica/South_Pole"); + countries.put("AT", "Europe/Vienna"); + countries.put("AU", "Australia/Sydney"); + countries.put("AKST", "America/Anchorage"); + countries.put("AKDT", "America/Anchorage"); + countries.put("BE", "Europe/Brussels"); + countries.put("BR", "America/Sao_Paulo"); + countries.put("CA", "America/Montreal"); + countries.put("CDT", "America/Chicago"); + countries.put("CET", "CET"); + countries.put("CH", "Europe/Zurich"); + countries.put("CN", "Asia/Shanghai"); + countries.put("CST", "America/Chicago"); + countries.put("CU", "Cuba"); + countries.put("DE", "Europe/Berlin"); + countries.put("DK", "Europe/Copenhagen"); + countries.put("EDT", "America/New_York"); + countries.put("EG", "Africa/Cairo"); + countries.put("ER", "Africa/Asmara"); + countries.put("ES", "Europe/Madrid"); + countries.put("EST", "America/New_York"); + countries.put("FI", "Europe/Helsinki"); + countries.put("FR", "Europe/Paris"); + countries.put("GB", "Europe/London"); + countries.put("GMT", "GMT"); + countries.put("GR", "Europe/Athens"); + countries.put("HK", "Asia/Hong_Kong"); + countries.put("HST", "Pacific/Honolulu"); + countries.put("IE", "Europe/Dublin"); + countries.put("IL", "Asia/Tel_Aviv"); + countries.put("IN", "Asia/Kolkata"); + countries.put("IQ", "Asia/Baghdad"); + countries.put("IR", "Asia/Tehran"); + countries.put("IS", "Atlantic/Reykjavik"); + countries.put("IT", "Europe/Rome"); + countries.put("JM", "Jamaica"); + countries.put("JP", "Asia/Tokyo"); + countries.put("LY", "Africa/Tripoli"); + countries.put("MA", "Africa/Casablanca"); + countries.put("MDT", "America/Denver"); + countries.put("MH", "Kwajalein"); + countries.put("MQ", "America/Martinique"); + countries.put("MST", "America/Denver"); + countries.put("MX", "America/Mexico_City"); + countries.put("NL", "Europe/Amsterdam"); + countries.put("NO", "Europe/Oslo"); + countries.put("NP", "Asia/Katmandu"); + countries.put("NZ", "Pacific/Auckland"); + countries.put("PDT", "America/Los_Angeles"); + countries.put("PH", "Asia/Manila"); + countries.put("PK", "Asia/Karachi"); + countries.put("PL", "Europe/Warsaw"); + countries.put("PST", "America/Los_Angeles"); + countries.put("PT", "Europe/Lisbon"); + countries.put("PR", "America/Puerto_Rico"); + countries.put("RU", "Europe/Moscow"); + countries.put("SE", "Europe/Stockholm"); + countries.put("SG", "Asia/Singapore"); + countries.put("TH", "Asia/Bangkok"); + countries.put("TM", "Asia/Ashgabat"); + countries.put("TN", "Africa/Tunis"); + countries.put("TR", "Europe/Istanbul"); + countries.put("TW", "Asia/Taipei"); + countries.put("UK", "Europe/London"); + countries.put("US", "America/New_York"); + countries.put("UTC", "UTC"); + countries.put("VA", "Europe/Vatican"); + countries.put("VE", "America/Caracas"); + countries.put("VN", "Asia/Ho_Chi_Minh"); + countries.put("ZA", "Africa/Johannesburg"); + countries.put("ZULU", "Zulu"); + countries.put("INTERNET", BEATS_KEYWORD); + countries.put("BEATS", BEATS_KEYWORD); ZoneId.getAvailableZoneIds().stream().filter(tz -> - !tz.contains("/") && tz.length() == 3 && !COUNTRIES_MAP.containsKey(tz)).forEach(tz -> - COUNTRIES_MAP.put(tz, tz)); + !tz.contains("/") && tz.length() == 3 && !countries.containsKey(tz)).forEach(tz -> + countries.put(tz, tz)); + + COUNTRIES_MAP = Collections.unmodifiableMap(countries); } /** @@ -175,12 +180,13 @@ public final class WorldTime extends AbstractModule { * @param query The query. * @return The {@link Message} containing the world time. */ + @SuppressFBWarnings(value = "STT_STRING_PARSING_A_FIELD") static Message worldTime(final String query) { final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim().toUpperCase()))); final String response; if (tz != null) { - if (tz.equals(BEATS_KEYWORD)) { + if (BEATS_KEYWORD.equals(tz)) { response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); } else { response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( From 57a02f9ffb13b4d38e9ba41573a4ce355f54ed57 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:27:41 -0700 Subject: [PATCH 257/842] spotbugs cleanup. --- src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 17f087f..78ed028 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -129,7 +129,7 @@ public final class Lookup extends AbstractModule { whoisClient.setSoTimeout(Constants.CONNECT_TIMEOUT); whoisClient.setSoLinger(false, 0); - if (host.equals(WHOIS_HOST)) { + if (WHOIS_HOST.equals(host)) { lines = whoisClient.query("n - " + query).split("\n"); } else { lines = whoisClient.query(query).split("\n"); From a43ef4f0e32fefd84f725cd018e4321ae4f182c8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:30:44 -0700 Subject: [PATCH 258/842] Improved dealing with ignored nicks. --- .../net/thauvin/erik/mobibot/Mobibot.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 641d569..89f6d71 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -85,8 +85,10 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Properties; +import java.util.Set; import java.util.StringTokenizer; /** @@ -170,7 +172,7 @@ public class Mobibot extends PircBot { // The history/backlogs array. private final List<String> history = new ArrayList<>(0); // The ignored nicks array. - private final List<String> ignoredNicks = new ArrayList<>(0); + private final Set<String> ignoredNicks = new HashSet<>(0); // The main channel. private final String ircChannel; // The IRC port. @@ -785,20 +787,16 @@ public class Mobibot extends PircBot { if (!isOp(sender)) { final String nick = sender.toLowerCase(); final boolean isMe = args.toLowerCase().startsWith(Commands.IGNORE_ME_KEYWORD); - - if (ignoredNicks.contains(nick)) { - if (isMe) { - ignoredNicks.remove(nick); - + if (isMe) { + if (ignoredNicks.remove(nick)) { send(sender, "You are no longer ignored."); } else { - send(sender, "You are currently ignored."); + ignoredNicks.add(nick); + send(sender, "You are now ignored."); } } else { - if (isMe) { - ignoredNicks.add(nick); - - send(sender, "You are now ignored."); + if (ignoredNicks.contains(nick)) { + send(sender, "You are currently ignored."); } else { send(sender, "You are not currently ignored."); } @@ -816,9 +814,7 @@ public class Mobibot extends PircBot { ignore = nick; } - if (ignoredNicks.contains(ignore)) { - ignoredNicks.remove(ignore); - } else { + if (!ignoredNicks.remove(ignore)) { ignoredNicks.add(ignore); } } From 86ca904c9eb9592d05d24173e426fe47a2dc78d3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:35:20 -0700 Subject: [PATCH 259/842] Implemented getChannelName(). --- .../java/net/thauvin/erik/mobibot/Mobibot.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 89f6d71..0f0d236 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -536,6 +536,16 @@ public class Mobibot extends PircBot { return ircChannel; } + /** + * Returns the current channel name. + * + * @return The current channel name. + */ + @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") + public final String getChannelName() { + return ircChannel.substring(1); + } + /** * Returns the irc server. * @@ -662,10 +672,9 @@ public class Mobibot extends PircBot { } else if (lcTopic.equals(Commands.VIEW_CMD)) { send(sender, "To list or search the current URL posts:"); send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); - } else if (lcTopic.equalsIgnoreCase(ircChannel.substring(1))) { + } else if (lcTopic.equalsIgnoreCase(getChannelName())) { send(sender, "To list the last 5 posts from the channel's weblog:"); - send(sender, helpIndent(getNick() + ": " + ircChannel.substring(1))); - } else if (lcTopic.equals(Commands.RECAP_CMD)) { + send(sender, helpIndent(getNick() + ": " + getChannelName())); send(sender, "To list the last 10 public channel messages:"); send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); } else if (lcTopic.equals(Commands.USERS_CMD)) { @@ -715,7 +724,7 @@ public class Mobibot extends PircBot { if (commandsList.isEmpty()) { commandsList.add(Commands.IGNORE_CMD); commandsList.add(Commands.INFO_CMD); - commandsList.add(ircChannel.substring(1)); + commandsList.add(getChannelName()); commandsList.add(Commands.HELP_POSTING_KEYWORD); commandsList.add(Commands.HELP_TAGS_KEYWORD); commandsList.add(Commands.RECAP_CMD); From c016b92db03969fb6311cea18a4903b717d57aa1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:37:27 -0700 Subject: [PATCH 260/842] Flipped equals checks against literal values. --- .../net/thauvin/erik/mobibot/Mobibot.java | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 0f0d236..20674b3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -652,7 +652,7 @@ public class Mobibot extends PircBot { private void helpResponse(final String sender, final String topic) { final String lcTopic = topic.toLowerCase().trim(); - if (lcTopic.equals(Commands.HELP_POSTING_KEYWORD)) { + if (Commands.HELP_POSTING_KEYWORD.equals(lcTopic)) { send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + '1')); @@ -666,45 +666,46 @@ public class Mobibot extends PircBot { send(sender, "To delete a comment, use its label and a minus sign: "); send(sender, helpIndent(Commands.LINK_CMD + "1.1:-")); send(sender, "You can also view a posting by saying its label."); - } else if (lcTopic.equals(Commands.HELP_TAGS_KEYWORD)) { + } else if (Commands.HELP_TAGS_KEYWORD.equals(lcTopic)) { send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:")); send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); - } else if (lcTopic.equals(Commands.VIEW_CMD)) { + } else if (Commands.VIEW_CMD.equals(lcTopic)) { send(sender, "To list or search the current URL posts:"); send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); } else if (lcTopic.equalsIgnoreCase(getChannelName())) { send(sender, "To list the last 5 posts from the channel's weblog:"); send(sender, helpIndent(getNick() + ": " + getChannelName())); + } else if (Commands.RECAP_CMD.equals(lcTopic)) { send(sender, "To list the last 10 public channel messages:"); send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); - } else if (lcTopic.equals(Commands.USERS_CMD)) { + } else if (Commands.USERS_CMD.equals(lcTopic)) { send(sender, "To list the users present on the channel:"); send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); - } else if (lcTopic.equals(Commands.INFO_CMD)) { + } else if (Commands.INFO_CMD.equals(lcTopic)) { send(sender, "To view information about the bot:"); send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); - } else if (lcTopic.equals(Commands.CYCLE_CMD) && isOp(sender)) { + } else if (Commands.CYCLE_CMD.equals(lcTopic) && isOp(sender)) { send(sender, "To have the bot leave the channel and come back:"); send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); - } else if (lcTopic.equals(Commands.ME_CMD) && isOp(sender)) { + } else if (Commands.ME_CMD.equals(lcTopic) && isOp(sender)) { send(sender, "To have the bot perform an action:"); send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); - } else if (lcTopic.equals(Commands.SAY_CMD) && isOp(sender)) { + } else if (Commands.SAY_CMD.equals(lcTopic) && isOp(sender)) { send(sender, "To have the bot say something on the channel:"); send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); - } else if (lcTopic.equals(Commands.VERSION_CMD) && isOp(sender)) { + } else if (Commands.VERSION_CMD.equals(lcTopic) && isOp(sender)) { send(sender, "To view the version data (bot, java, etc.):"); send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); - } else if (lcTopic.equals(Commands.MSG_CMD) && isOp(sender)) { + } else if (Commands.MSG_CMD.equals(lcTopic) && isOp(sender)) { send(sender, "To have the bot send a private message to someone:"); send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); - } else if (lcTopic.equals(Commands.IGNORE_CMD)) { + } else if (Commands.IGNORE_CMD.equals(lcTopic)) { send(sender, "To check your ignore status:"); send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); send(sender, "To toggle your ignore status:"); send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); - } else if (lcTopic.equals(Tell.TELL_CMD) && tell.isEnabled()) { + } else if (Tell.TELL_CMD.equals(lcTopic) && tell.isEnabled()) { tell.helpResponse(sender); } else { for (final AbstractModule module : MODULES) { @@ -1060,15 +1061,15 @@ public class Mobibot extends PircBot { if (cmd.startsWith(Commands.HELP_CMD)) { // mobibot: help helpResponse(sender, args); - } else if (cmd.equals(Commands.RECAP_CMD)) { // mobibot: recap + } else if (Commands.RECAP_CMD.equals(cmd)) { // mobibot: recap recapResponse(sender, false); - } else if (cmd.equals(Commands.USERS_CMD)) { // mobibot: users + } else if (Commands.USERS_CMD.equals(cmd)) { // mobibot: users usersResponse(sender, false); - } else if (cmd.equals(Commands.INFO_CMD)) { // mobibot: info + } else if (Commands.INFO_CMD.equals(cmd)) { // mobibot: info infoResponse(sender, false); - } else if (cmd.equals(Commands.VERSION_CMD)) { // mobbiot: version + } else if (Commands.VERSION_CMD.equals(cmd)) { // mobbiot: version versionResponse(sender, false); - } else if (cmd.equalsIgnoreCase(channel.substring(1))) { // mobibot: <channel> + } else if (cmd.equalsIgnoreCase(getChannelName())) { // mobibot: <channel> feedResponse(sender); } else if (cmd.startsWith(Commands.VIEW_CMD)) { // mobibot: view viewResponse(sender, args, false); @@ -1288,23 +1289,23 @@ public class Mobibot extends PircBot { } else if ("kill".equals(cmd) && isOp(sender)) { sendRawLine("QUIT : Poof!"); System.exit(0); - } else if (cmd.equals(Commands.DIE_CMD) && isOp(sender)) { + } else if (Commands.DIE_CMD.equals(cmd) && isOp(sender)) { send(ircChannel, sender + " has just signed my death sentence."); saveEntries(true); sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); - } else if (cmd.equals(Commands.CYCLE_CMD)) { + } else if (Commands.CYCLE_CMD.equals(cmd)) { send(ircChannel, sender + " has just asked me to leave. I'll be back!"); sleep(0); partChannel(ircChannel); sleep(10); joinChannel(ircChannel); - } else if (cmd.equals(Commands.RECAP_CMD)) { + } else if (Commands.RECAP_CMD.equals(cmd)) { recapResponse(sender, true); - } else if (cmd.equals(Commands.USERS_CMD)) { + } else if (Commands.USERS_CMD.equals(cmd)) { usersResponse(sender, true); - } else if (cmd.equals(Commands.ADDLOG_CMD) && (cmds.length > 1) && isOp(sender)) { + } else if ((cmds.length > 1) && isOp(sender) && Commands.ADDLOG_CMD.equals(cmd)) { // e.g. 2014-04-01 final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); if (backlog.exists()) { @@ -1313,21 +1314,21 @@ public class Mobibot extends PircBot { } else { send(sender, "The specified log could not be found."); } - } else if (cmd.equals(Commands.ME_CMD) && isOp(sender)) { + } else if (Commands.ME_CMD.equals(cmd) && isOp(sender)) { if (args.length() > 1) { action(args); } else { helpResponse(sender, Commands.ME_CMD); } - } else if (cmd.equals(Commands.NICK_CMD) && (cmds.length > 1) && isOp(sender)) { + } else if ((cmds.length > 1) && isOp(sender) && Commands.NICK_CMD.equals(cmd)) { changeNick(args); - } else if (cmd.equals(Commands.SAY_CMD) && isOp(sender)) { + } else if (Commands.SAY_CMD.equals(cmd) && isOp(sender)) { if (cmds.length > 1) { send(ircChannel, args, true); } else { helpResponse(sender, Commands.SAY_CMD); } - } else if (cmd.equals(Commands.MSG_CMD) && isOp(sender)) { + } else if (Commands.MSG_CMD.equals(cmd) && isOp(sender)) { if (cmds.length > 1) { final String[] msg = args.split(" ", 2); @@ -1339,15 +1340,15 @@ public class Mobibot extends PircBot { } else { helpResponse(sender, Commands.MSG_CMD); } - } else if (cmd.equals(Commands.VIEW_CMD)) { + } else if (Commands.VIEW_CMD.equals(cmd)) { viewResponse(sender, args, true); - } else if (cmd.equals(Tell.TELL_CMD) && tell.isEnabled()) { + } else if (Tell.TELL_CMD.equals(cmd) && tell.isEnabled()) { tell.response(sender, args); - } else if (cmd.equals(Commands.INFO_CMD)) { + } else if (Commands.INFO_CMD.equals(cmd)) { infoResponse(sender, true); - } else if (cmd.equals(Commands.VERSION_CMD)) { + } else if (Commands.VERSION_CMD.equals(cmd)) { versionResponse(sender, true); - } else if (cmd.equals(Commands.DEBUG_CMD) && isOp(sender)) { + } else if (Commands.DEBUG_CMD.equals(cmd) && isOp(sender)) { if (logger.isDebugEnabled()) { Configurator.setLevel(logger.getName(), loggerLevel); } else { From bae9c82ba8838fb2e64dc94f21be8336b3d008c2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 05:38:38 -0700 Subject: [PATCH 261/842] Spotbugs cleanups. --- .../net/thauvin/erik/mobibot/Mobibot.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 20674b3..c6d0ab9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -324,7 +324,8 @@ public class Mobibot extends PircBot { * * @param args The command line arguments. */ - @SuppressFBWarnings(value = {"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING"}) + @SuppressFBWarnings({"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING", + "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS"}) public static void main(final String[] args) { // Setup the command line options final Options options = new Options(); @@ -523,7 +524,7 @@ public class Mobibot extends PircBot { * * @param url The backlogs URL. */ - private void setBacklogsUrl(final String url) { + final void setBacklogsUrl(final String url) { backLogsUrl = url; } @@ -618,7 +619,7 @@ public class Mobibot extends PircBot { * * @param url The weblog URL. */ - private void setWeblogUrl(final String url) { + final void setWeblogUrl(final String url) { weblogUrl = url; } @@ -938,7 +939,8 @@ public class Mobibot extends PircBot { getName() + ReleaseInfo.VERSION + " has joined " + ircChannel, true); } catch (ModuleException e) { - logger.warn(e.getMessage(), e); + logger.warn( + "Failed to notify " + twitterHandle + " of joining " + ircChannel + " on Twitter.", e); } }).start(); } @@ -961,6 +963,7 @@ public class Mobibot extends PircBot { /** * {@inheritDoc} */ + @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.") @Override protected final void onMessage(final String channel, final String sender, @@ -1266,7 +1269,7 @@ public class Mobibot extends PircBot { /** * {@inheritDoc} */ - @SuppressFBWarnings(value = "DM_EXIT", justification = "Yes, we want to bail out.") + @SuppressFBWarnings(value = {"DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, we want to bail out.") @Override protected final void onPrivateMessage(final String sender, final String login, @@ -1424,7 +1427,7 @@ public class Mobibot extends PircBot { * * @param isDayBackup Set the <code>true</code> if the daily backup file should also be created. */ - private void saveEntries(final boolean isDayBackup) { + final void saveEntries(final boolean isDayBackup) { EntriesMgr.saveEntries(this, entries, history, isDayBackup); } @@ -1493,7 +1496,7 @@ public class Mobibot extends PircBot { * @param color The message's color. * @param isPrivate The private flag. */ - public final void send(final String who, final String message, final String color, boolean isPrivate) { + public final void send(final String who, final String message, final String color, final boolean isPrivate) { send(who, Utils.colorize(message, color), isPrivate); } @@ -1515,7 +1518,7 @@ public class Mobibot extends PircBot { * * @param feedUrl The feed URL. */ - private void setFeedUrl(final String feedUrl) { + final void setFeedUrl(final String feedUrl) { this.feedUrl = feedUrl; } @@ -1526,7 +1529,7 @@ public class Mobibot extends PircBot { * @param nick The ident nick name. * @param msg The ident message. */ - private void setIdentity(final String pwd, final String nick, final String msg) { + final void setIdentity(final String pwd, final String nick, final String msg) { identPwd = pwd; identNick = nick; identMsg = msg; @@ -1537,7 +1540,7 @@ public class Mobibot extends PircBot { * * @param nicks The nicks to ignore */ - private void setIgnoredNicks(final String nicks) { + final void setIgnoredNicks(final String nicks) { if (Utils.isValidString(nicks)) { final StringTokenizer st = new StringTokenizer(nicks, ","); @@ -1552,7 +1555,7 @@ public class Mobibot extends PircBot { * * @param apiToken The API token */ - private void setPinboardAuth(final String apiToken) { + final void setPinboardAuth(final String apiToken) { if (Utils.isValidString(apiToken)) { pinboard = new Pinboard(this, apiToken, ircServer); } @@ -1563,7 +1566,7 @@ public class Mobibot extends PircBot { * * @param tags The tags. */ - private void setTags(final String tags) { + final void setTags(final String tags) { defaultTags = tags; } From 59626b1d946119d4145ee8f1c6ecc2603dc5124c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Apr 2019 06:24:53 -0700 Subject: [PATCH 262/842] Spotbugs JDK 8 cleanups. --- config/spotbugs/excludeFilter.xml | 6 ++++++ .../java/net/thauvin/erik/mobibot/Mobibot.java | 12 ++++++------ .../thauvin/erik/mobibot/entries/EntriesMgr.java | 14 ++++++++++---- .../thauvin/erik/mobibot/entries/EntriesUtils.java | 2 +- .../erik/mobibot/modules/CurrencyConverter.java | 6 +++--- .../thauvin/erik/mobibot/modules/WorldTime.java | 2 +- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/config/spotbugs/excludeFilter.xml b/config/spotbugs/excludeFilter.xml index e2ac6d0..3d88834 100644 --- a/config/spotbugs/excludeFilter.xml +++ b/config/spotbugs/excludeFilter.xml @@ -27,4 +27,10 @@ </Or> <Bug pattern="FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY"/> </Match> + <Match> + <Class name="net.thauvin.erik.mobibot.Mobibot"/> + <Method name="main"/> + <Bug pattern="PATH_TRAVERSAL_OUT"/> + <Confidence value="1"/> + </Match> </FindBugsFilter> diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c6d0ab9..8ce289e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -241,7 +241,7 @@ public class Mobibot extends PircBot { today = EntriesMgr.loadEntries(logsDir + EntriesMgr.CURRENT_XML, ircChannel, entries); if (logger.isDebugEnabled()) { - logger.debug("Last feed: " + today); + logger.debug("Last feed: {}", today); } if (!Utils.today().equals(today)) { @@ -831,7 +831,7 @@ public class Mobibot extends PircBot { } } - send(sender, "The following nicks are ignored: " + ignoredNicks.toString()); + send(sender, "The following nicks are ignored: " + ignoredNicks); } } @@ -971,7 +971,7 @@ public class Mobibot extends PircBot { final String hostname, final String message) { if (logger.isDebugEnabled()) { - logger.debug(">>> " + sender + ": " + message); + logger.debug(">>> {} : {}", sender, message); } boolean isCommand = false; @@ -1276,7 +1276,7 @@ public class Mobibot extends PircBot { final String hostname, final String message) { if (logger.isDebugEnabled()) { - logger.debug(">>> " + sender + ": " + message); + logger.debug(">>> {} : {}", sender, message); } final String[] cmds = message.split(" ", 2); @@ -1443,13 +1443,13 @@ public class Mobibot extends PircBot { if (Utils.isValidString(message) && Utils.isValidString(sender)) { if (isPrivate) { if (logger.isDebugEnabled()) { - logger.debug("Sending message to " + sender + ": " + message); + logger.debug("Sending message to {} : {}", sender, message); } sendMessage(sender, message); } else { if (logger.isDebugEnabled()) { - logger.debug("Sending notice to " + sender + ": " + message); + logger.debug("Sending notice to {} : {}", sender, message); } sendNotice(sender, message); diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index 3a3920f..453e56a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -189,7 +189,7 @@ public final class EntriesMgr { * @param history The history array. * @param isDayBackup Set the true if the daily backup file should also be created. */ - @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.") + @SuppressFBWarnings(value = {"CE_CLASS_ENVY", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, it does.") public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, @@ -221,9 +221,15 @@ public final class EntriesMgr { for (int i = (entries.size() - 1); i >= 0; --i) { entry = entries.get(i); - buff = new StringBuilder( - "Posted by <b>" + entry.getNick() + "</b> on <a href=\"irc://" + bot.getIrcServer() + '/' - + entry.getChannel() + "\"><b>" + entry.getChannel() + "</b></a>"); + buff = new StringBuilder() + .append("Posted by <b>") + .append(entry.getNick()) + .append("</b> on <a href=\"irc://") + .append(bot.getIrcServer()).append('/') + .append(entry.getChannel()) + .append("\"><b>") + .append(entry.getChannel()) + .append("</b></a>"); if (entry.getCommentsCount() > 0) { buff.append(" <br/><br/>"); diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java index 27526d3..8e7f9ba 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -87,7 +87,7 @@ public final class EntriesUtils { */ @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.") public static String buildLink(final int index, final EntryLink entry, final boolean isView) { - final StringBuilder buff = new StringBuilder(Commands.LINK_CMD + (index + 1) + ": "); + final StringBuilder buff = new StringBuilder().append(Commands.LINK_CMD).append(index + 1).append(": "); buff.append('[').append(entry.getNick()).append(']'); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index c23ddd1..d1a3900 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -150,12 +150,12 @@ public final class CurrencyConverter extends ThreadedModule { + cmds[3].toUpperCase()); } catch (Exception e) { throw new ModuleException("convertCurrency(" + query + ')', - "The supported currencies are: " + EXCHANGE_RATES.keySet().toString(), e); + "The supported currencies are: " + EXCHANGE_RATES.keySet(), e); } } } else if (CURRENCY_RATES_KEYWORD.equals(query)) { - final StringBuilder buff = new StringBuilder('[' + pubDate + "]: "); + final StringBuilder buff = new StringBuilder().append('[').append(pubDate).append("]: "); int i = 0; for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { @@ -169,7 +169,7 @@ public final class CurrencyConverter extends ThreadedModule { return new NoticeMessage(buff.toString()); } } - return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet().toString()); + return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet()); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index e9c0729..506ffd5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -194,7 +194,7 @@ public final class WorldTime extends AbstractModule { + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); } } else { - return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet().toString()); + return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet()); } return new PublicMessage(response); From c3d4eb1ab814709d6bca0d402aa7c1a0d510d92b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:27:08 -0700 Subject: [PATCH 263/842] Added PMD ruleset. --- config/pmd.xml | 253 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 config/pmd.xml diff --git a/config/pmd.xml b/config/pmd.xml new file mode 100644 index 0000000..547f745 --- /dev/null +++ b/config/pmd.xml @@ -0,0 +1,253 @@ +<?xml version="1.0"?> +<ruleset name="erik" + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> + <description>Erik's Ruleset</description> + <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/> + <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/> + <rule ref="category/java/bestpractices.xml/CheckResultSet"/> + <rule ref="category/java/bestpractices.xml/ConstantsInInterface"/> + <rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitchStmt"/> + <rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/> + <rule ref="category/java/bestpractices.xml/GuardLogStatement"/> + <rule ref="category/java/bestpractices.xml/LooseCoupling"/> + <rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray"/> + <rule ref="category/java/bestpractices.xml/MissingOverride"/> + <rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInCaseInsensitiveComparisons"/> + <rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInComparisons"/> + <rule ref="category/java/bestpractices.xml/PreserveStackTrace"/> + <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/> + <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> + <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/> + <rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/> + <rule ref="category/java/bestpractices.xml/SystemPrintln"/> + <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/> + <rule ref="category/java/bestpractices.xml/UnusedImports"/> + <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/> + <rule ref="category/java/bestpractices.xml/UnusedPrivateField"/> + <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/> + <rule ref="category/java/bestpractices.xml/UseAssertEqualsInsteadOfAssertTrue"/> + <rule ref="category/java/bestpractices.xml/UseAssertNullInsteadOfAssertTrue"/> + <rule ref="category/java/bestpractices.xml/UseAssertSameInsteadOfAssertTrue"/> + <rule ref="category/java/bestpractices.xml/UseAssertTrueInsteadOfAssertEquals"/> + <rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"/> + <rule ref="category/java/bestpractices.xml/UseVarargs"/> + + + <!-- NAMING CONVENTIONS --> + <rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"/> + <rule ref="category/java/codestyle.xml/GenericsNaming"/> + <rule ref="category/java/codestyle.xml/LocalVariableNamingConventions"/> + <rule ref="category/java/codestyle.xml/MethodNamingConventions"/> + <rule ref="category/java/codestyle.xml/PackageCase"/> + + + <!-- OTHER --> + <rule ref="category/java/codestyle.xml/AvoidDollarSigns"/> + <rule ref="category/java/codestyle.xml/AvoidProtectedFieldInFinalClass"/> + <rule ref="category/java/codestyle.xml/AvoidProtectedMethodInFinalClassNotExtending"/> + <rule ref="category/java/codestyle.xml/AvoidUsingNativeCode"/> + <rule ref="category/java/codestyle.xml/BooleanGetMethodName"/> + <rule ref="category/java/codestyle.xml/CallSuperInConstructor"/> + <rule ref="category/java/codestyle.xml/ControlStatementBraces"/> + <rule ref="category/java/codestyle.xml/DontImportJavaLang"/> + <rule ref="category/java/codestyle.xml/DuplicateImports"/> + <rule ref="category/java/codestyle.xml/EmptyMethodInAbstractClassShouldBeAbstract"/> + <rule ref="category/java/codestyle.xml/ExtendsObject"/> + <rule ref="category/java/codestyle.xml/FieldDeclarationsShouldBeAtStartOfClass"/> + <rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"/> + <rule ref="category/java/codestyle.xml/IdenticalCatchBranches"/> + <rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal"/> + <rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal"/> + <rule ref="category/java/codestyle.xml/NoPackage"/> + <rule ref="category/java/codestyle.xml/PrematureDeclaration"/> + <rule ref="category/java/codestyle.xml/TooManyStaticImports"/> + <rule ref="category/java/codestyle.xml/UnnecessaryAnnotationValueElement"/> + <rule ref="category/java/codestyle.xml/UnnecessaryConstructor"/> + <rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName"/> + <rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/> + <rule ref="category/java/codestyle.xml/UnnecessaryReturn"/> + <rule ref="category/java/codestyle.xml/UselessQualifiedThis"/> + + + <!-- DESIGN --> + <rule ref="category/java/design.xml/AbstractClassWithoutAnyMethod"/> + <rule ref="category/java/design.xml/AvoidRethrowingException"/> + <rule ref="category/java/design.xml/AvoidThrowingNewInstanceOfSameException"/> + <rule ref="category/java/design.xml/AvoidThrowingNullPointerException"/> + <rule ref="category/java/design.xml/AvoidThrowingRawExceptionTypes"/> + <rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"/> + <rule ref="category/java/design.xml/CollapsibleIfStatements"/> + <rule ref="category/java/design.xml/CouplingBetweenObjects"/> + <rule ref="category/java/design.xml/DataClass"/> + <rule ref="category/java/design.xml/DoNotExtendJavaLangError"/> + <rule ref="category/java/design.xml/ExceptionAsFlowControl"/> + <rule ref="category/java/design.xml/ExcessivePublicCount"/> + <rule ref="category/java/design.xml/FinalFieldCouldBeStatic"/> + <rule ref="category/java/design.xml/ImmutableField"/> + <rule ref="category/java/design.xml/LogicInversion"/> + <rule ref="category/java/design.xml/SignatureDeclareThrowsException"/> + <rule ref="category/java/design.xml/SimplifiedTernary"/> + <rule ref="category/java/design.xml/SimplifyBooleanAssertion"/> + <rule ref="category/java/design.xml/SimplifyBooleanExpressions"/> + <rule ref="category/java/design.xml/SimplifyBooleanReturns"/> + <rule ref="category/java/design.xml/SimplifyConditional"/> + <rule ref="category/java/design.xml/SingularField"/> + <rule ref="category/java/design.xml/SwitchDensity"/> + <rule ref="category/java/design.xml/UselessOverridingMethod"/> + <rule ref="category/java/design.xml/UseUtilityClass"/> + + <!-- DOCUMENTATION --> + <rule ref="category/java/documentation.xml/UncommentedEmptyConstructor"/> + <rule ref="category/java/documentation.xml/UncommentedEmptyMethodBody"/> + + + <!-- ERRORPRONE --> + <rule ref="category/java/errorprone.xml/AssignmentInOperand"> + <properties> + <property name="allowWhile" value="true"/> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"/> + <rule ref="category/java/errorprone.xml/AvoidAccessibilityAlteration"/> + <rule ref="category/java/errorprone.xml/AvoidAssertAsIdentifier"/> + <rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"/> + <rule ref="category/java/errorprone.xml/AvoidCallingFinalize"/> + <rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"/> + <rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/> + <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals"/> + <rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier"/> + <rule ref="category/java/errorprone.xml/AvoidInstanceofChecksInCatchClause"/> + <rule ref="category/java/errorprone.xml/AvoidLosingExceptionInformation"/> + <rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators"/> + <rule ref="category/java/errorprone.xml/AvoidUsingOctalValues"/> + <rule ref="category/java/errorprone.xml/BadComparison"/> + <rule ref="category/java/errorprone.xml/BrokenNullCheck"/> + <rule ref="category/java/errorprone.xml/CallSuperFirst"/> + <rule ref="category/java/errorprone.xml/CallSuperLast"/> + <rule ref="category/java/errorprone.xml/CheckSkipResult"/> + <rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray"/> + <rule ref="category/java/errorprone.xml/CloneMethodMustBePublic"/> + <rule ref="category/java/errorprone.xml/CloneMethodMustImplementCloneable"/> + <rule ref="category/java/errorprone.xml/CloneMethodReturnTypeMustMatchClassName"/> + <rule ref="category/java/errorprone.xml/CloneThrowsCloneNotSupportedException"/> + <rule ref="category/java/errorprone.xml/CloseResource"/> + <rule ref="category/java/errorprone.xml/CompareObjectsWithEquals"/> + <rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod"/>> + <rule ref="category/java/errorprone.xml/DoNotCallGarbageCollectionExplicitly"/> + <rule ref="category/java/errorprone.xml/DoNotExtendJavaLangThrowable"/> + <rule ref="category/java/errorprone.xml/DoNotHardCodeSDCard"/> + <rule ref="category/java/errorprone.xml/DoNotThrowExceptionInFinally"/> + <rule ref="category/java/errorprone.xml/DontImportSun"/> + <rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices"/> + <rule ref="category/java/errorprone.xml/EqualsNull"/> + <rule ref="category/java/errorprone.xml/FinalizeDoesNotCallSuperFinalize"/> + <rule ref="category/java/errorprone.xml/FinalizeOnlyCallsSuperFinalize"/> + <rule ref="category/java/errorprone.xml/FinalizeOverloaded"/> + <rule ref="category/java/errorprone.xml/FinalizeShouldBeProtected"/> + <rule ref="category/java/errorprone.xml/IdempotentOperations"/> + <rule ref="category/java/errorprone.xml/ImportFromSamePackage"/> + <rule ref="category/java/errorprone.xml/InstantiationToGetClass"/> + <rule ref="category/java/errorprone.xml/InvalidSlf4jMessageFormat"/> + <rule ref="category/java/errorprone.xml/JumbledIncrementer"/> + <rule ref="category/java/errorprone.xml/JUnitSpelling"/> + <rule ref="category/java/errorprone.xml/JUnitStaticSuite"/> + <rule ref="category/java/errorprone.xml/LoggerIsNotStaticFinal"/> + <rule ref="category/java/errorprone.xml/MethodWithSameNameAsEnclosingClass"/> + <rule ref="category/java/errorprone.xml/MisplacedNullCheck"/> + <rule ref="category/java/errorprone.xml/MissingBreakInSwitch"/> + <rule ref="category/java/errorprone.xml/MissingSerialVersionUID"/> + <rule ref="category/java/errorprone.xml/MissingStaticMethodInNonInstantiatableClass"/> + <rule ref="category/java/errorprone.xml/MoreThanOneLogger"/> + <rule ref="category/java/errorprone.xml/NonCaseLabelInSwitchStatement"/> + <rule ref="category/java/errorprone.xml/NonStaticInitializer"/> + <rule ref="category/java/errorprone.xml/NullAssignment"/> + <rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"/> + <rule ref="category/java/errorprone.xml/ProperCloneImplementation"/> + <rule ref="category/java/errorprone.xml/ProperLogger"/> + <rule ref="category/java/errorprone.xml/ReturnEmptyArrayRatherThanNull"/> + <rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock"/> + <rule ref="category/java/errorprone.xml/SimpleDateFormatNeedsLocale"/> + <rule ref="category/java/errorprone.xml/SingleMethodSingleton"/> + <rule ref="category/java/errorprone.xml/SingletonClassReturningNewInstance"/> + <rule ref="category/java/errorprone.xml/StaticEJBFieldShouldBeFinal"/> + <rule ref="category/java/errorprone.xml/StringBufferInstantiationWithChar"/> + <rule ref="category/java/errorprone.xml/SuspiciousEqualsMethodName"/> + <rule ref="category/java/errorprone.xml/SuspiciousHashcodeMethodName"/> + <rule ref="category/java/errorprone.xml/SuspiciousOctalEscape"/> + <rule ref="category/java/errorprone.xml/TestClassWithoutTestCases"/> + <rule ref="category/java/errorprone.xml/UnconditionalIfStatement"/> + <rule ref="category/java/errorprone.xml/UnnecessaryBooleanAssertion"/> + <rule ref="category/java/errorprone.xml/UnnecessaryCaseChange"/> + <rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary"/> + <rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals"/> + <rule ref="category/java/errorprone.xml/UseCorrectExceptionLogging"/> + <rule ref="category/java/errorprone.xml/UseEqualsToCompareStrings"/> + <rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"/> + <rule ref="category/java/errorprone.xml/UseLocaleWithCaseConversions"/> + <rule ref="category/java/errorprone.xml/EmptyCatchBlock"> + <properties> + <property name="allowExceptionNameRegex"> + <value>^ignore$</value> + </property> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/EmptyFinalizer"/> + <rule ref="category/java/errorprone.xml/EmptyFinallyBlock"/> + <rule ref="category/java/errorprone.xml/EmptyIfStmt"/> + <rule ref="category/java/errorprone.xml/EmptyInitializer"/> + <rule ref="category/java/errorprone.xml/EmptyStatementBlock"/> + <rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop"/> + <rule ref="category/java/errorprone.xml/EmptySwitchStatements"/> + <rule ref="category/java/errorprone.xml/EmptySynchronizedBlock"/> + <rule ref="category/java/errorprone.xml/EmptyTryBlock"/> + <rule ref="category/java/errorprone.xml/EmptyWhileStmt"/> + + + <!-- MULTITHREADING --> + <rule ref="category/java/multithreading.xml/AvoidSynchronizedAtMethodLevel"/> + <rule ref="category/java/multithreading.xml/AvoidThreadGroup"/> + <rule ref="category/java/multithreading.xml/AvoidUsingVolatile"/> + <rule ref="category/java/multithreading.xml/DontCallThreadRun"/> + <rule ref="category/java/multithreading.xml/DoubleCheckedLocking"/> + <rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"/> + <rule ref="category/java/multithreading.xml/UseConcurrentHashMap"/> + <rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"/> + + <!-- PERFORMANCE --> + <rule ref="category/java/performance.xml/AddEmptyString"/> + <rule ref="category/java/performance.xml/AppendCharacterWithChar"/> + <rule ref="category/java/performance.xml/AvoidArrayLoops"/> + <rule ref="category/java/performance.xml/AvoidFileStream"/> + <rule ref="category/java/performance.xml/AvoidInstantiatingObjectsInLoops"/> + <rule ref="category/java/performance.xml/AvoidUsingShortType"/> + <rule ref="category/java/performance.xml/BigIntegerInstantiation"/> + <rule ref="category/java/performance.xml/BooleanInstantiation"/> + <rule ref="category/java/performance.xml/ByteInstantiation"/> + <rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse"/> + <rule ref="category/java/performance.xml/ConsecutiveLiteralAppends"/> + <rule ref="category/java/performance.xml/InefficientEmptyStringCheck"/> + <rule ref="category/java/performance.xml/InefficientStringBuffering"/> + <rule ref="category/java/performance.xml/InsufficientStringBufferDeclaration"/> + <rule ref="category/java/performance.xml/IntegerInstantiation"/> + <rule ref="category/java/performance.xml/LongInstantiation"/> + <rule ref="category/java/performance.xml/OptimizableToArrayCall"/> + <rule ref="category/java/performance.xml/RedundantFieldInitializer"/> + <rule ref="category/java/performance.xml/SimplifyStartsWith"/> + <rule ref="category/java/performance.xml/ShortInstantiation"/> + <rule ref="category/java/performance.xml/StringInstantiation"/> + <rule ref="category/java/performance.xml/StringToString"/> + <rule ref="category/java/performance.xml/TooFewBranchesForASwitchStatement"/> + <rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation"/> + <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector"/> + <rule ref="category/java/performance.xml/UseArraysAsList"/> + <rule ref="category/java/performance.xml/UseIndexOfChar"/> + <rule ref="category/java/performance.xml/UselessStringValueOf"/> + <rule ref="category/java/performance.xml/UseStringBufferForStringAppends"/> + <rule ref="category/java/performance.xml/UseStringBufferLength"/> + + <!-- SECURITY --> + <rule ref="category/java/security.xml/HardCodedCryptoKey"/> + <rule ref="category/java/security.xml/InsecureCryptoIv"/> +</ruleset> From bb23d7e79d1eb6bc9b080b7f660f9fe97ae25f6b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:37:21 -0700 Subject: [PATCH 264/842] Using List instead of ArrayList (PMD) --- .../java/net/thauvin/erik/mobibot/modules/StockQuote.java | 6 ++++-- .../java/net/thauvin/erik/mobibot/modules/Weather2.java | 5 +++-- .../net/thauvin/erik/mobibot/modules/GoogleSearchTest.java | 5 +++-- .../net/thauvin/erik/mobibot/modules/StockQuoteTest.java | 5 +++-- .../java/net/thauvin/erik/mobibot/modules/Weather2Test.java | 4 ++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 23f0f80..9e09bef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -46,6 +46,7 @@ import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; +import java.util.List; /** * The StockQuote module. @@ -80,7 +81,7 @@ public final class StockQuote extends ThreadedModule { * @return The {@link Message} array containing the stock quote. * @throws ModuleException If an errors occurs. */ - static ArrayList<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { + static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { if (!Utils.isValidString(apiKey)) { throw new ModuleException(Utils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); } @@ -166,10 +167,11 @@ public final class StockQuote extends ThreadedModule { /** * Returns the specified stock quote from Alpha Advantage. */ + @Override void run(final Mobibot bot, final String sender, final String symbol) { if (Utils.isValidString(symbol)) { try { - final ArrayList<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); + final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); for (final Message msg : messages) { bot.send(sender, msg); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index a9dae96..92804eb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -102,7 +102,7 @@ public class Weather2 extends ThreadedModule { * @return The {@link Message} array containing the weather data. * @throws ModuleException If an error occurs while retrieving the weather data. */ - static ArrayList<Message> getWeather(final String query, final String apiKey) throws ModuleException { + static List<Message> getWeather(final String query, final String apiKey) throws ModuleException { if (!Utils.isValidString(apiKey)) { throw new ModuleException(Utils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing."); } @@ -204,10 +204,11 @@ public class Weather2 extends ThreadedModule { /** * Fetches the weather data from a specific city. */ + @Override void run(final Mobibot bot, final String sender, final String args) { if (Utils.isValidString(args)) { try { - final ArrayList<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); + final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); if (messages.get(0).isError()) { helpResponse(bot, sender, args, true); } else { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index c81b883..88e005c 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -36,7 +36,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; -import java.util.ArrayList; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -54,12 +54,13 @@ public class GoogleSearchTest extends LocalProperties { } @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE") + @SuppressWarnings("PMD.PreserveStackTrace") @Test public void testSearchGoogle() throws ModuleException { final String apiKey = LocalProperties.getProperty(GoogleSearch.GOOGLE_API_KEY_PROP); final String cseKey = LocalProperties.getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP); try { - ArrayList<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey); + List<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey); assertThat(messages).as("mobibot results not empty").isNotEmpty(); assertThat(messages.get(0).getMessage()).as("found mobitopia").contains("mobibot"); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index b4fff14..488de37 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -36,7 +36,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; -import java.util.ArrayList; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -50,11 +50,12 @@ import static org.assertj.core.api.Assertions.assertThat; public class StockQuoteTest extends LocalProperties { @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE") + @SuppressWarnings("PMD.PreserveStackTrace") @Test public void testGetQuote() throws ModuleException { final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); try { - ArrayList<Message> messages = StockQuote.getQuote("AAPL", apiKey); + List<Message> messages = StockQuote.getQuote("AAPL", apiKey); assertThat(messages).as("response not empty").isNotEmpty(); assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL"); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index 2ac1b0a..6f549dc 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -36,7 +36,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; -import java.util.ArrayList; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -51,7 +51,7 @@ public class Weather2Test extends LocalProperties { @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") @Test public void testWeather() throws ModuleException { - ArrayList<Message> messages = Weather2.getWeather("98204", + List<Message> messages = Weather2.getWeather("98204", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett"); messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); From be0c1c4cd4ce1818085b9178b3b7a4050af5945a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:37:45 -0700 Subject: [PATCH 265/842] Repeat string to variables (PMD) --- .../erik/mobibot/modules/ModuleExceptionTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java index c058317..a2faff9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java @@ -48,26 +48,29 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class ModuleExceptionTest { + static final String debugMessage = "debugMessage"; + static final String message = "message"; + @DataProvider(name = "dp") Object[][] createData(final Method m) { - return new Object[][]{new Object[]{new ModuleException("debugMessage", "message", + return new Object[][]{new Object[]{new ModuleException(debugMessage, message, new IOException("Secret URL http://foo.com?apiKey=sec&userID=me"))}, - new Object[]{new ModuleException("debugMessage", "message", + new Object[]{new ModuleException(debugMessage, message, new IOException("URL http://foobar.com"))}, - new Object[]{new ModuleException("debugMessage", "message", + new Object[]{new ModuleException(debugMessage, message, new IOException("URL http://foobar.com?"))}, - new Object[]{new ModuleException("debugMessage", "message")} + new Object[]{new ModuleException(debugMessage, message)} }; } @Test(dataProvider = "dp") final void testGetDebugMessage(final ModuleException e) { - assertThat(e.getDebugMessage()).as("get debug message").isEqualTo("debugMessage"); + assertThat(e.getDebugMessage()).as("get debug message").isEqualTo(debugMessage); } @Test(dataProvider = "dp") final void testGetMessage(final ModuleException e) { - assertThat(e.getMessage()).as("get message").isEqualTo("message"); + assertThat(e.getMessage()).as("get message").isEqualTo(message); } @Test(dataProvider = "dp") From bd9bb8d47e33207efc4c1bff742ee8a3f179e71b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:43:16 -0700 Subject: [PATCH 266/842] Suppress Warnings (PMD) --- src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java | 1 + src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java | 3 ++- src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java | 3 ++- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.java | 1 + src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java | 1 + src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java | 1 + 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index f69fb8d..1fc1b34 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -27,6 +27,7 @@ import java.io.InputStreamReader; * @created Sep 13, 2010 * @since 1.0 */ +@SuppressWarnings("PMD.UseUtilityClass") public final class TwitterOAuth { /** * Twitter OAuth Client Registration. diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index 453e56a..fc8b401 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -130,7 +130,7 @@ public final class EntriesMgr { * @throws IOException If the file was not found or could not be read. * @throws FeedException If an error occurred while reading the feed. */ - @SuppressWarnings("unchecked") + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) throws IOException, FeedException { entries.clear(); @@ -190,6 +190,7 @@ public final class EntriesMgr { * @param isDayBackup Set the true if the daily backup file should also be created. */ @SuppressFBWarnings(value = {"CE_CLASS_ENVY", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, it does.") + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 472367f..dec9fd7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -292,7 +292,8 @@ public class EntryLink implements Serializable { * * @param tags The space-delimited tags. */ - @SuppressFBWarnings(value = "STT_STRING_PARSING_A_FIELD") + @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public final void setTags(final String tags) { if (tags != null) { final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index d1a3900..7e4a024 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -60,6 +60,7 @@ import java.util.TreeMap; * @created Feb 11, 2004 * @since 1.0 */ +@SuppressWarnings("PMD.UseConcurrentHashMap") public final class CurrencyConverter extends ThreadedModule { /** * The rates keyword. diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java index 1c2cd4d..cc73893 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java @@ -44,6 +44,7 @@ import java.time.format.DateTimeFormatter; * @created 2014-04-24 * @since 1.0 */ +@SuppressWarnings("PMD.DataClass") public class TellMessage implements Serializable { private static final long serialVersionUID = 2L; private final String id; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index 95288a5..d0d55f5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2017-05-30 * @since 1.0 */ +@SuppressWarnings("PMD.AvoidUsingHardCodedIP") public class LookupTest { @Test public void testLookupImpl() { From 1d7f8eb86b8c9d3707858e87112672df241cccf5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:50:47 -0700 Subject: [PATCH 267/842] Added default locale (PMD) --- .../net/thauvin/erik/mobibot/Constants.java | 7 ++ .../net/thauvin/erik/mobibot/Mobibot.java | 6 +- .../java/net/thauvin/erik/mobibot/Utils.java | 2 +- .../erik/mobibot/entries/EntryLink.java | 6 +- .../mobibot/modules/CurrencyConverter.java | 86 ++++++++++--------- .../erik/mobibot/modules/WorldTime.java | 1 + .../erik/mobibot/modules/LocalProperties.java | 3 +- 7 files changed, 63 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index c5b4509..4160517 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -32,6 +32,8 @@ package net.thauvin.erik.mobibot; +import java.util.Locale; + /** * The <code>Constants</code> class. * @@ -55,6 +57,11 @@ public final class Constants { */ public static final String TWITTER_HANDLE_PROP = "twitter-handle"; + /** + * Default locale. + */ + public static final Locale LOCALE = Locale.getDefault(); + /** * Disables the default constructor. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 8ce289e..c20dd45 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -585,8 +585,8 @@ public class Mobibot extends PircBot { for (final char c : getNick().toCharArray()) { if (Character.isLetter(c)) { buff.append('[') - .append(String.valueOf(c).toLowerCase()) - .append(String.valueOf(c).toUpperCase()) + .append(String.valueOf(c).toLowerCase(Constants.LOCALE)) + .append(String.valueOf(c).toUpperCase(Constants.LOCALE)) .append(']'); } else { buff.append(c); @@ -651,7 +651,7 @@ public class Mobibot extends PircBot { * @param topic The help topic, if any. */ private void helpResponse(final String sender, final String topic) { - final String lcTopic = topic.toLowerCase().trim(); + final String lcTopic = topic.toLowerCase(Constants.LOCALE).trim(); if (Commands.HELP_POSTING_KEYWORD.equals(lcTopic)) { send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 0b10b26..9a55ace 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -84,7 +84,7 @@ public final class Utils { * @return The capitalized string. */ public static String capitalize(final String s) { - return s.substring(0, 1).toUpperCase() + s.substring(1); + return s.substring(0, 1).toUpperCase(Constants.LOCALE) + s.substring(1); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index dec9fd7..f96871b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -35,6 +35,7 @@ package net.thauvin.erik.mobibot.entries; import com.rometools.rome.feed.synd.SyndCategory; import com.rometools.rome.feed.synd.SyndCategoryImpl; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.Constants; import java.io.Serializable; import java.util.Calendar; @@ -307,7 +308,7 @@ public class EntryLink implements Serializable { if (part.length() >= 2) { tag = new SyndCategoryImpl(); - tag.setName(part.substring(1).toLowerCase()); + tag.setName(part.substring(1).toLowerCase(Constants.LOCALE)); mod = part.charAt(0); @@ -321,7 +322,7 @@ public class EntryLink implements Serializable { this.tags.add(tag); } } else { - tag.setName(part.trim().toLowerCase()); + tag.setName(part.trim().toLowerCase(Constants.LOCALE)); if (!this.tags.contains(tag)) { this.tags.add(tag); @@ -395,6 +396,7 @@ public class EntryLink implements Serializable { * * @return A string representation of the object. */ + @Override public final String toString() { return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 7e4a024..a50c464 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; @@ -80,9 +81,48 @@ public final class CurrencyConverter extends ThreadedModule { * Creates a new {@link CurrencyConverter} instance. */ public CurrencyConverter() { + super(); commands.add(CURRENCY_CMD); } + /** + * {@inheritDoc} + */ + @Override + public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + synchronized (this) { + if (!pubDate.equals(Utils.today())) { + EXCHANGE_RATES.clear(); + } + } + + super.commandResponse(bot, sender, args, isPrivate); + } + + /** + * Converts the specified currencies. + */ + @SuppressFBWarnings("REDOS") + @Override + void run(final Mobibot bot, final String sender, final String query) { + if (Utils.isValidString(sender) && Utils.isValidString(query)) { + if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { + try { + final Message msg = convertCurrency(query); + if (msg.isError()) { + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false); + } + bot.send(sender, msg); + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(sender, e.getMessage()); + } + } else { + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); + } + } + } + /** * Converts from a currency to another. * @@ -136,19 +176,20 @@ public final class CurrencyConverter extends ThreadedModule { } else { try { final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); - final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase())); - final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase())); + final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1] + .toUpperCase(Constants.LOCALE))); + final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase(Constants.LOCALE))); return new PublicMessage( NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' - + cmds[1].toUpperCase() + + cmds[1].toUpperCase(Constants.LOCALE) + " = " + NumberFormat.getCurrencyInstance(Locale.US) .format((amt * to) / from) .substring(1) + ' ' - + cmds[3].toUpperCase()); + + cmds[3].toUpperCase(Constants.LOCALE)); } catch (Exception e) { throw new ModuleException("convertCurrency(" + query + ')', "The supported currencies are: " + EXCHANGE_RATES.keySet(), e); @@ -173,43 +214,6 @@ public final class CurrencyConverter extends ThreadedModule { return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet()); } - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - synchronized (this) { - if (!pubDate.equals(Utils.today())) { - EXCHANGE_RATES.clear(); - } - } - - super.commandResponse(bot, sender, args, isPrivate); - } - - /** - * Converts the specified currencies. - */ - @SuppressFBWarnings(value = "REDOS") - void run(final Mobibot bot, final String sender, final String query) { - if (Utils.isValidString(sender) && Utils.isValidString(query)) { - if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { - try { - final Message msg = convertCurrency(query); - if (msg.isError()) { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false); - } - bot.send(sender, msg); - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); - } - } else { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); - } - } - } - /** * {@inheritDoc} */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 506ffd5..ab267f9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java index b2d1686..0cd1082 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Constants; import org.testng.annotations.BeforeSuite; import java.io.IOException; @@ -64,7 +65,7 @@ class LocalProperties { } private static String keyToEnv(final String key) { - return key.replaceAll("-", "_").toUpperCase(); + return key.replaceAll("-", "_").toUpperCase(Constants.LOCALE); } @BeforeSuite(alwaysRun = true) From bc4399f7985e9b64709a745c9bceed3214183b2e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:51:06 -0700 Subject: [PATCH 268/842] Added private constructor (PMD) --- .../net/thauvin/erik/mobibot/modules/AbstractModuleTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java index b28d3f4..fb87f9a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -45,6 +45,10 @@ import static org.assertj.core.api.Assertions.assertThat; */ final class AbstractModuleTest { + private AbstractModuleTest() { + throw new UnsupportedOperationException("Illegal constructor call."); + } + @SuppressFBWarnings("CE_CLASS_ENVY") static void testAbstractModule(final AbstractModule module) { final String name = module.getClass().getName(); From 1ba412d1f0ebfa8ef141808b8f5b56190b5dcbba Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:53:30 -0700 Subject: [PATCH 269/842] Added call to super() in constructor (PMD) --- .../thauvin/erik/mobibot/modules/Calc.java | 1 + .../thauvin/erik/mobibot/modules/Dice.java | 1 + .../erik/mobibot/modules/GoogleSearch.java | 74 +++++++-------- .../thauvin/erik/mobibot/modules/Joke.java | 2 + .../thauvin/erik/mobibot/modules/Lookup.java | 1 + .../thauvin/erik/mobibot/modules/Ping.java | 10 +-- .../erik/mobibot/modules/StockQuote.java | 1 + .../thauvin/erik/mobibot/modules/Twitter.java | 1 + .../net/thauvin/erik/mobibot/modules/War.java | 1 + .../erik/mobibot/modules/Weather2.java | 11 +-- .../erik/mobibot/modules/WorldTime.java | 90 ++++++++++--------- .../erik/mobibot/msg/ErrorMessage.java | 2 + .../erik/mobibot/msg/NoticeMessage.java | 2 + .../erik/mobibot/msg/PrivateMessage.java | 8 ++ .../erik/mobibot/msg/PublicMessage.java | 8 ++ 15 files changed, 125 insertions(+), 88 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 931fc65..b68cc4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -54,6 +54,7 @@ public class Calc extends AbstractModule { * The default constructor. */ public Calc() { + super(); commands.add(CALC_CMD); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 8c45f48..1e69b10 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -52,6 +52,7 @@ public final class Dice extends AbstractModule { * The default constructor. */ public Dice() { + super(); commands.add(DICE_CMD); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 866def3..ed26c4a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -49,6 +49,7 @@ import java.net.URLConnection; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.List; /** * The GoogleSearch module. @@ -71,11 +72,46 @@ public final class GoogleSearch extends ThreadedModule { * Creates a new {@link GoogleSearch} instance. */ public GoogleSearch() { + super(); commands.add(GOOGLE_CMD); properties.put(GOOGLE_API_KEY_PROP, ""); properties.put(GOOGLE_CSE_KEY_PROP, ""); } + /** + * {@inheritDoc} + */ + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + if (isEnabled()) { + bot.send(sender, "To search Google:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); + } else { + bot.send(sender, "The Google search module is disabled."); + } + } + + /** + * Searches Google. + */ + @Override + void run(final Mobibot bot, final String sender, final String query) { + if (Utils.isValidString(query)) { + try { + final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), + properties.get(GOOGLE_CSE_KEY_PROP)); + for (final Message msg : results) { + bot.send(sender, msg); + } + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(sender, e.getMessage()); + } + } else { + helpResponse(bot, sender, query, true); + } + } + /** * Performs a search on Google. * @@ -85,8 +121,9 @@ public final class GoogleSearch extends ThreadedModule { * @return The {@link Message} array containing the search results. * @throws ModuleException If an error occurs while searching. */ - @SuppressFBWarnings(value = {"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) - static ArrayList<Message> searchGoogle(final String query, final String apiKey, final String cseKey) + @SuppressFBWarnings({"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) + @SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops")) + static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey) throws ModuleException { if (!Utils.isValidString(apiKey) || !Utils.isValidString(cseKey)) { throw new ModuleException(Utils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing."); @@ -134,37 +171,4 @@ public final class GoogleSearch extends ThreadedModule { throw new ModuleException("Invalid query."); } } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - if (isEnabled()) { - bot.send(sender, "To search Google:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); - } else { - bot.send(sender, "The Google search module is disabled."); - } - } - - /** - * Searches Google. - */ - void run(final Mobibot bot, final String sender, final String query) { - if (Utils.isValidString(query)) { - try { - final ArrayList<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), - properties.get(GOOGLE_CSE_KEY_PROP)); - for (final Message msg : results) { - bot.send(sender, msg); - } - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); - } - } else { - helpResponse(bot, sender, query, true); - } - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 07cbdf9..cfd5dad 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -62,6 +62,7 @@ public final class Joke extends ThreadedModule { * Creates a new {@link Joke} instance. */ public Joke() { + super(); commands.add(JOKE_CMD); } @@ -107,6 +108,7 @@ public final class Joke extends ThreadedModule { /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. */ + @Override void run(final Mobibot bot, final String sender, final String arg) { try { bot.send(Utils.cyan(randomJoke().getMessage())); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 78ed028..f9df1af 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -60,6 +60,7 @@ public final class Lookup extends AbstractModule { * The default constructor. */ public Lookup() { + super(); commands.add(LOOKUP_CMD); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 03b7945..07254bd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -46,11 +46,6 @@ import java.util.List; * @since 1.0 */ public class Ping extends AbstractModule { - /** - * The ping command. - */ - private static final String PING_CMD = "ping"; - /** * The ping responses. */ @@ -68,11 +63,16 @@ public class Ping extends AbstractModule { "is hibernating.", "is saving energy: apathetic mode activated.", "is busy. Go away!"); + /** + * The ping command. + */ + private static final String PING_CMD = "ping"; /** * The default constructor. */ public Ping() { + super(); commands.add(PING_CMD); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 9e09bef..2053d88 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -70,6 +70,7 @@ public final class StockQuote extends ThreadedModule { * Creates a new {@link StockQuote} instance. */ public StockQuote() { + super(); commands.add(STOCK_CMD); properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index f28fd31..aaf6a1d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -60,6 +60,7 @@ public final class Twitter extends ThreadedModule { * Creates a new {@link Twitter} instance. */ public Twitter() { + super(); commands.add(TWITTER_CMD); properties.put(CONSUMER_SECRET_PROP, ""); properties.put(CONSUMER_KEY_PROP, ""); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index b6c3442..9267c0f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -57,6 +57,7 @@ public final class War extends AbstractModule { * The default constructor. */ public War() { + super(); commands.add(WAR_CMD); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 92804eb..4f2adac 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -69,6 +69,7 @@ public class Weather2 extends ThreadedModule { * Creates a new {@link Weather2} instance. */ public Weather2() { + super(); commands.add(WEATHER_CMD); properties.put(OWM_API_KEY_PROP, ""); } @@ -183,11 +184,6 @@ public class Weather2 extends ThreadedModule { return messages; } - private static String wind(final Double w) { - final double kmh = w * 1.60934; - return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; - } - /** * {@inheritDoc} */ @@ -224,4 +220,9 @@ public class Weather2 extends ThreadedModule { helpResponse(bot, sender, args, true); } } + + private static String wind(final Double w) { + final double kmh = w * 1.60934; + return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index ab267f9..6000a91 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -54,12 +54,14 @@ import java.util.TreeMap; * @created 2014-04-27 * @since 1.0 */ +@SuppressWarnings("PMD.UseConcurrentHashMap") public final class WorldTime extends AbstractModule { // The beats (Internet Time) keyword. private static final String BEATS_KEYWORD = ".beats"; // The supported countries. private static final Map<String, String> COUNTRIES_MAP; + /** * The time command. */ @@ -155,52 +157,10 @@ public final class WorldTime extends AbstractModule { * Creates a new {@link WorldTime} instance. */ public WorldTime() { + super(); commands.add(TIME_CMD); } - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - private static String internetTime() { - final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); - final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) - + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); - return String.format("%c%03d", '@', beats); - } - - /** - * Returns the world time. - * - * <ul> - * <li>PST</li> - * <li>BEATS</li> - * </ul> - * - * @param query The query. - * @return The {@link Message} containing the world time. - */ - @SuppressFBWarnings(value = "STT_STRING_PARSING_A_FIELD") - static Message worldTime(final String query) { - final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim().toUpperCase()))); - final String response; - - if (tz != null) { - if (BEATS_KEYWORD.equals(tz)) { - response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); - } else { - response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( - DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) - + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); - } - } else { - return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet()); - } - - return new PublicMessage(response); - } - /** * Responds with the current time in the specified timezone/country. * @@ -243,4 +203,48 @@ public final class WorldTime extends AbstractModule { public boolean isPrivateMsgEnabled() { return true; } + + /** + * Returns the current Internet (beat) Time. + * + * @return The Internet Time string. + */ + private static String internetTime() { + final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); + final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) + + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); + return String.format("%c%03d", '@', beats); + } + + /** + * Returns the world time. + * + * <ul> + * <li>PST</li> + * <li>BEATS</li> + * </ul> + * + * @param query The query. + * @return The {@link Message} containing the world time. + */ + @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") + static Message worldTime(final String query) { + final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim() + .toUpperCase(Constants.LOCALE)))); + final String response; + + if (tz != null) { + if (BEATS_KEYWORD.equals(tz)) { + response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); + } else { + response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( + DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) + + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); + } + } else { + return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet()); + } + + return new PublicMessage(response); + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java index 8e6ff2f..dea79a6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java @@ -46,6 +46,7 @@ public class ErrorMessage extends Message { * @param message The error message. */ public ErrorMessage(final String message) { + super(); this.setMessage(message); this.setError(true); this.setNotice(true); @@ -59,6 +60,7 @@ public class ErrorMessage extends Message { */ @SuppressWarnings("unused") public ErrorMessage(final String message, final String color) { + super(); this.setMessage(message); this.setError(true); this.setNotice(true); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java index 9abcf09..258d947 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java @@ -46,6 +46,7 @@ public class NoticeMessage extends Message { * @param message The notice's message. */ public NoticeMessage(final String message) { + super(); this.setMessage(message); this.setNotice(true); } @@ -57,6 +58,7 @@ public class NoticeMessage extends Message { * @param color The color. */ public NoticeMessage(final String message, final String color) { + super(); this.setMessage(message); this.setNotice(true); this.setColor(color); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java index e1a48cf..1079a4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java @@ -42,10 +42,18 @@ package net.thauvin.erik.mobibot.msg; @SuppressWarnings("unused") public class PrivateMessage extends Message { public PrivateMessage(final String message) { + super(); this.setMessage(message); } + /** + * Creates a new private message. + * + * @param message The message. + * @param color The message color. + */ public PrivateMessage(final String message, final String color) { + super(); this.setMessage(message); this.setColor(color); } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java index 702f0a3..3c4e924 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java @@ -41,11 +41,19 @@ package net.thauvin.erik.mobibot.msg; */ public class PublicMessage extends Message { public PublicMessage(final String message) { + super(); this.setMessage(message); } + /** + * Creates a new public message. + * + * @param message The message. + * @param color The message color. + */ @SuppressWarnings("unused") public PublicMessage(final String message, final String color) { + super(); this.setMessage(message); this.setColor(color); } From 5417bc1717302474019a1d180227d2d5a7f18b60 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:54:16 -0700 Subject: [PATCH 270/842] Removed generic exceptions (PMD) --- src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 1fc1b34..05b64d7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -7,6 +7,7 @@ import twitter4j.auth.AccessToken; import twitter4j.auth.RequestToken; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; /** @@ -35,8 +36,8 @@ public final class TwitterOAuth { * @param args The consumerKey and consumerSecret should be passed as arguments. */ @SuppressFBWarnings({"DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE"}) - public static void main(final String[] args) - throws Exception { + @SuppressWarnings({"PMD.AvoidPrintStackTrace", "PMD.SystemPrintln"}) + public static void main(final String[] args) throws TwitterException, IOException { if (args.length == 2) { final twitter4j.Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(args[0], args[1]); From 063e23358bf5ad8257bf9f216eb1715650314666 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:55:00 -0700 Subject: [PATCH 271/842] Added default constructor and javadoc. --- .../net/thauvin/erik/mobibot/msg/Message.java | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 6a9bf20..8acf324 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -52,7 +52,7 @@ public class Message { * Creates a new message. */ public Message() { - + // This constructor is intentionally empty. } /** @@ -101,15 +101,6 @@ public class Message { return color; } - /** - * Set the color. - * - * @param color The new color. - */ - public void setColor(final String color) { - this.color = color; - } - /** * Returns the message. * @@ -119,15 +110,6 @@ public class Message { return msg; } - /** - * Sets the message. - * - * @param message The new message. - */ - public void setMessage(final String message) { - msg = message; - } - /** * Returns the message error flag. * @@ -137,15 +119,6 @@ public class Message { return isError; } - /** - * Sets the message error flag. - * - * @param error The error flag. - */ - public void setError(final boolean error) { - isError = error; - } - /** * Returns the message notice flag. * @@ -155,15 +128,6 @@ public class Message { return isNotice; } - /** - * Sets the message notice flag. - * - * @param isNotice The notice flag. - */ - public void setNotice(final boolean isNotice) { - this.isNotice = isNotice; - } - /** * Returns the message private flag. * @@ -173,6 +137,42 @@ public class Message { return isPrivate; } + /** + * Set the color. + * + * @param color The new color. + */ + public void setColor(final String color) { + this.color = color; + } + + /** + * Sets the message error flag. + * + * @param error The error flag. + */ + public void setError(final boolean error) { + isError = error; + } + + /** + * Sets the message. + * + * @param message The new message. + */ + public void setMessage(final String message) { + msg = message; + } + + /** + * Sets the message notice flag. + * + * @param isNotice The notice flag. + */ + public void setNotice(final boolean isNotice) { + this.isNotice = isNotice; + } + /** * Sets the message private flag. * From e785fd6852358492d8307eaada274cb87cc17e18 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:56:17 -0700 Subject: [PATCH 272/842] Removed unecessary variable (PMD) --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 9a55ace..1ff35f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -173,10 +173,10 @@ public final class Utils { * @return <code>true</code> if the string is valid, <code>false</code> otherwise. */ public static boolean isValidString(final CharSequence s) { - final int len; - if (s == null || (len = s.length()) == 0) { + if (s == null || s.length() == 0) { return false; } + final int len = s.length(); for (int i = 0; i < len; i++) { if (!Character.isWhitespace(s.charAt(i))) { return true; From 214180491ff3ff2072321bef50fe3bae056d46b8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:56:33 -0700 Subject: [PATCH 273/842] Added missing @Override (PMD) --- src/main/java/net/thauvin/erik/mobibot/FeedReader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index e313e3f..43621a9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -80,6 +80,7 @@ class FeedReader implements Runnable { /** * Fetches the Feed's items. */ + @Override public final void run() { try { final SyndFeedInput input = new SyndFeedInput(); From eec6741a2fc561ae7b2a049340944d8c39f53167 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:57:36 -0700 Subject: [PATCH 274/842] Combined append calls (PMD) --- .../java/net/thauvin/erik/mobibot/entries/EntriesUtils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java index 8e7f9ba..63e8561 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -87,9 +87,8 @@ public final class EntriesUtils { */ @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.") public static String buildLink(final int index, final EntryLink entry, final boolean isView) { - final StringBuilder buff = new StringBuilder().append(Commands.LINK_CMD).append(index + 1).append(": "); - - buff.append('[').append(entry.getNick()).append(']'); + final StringBuilder buff = new StringBuilder().append(Commands.LINK_CMD).append(index + 1) + .append(": ").append('[').append(entry.getNick()).append(']'); if (isView && entry.hasComments()) { buff.append("[+").append(entry.getCommentsCount()).append(']'); From 612c358d26d7670135608f4c28fce3badcc46edb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:58:31 -0700 Subject: [PATCH 275/842] Moved variable outside for-loop (PMD) --- .../net/thauvin/erik/mobibot/modules/ModuleException.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 890936a..4e143ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -46,6 +46,7 @@ import java.util.regex.Pattern; */ public class ModuleException extends Exception { private static final long serialVersionUID = -3036774290621088107L; + private final String debugMessage; private final Pattern urlPattern = Pattern.compile("(https?://\\S+)(\\?\\S+)"); @@ -104,11 +105,12 @@ public class ModuleException extends Exception { final HttpUrl url = HttpUrl.parse(matcher.group(1) + matcher.group(2)); if (url != null) { final StringBuilder query = new StringBuilder("?"); - for (int i = 0, size = url.querySize(); i < size; i++) { + final int size = url.querySize(); + for (int i = 0; i < size; i++) { if (i > 0) { query.append('&'); } - query.append(url.queryParameterName(i)).append('=').append('[') + query.append(url.queryParameterName(i)).append("=[") .append(url.queryParameterValue(i).length()).append(']'); } return getDebugMessage() + "\nCaused by: " + getCause().getClass().getName() + ": " From ae023204787df23acd82a0bc13adccd5d293887c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:58:59 -0700 Subject: [PATCH 276/842] Using ConcurrentHashMap (PMD) --- .../java/net/thauvin/erik/mobibot/modules/AbstractModule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index af9fbe3..a704eca 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -36,10 +36,10 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * The <code>Module</code> abstract class. @@ -50,7 +50,7 @@ import java.util.Set; */ public abstract class AbstractModule { final List<String> commands = new ArrayList<>(); - final Map<String, String> properties = new HashMap<>(); + final Map<String, String> properties = new ConcurrentHashMap<>(); /** * Responds to a command. From 8e7873192402e27497bc880511ff45ab7b07c85c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:59:31 -0700 Subject: [PATCH 277/842] Made logger static. (PMD) --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c20dd45..5876038 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -165,6 +165,8 @@ public class Mobibot extends PircBot { + System.getProperty("java.vm.info") + ')' }; + // The logger. + private static final Logger logger = LogManager.getLogger(Mobibot.class); // The commands list. private final List<String> commandsList = new ArrayList<>(); // The entries array. @@ -179,8 +181,6 @@ public class Mobibot extends PircBot { private final int ircPort; // The IRC server. private final String ircServer; - // The logger. - private final Logger logger = LogManager.getLogger(Mobibot.class); // The logger default level. private final Level loggerLevel; // The log directory. From f6234f6db342af8c2d7160919c66283d0d3019fe Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 02:59:52 -0700 Subject: [PATCH 278/842] Removed uneccesary initialization. (PMD) --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 5876038..9f5caad 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -206,7 +206,7 @@ public class Mobibot extends PircBot { // The NickServ ident password. private String identPwd = ""; // The pinboard posts handler. - private Pinboard pinboard = null; + private Pinboard pinboard; // Today's date. private String today = Utils.today(); // The weblog URL. From 7f06e8b34852dc1ff7fb4c084e00a37703f3412b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:01:01 -0700 Subject: [PATCH 279/842] Made sure logger is enabled before logging errors/warnings. (PMD) --- .../net/thauvin/erik/mobibot/Mobibot.java | 104 +++++++++++++++++- 1 file changed, 100 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 9f5caad..6b65e67 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -251,7 +251,9 @@ public class Mobibot extends PircBot { } catch (IOException ignore) { // Do nothing. } catch (FeedException e) { - logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); + if (logger.isErrorEnabled()) { + logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); + } } // Load the backlogs, if any. @@ -260,7 +262,9 @@ public class Mobibot extends PircBot { } catch (IOException ignore) { // Do nothing. } catch (FeedException e) { - logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); + if (logger.isErrorEnabled()) { + logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); + } } // Initialize the bot @@ -939,13 +943,105 @@ public class Mobibot extends PircBot { getName() + ReleaseInfo.VERSION + " has joined " + ircChannel, true); } catch (ModuleException e) { - logger.warn( - "Failed to notify " + twitterHandle + " of joining " + ircChannel + " on Twitter.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to notify " + twitterHandle + " of joining " + ircChannel + " on Twitter.", e); + } } }).start(); } } + /** + * The Truth Is Out There... + * + * @param args The command line arguments. + */ + @SuppressFBWarnings({"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING", + "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS"}) + @SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidFileStream"}) + public static void main(final String[] args) { + // Setup the command line options + final Options options = new Options(); + options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); + options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, + "print debug & logging data directly to the console"); + options.addOption(Option.builder( + Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") + .longOpt(Commands.PROPS_ARG).build()); + options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); + + // Parse the command line + final CommandLineParser parser = new DefaultParser(); + CommandLine line = null; + + try { + line = parser.parse(options, args); + } catch (ParseException e) { + System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); + e.printStackTrace(System.err); + System.exit(1); + } + + if (line.hasOption(Commands.HELP_ARG.charAt(0))) { + // Output the usage + new HelpFormatter().printHelp(Mobibot.class.getName(), options); + } else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) { + for (final String s : INFO_STRS) { + System.out.println(s); + } + } else { + final Properties p = new Properties(); + + try (final InputStream fis = Files.newInputStream(Paths.get( + line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + // Load the properties files + p.load(fis); + } catch (FileNotFoundException e) { + System.err.println("Unable to find properties file."); + e.printStackTrace(System.err); + System.exit(1); + } catch (IOException e) { + System.err.println("Unable to open properties file."); + e.printStackTrace(System.err); + System.exit(1); + } + + final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase(Constants.LOCALE)); + final String channel = p.getProperty("channel"); + final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); + + // Redirect the stdout and stderr + if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { + try { + final PrintStream stdout = new PrintStream(new FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + System.setOut(stdout); + } catch (IOException e) { + System.err.println("Unable to open output (stdout) log file."); + e.printStackTrace(System.err); + System.exit(1); + } + + try { + final PrintStream stderr = new PrintStream( + new FileOutputStream(logsDir + nickname + ".err", true)); + System.setErr(stderr); + } catch (IOException e) { + System.err.println("Unable to open error (stderr) log file."); + e.printStackTrace(System.err); + System.exit(1); + } + } + + // Create the bot + final Mobibot bot = new Mobibot(nickname, channel, logsDir, p); + + // Connect + bot.connect(); + } + } + /** * {@inheritDoc} */ From f4afbcaaa57f8d007d93737f32abd673e494b16f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:04:40 -0700 Subject: [PATCH 280/842] Added locale. (PMD) --- .../net/thauvin/erik/mobibot/Mobibot.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 6b65e67..e6ff9e8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -800,8 +800,8 @@ public class Mobibot extends PircBot { */ private void ignoreResponse(final String sender, final String args) { if (!isOp(sender)) { - final String nick = sender.toLowerCase(); - final boolean isMe = args.toLowerCase().startsWith(Commands.IGNORE_ME_KEYWORD); + final String nick = sender.toLowerCase(Constants.LOCALE); + final boolean isMe = args.toLowerCase(Constants.LOCALE).startsWith(Commands.IGNORE_ME_KEYWORD); if (isMe) { if (ignoredNicks.remove(nick)) { send(sender, "You are no longer ignored."); @@ -818,13 +818,13 @@ public class Mobibot extends PircBot { } } else { if (args.length() > 0) { - final String[] nicks = args.toLowerCase().split(" "); + final String[] nicks = args.toLowerCase(Constants.LOCALE).split(" "); for (final String nick : nicks) { final String ignore; if (Commands.IGNORE_ME_KEYWORD.equals(nick)) { - ignore = sender.toLowerCase(); + ignore = sender.toLowerCase(Constants.LOCALE); } else { ignore = nick; } @@ -908,7 +908,7 @@ public class Mobibot extends PircBot { * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. */ private boolean isIgnoredNick(final String nick) { - return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase()); + return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE)); } /** @@ -1149,7 +1149,7 @@ public class Mobibot extends PircBot { isCommand = true; final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); - final String cmd = cmds[0].toLowerCase(); + final String cmd = cmds[0].toLowerCase(Constants.LOCALE); String args = ""; @@ -1376,7 +1376,7 @@ public class Mobibot extends PircBot { } final String[] cmds = message.split(" ", 2); - final String cmd = cmds[0].toLowerCase(); + final String cmd = cmds[0].toLowerCase(Constants.LOCALE); String args = ""; if (cmds.length > 1) { @@ -1641,7 +1641,7 @@ public class Mobibot extends PircBot { final StringTokenizer st = new StringTokenizer(nicks, ","); while (st.hasMoreTokens()) { - ignoredNicks.add(st.nextToken().trim().toLowerCase()); + ignoredNicks.add(st.nextToken().trim().toLowerCase(Constants.LOCALE)); } } } @@ -1733,7 +1733,7 @@ public class Mobibot extends PircBot { * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void viewResponse(final String sender, final String args, final boolean isPrivate) { - String lcArgs = args.toLowerCase(); + String lcArgs = args.toLowerCase(Constants.LOCALE); if (!entries.isEmpty()) { final int max = entries.size(); @@ -1774,9 +1774,9 @@ public class Mobibot extends PircBot { entry = entries.get(i); if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase().contains(lcArgs)) - || (entry.getTitle().toLowerCase().contains(lcArgs)) - || (entry.getNick().toLowerCase().contains(lcArgs))) { + if ((entry.getLink().toLowerCase(Constants.LOCALE).contains(lcArgs)) + || (entry.getTitle().toLowerCase(Constants.LOCALE).contains(lcArgs)) + || (entry.getNick().toLowerCase(Constants.LOCALE).contains(lcArgs))) { if (sent > MAX_ENTRIES) { send(sender, "To view more, try: " + Utils From 7e16f1cb3fe24077ec955a5a5a06e2c571d3ba9d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:06:37 -0700 Subject: [PATCH 281/842] Used variable instead of repeat literal strings. (PMD) --- .../net/thauvin/erik/mobibot/Mobibot.java | 143 +++++++++--------- 1 file changed, 73 insertions(+), 70 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index e6ff9e8..adc8324 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -689,91 +689,94 @@ public class Mobibot extends PircBot { } else if (Commands.INFO_CMD.equals(lcTopic)) { send(sender, "To view information about the bot:"); send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); - } else if (Commands.CYCLE_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot leave the channel and come back:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.CYCLE_CMD)); - } else if (Commands.ME_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot perform an action:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.ME_CMD + " <action>")); - } else if (Commands.SAY_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot say something on the channel:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.SAY_CMD + " <text>")); - } else if (Commands.VERSION_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To view the version data (bot, java, etc.):"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.VERSION_CMD)); - } else if (Commands.MSG_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot send a private message to someone:"); - send(sender, helpIndent("/msg " + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); - } else if (Commands.IGNORE_CMD.equals(lcTopic)) { - send(sender, "To check your ignore status:"); - send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); - - send(sender, "To toggle your ignore status:"); - send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); - } else if (Tell.TELL_CMD.equals(lcTopic) && tell.isEnabled()) { - tell.helpResponse(sender); } else { - for (final AbstractModule module : MODULES) { - for (final String cmd : module.getCommands()) { - if (lcTopic.equals(cmd)) { - module.helpResponse(this, sender, topic, true); - return; + final String msg = "/msg "; + if (Commands.CYCLE_CMD.equals(lcTopic) && isOp(sender)) { + send(sender, "To have the bot leave the channel and come back:"); + send(sender, helpIndent(msg + getNick() + ' ' + Commands.CYCLE_CMD)); + } else if (Commands.ME_CMD.equals(lcTopic) && isOp(sender)) { + send(sender, "To have the bot perform an action:"); + send(sender, helpIndent(msg + getNick() + ' ' + Commands.ME_CMD + " <action>")); + } else if (Commands.SAY_CMD.equals(lcTopic) && isOp(sender)) { + send(sender, "To have the bot say something on the channel:"); + send(sender, helpIndent(msg + getNick() + ' ' + Commands.SAY_CMD + " <text>")); + } else if (Commands.VERSION_CMD.equals(lcTopic) && isOp(sender)) { + send(sender, "To view the version data (bot, java, etc.):"); + send(sender, helpIndent(msg + getNick() + ' ' + Commands.VERSION_CMD)); + } else if (Commands.MSG_CMD.equals(lcTopic) && isOp(sender)) { + send(sender, "To have the bot send a private message to someone:"); + send(sender, helpIndent(msg + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); + } else if (Commands.IGNORE_CMD.equals(lcTopic)) { + send(sender, "To check your ignore status:"); + send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); + + send(sender, "To toggle your ignore status:"); + send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); + } else if (Tell.TELL_CMD.equals(lcTopic) && tell.isEnabled()) { + tell.helpResponse(sender); + } else { + for (final AbstractModule module : MODULES) { + for (final String cmd : module.getCommands()) { + if (lcTopic.equals(cmd)) { + module.helpResponse(this, sender, topic, true); + return; + } } } - } - send(sender, Utils.bold("Type a URL on " + ircChannel + " to post it.")); - send(sender, "For more information on a specific command, type:"); - send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); - send(sender, "The commands are:"); + send(sender, Utils.bold("Type a URL on " + ircChannel + " to post it.")); + send(sender, "For more information on a specific command, type:"); + send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); + send(sender, "The commands are:"); - if (commandsList.isEmpty()) { - commandsList.add(Commands.IGNORE_CMD); - commandsList.add(Commands.INFO_CMD); - commandsList.add(getChannelName()); - commandsList.add(Commands.HELP_POSTING_KEYWORD); - commandsList.add(Commands.HELP_TAGS_KEYWORD); - commandsList.add(Commands.RECAP_CMD); - commandsList.add(Commands.USERS_CMD); - commandsList.add(Commands.VIEW_CMD); + if (commandsList.isEmpty()) { + commandsList.add(Commands.IGNORE_CMD); + commandsList.add(Commands.INFO_CMD); + commandsList.add(getChannelName()); + commandsList.add(Commands.HELP_POSTING_KEYWORD); + commandsList.add(Commands.HELP_TAGS_KEYWORD); + commandsList.add(Commands.RECAP_CMD); + commandsList.add(Commands.USERS_CMD); + commandsList.add(Commands.VIEW_CMD); - MODULES.stream().filter(AbstractModule::isEnabled).forEach( - module -> commandsList.addAll(module.getCommands())); + MODULES.stream().filter(AbstractModule::isEnabled).forEach( + module -> commandsList.addAll(module.getCommands())); - if (tell.isEnabled()) { - commandsList.add(Tell.TELL_CMD); + if (tell.isEnabled()) { + commandsList.add(Tell.TELL_CMD); + } + + Collections.sort(commandsList); } - Collections.sort(commandsList); - } + final StringBuilder sb = new StringBuilder(0); - final StringBuilder sb = new StringBuilder(0); + for (int i = 0, cmdCount = 1; i < commandsList.size(); i++, cmdCount++) { + if (sb.length() > 0) { + sb.append(" "); + } - for (int i = 0, cmdCount = 1; i < commandsList.size(); i++, cmdCount++) { - if (sb.length() > 0) { - sb.append(" "); + sb.append(commandsList.get(i)); + + // 6 commands per line or last command + if (sb.length() > 0 && (cmdCount == 6 || i == (commandsList.size() - 1))) { + send(sender, helpIndent(sb.toString())); + + sb.setLength(0); + cmdCount = 0; + } } - sb.append(commandsList.get(i)); - - // 6 commands per line or last command - if (sb.length() > 0 && (cmdCount == 6 || i == (commandsList.size() - 1))) { - send(sender, helpIndent(sb.toString())); - - sb.setLength(0); - cmdCount = 0; + if (isOp(sender)) { + send(sender, "The op commands are:"); + send(sender, helpIndent( + Commands.CYCLE_CMD + " " + + Commands.ME_CMD + " " + + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + + Commands.VERSION_CMD)); } } - - if (isOp(sender)) { - send(sender, "The op commands are:"); - send(sender, helpIndent( - Commands.CYCLE_CMD + " " - + Commands.ME_CMD + " " - + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " - + Commands.VERSION_CMD)); - } } } From ad4701060d469239280f9d62e83ac570afe50a0d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:07:25 -0700 Subject: [PATCH 282/842] PMD cleanup. --- .../net/thauvin/erik/mobibot/Mobibot.java | 162 ++++-------------- 1 file changed, 35 insertions(+), 127 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index adc8324..0caaa6c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -221,6 +221,7 @@ public class Mobibot extends PircBot { * @param p The bot's properties. */ public Mobibot(final String nickname, final String channel, final String logsDirPath, final Properties p) { + super(); System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(Constants.CONNECT_TIMEOUT)); System.getProperties().setProperty("sun.net.client.defaultReadTimeout", @@ -323,108 +324,6 @@ public class Mobibot extends PircBot { saveEntries(true); } - /** - * The Truth Is Out There... - * - * @param args The command line arguments. - */ - @SuppressFBWarnings({"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING", - "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS"}) - public static void main(final String[] args) { - // Setup the command line options - final Options options = new Options(); - options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); - options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, - "print debug & logging data directly to the console"); - options.addOption(Option.builder( - Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") - .longOpt(Commands.PROPS_ARG).build()); - options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); - - // Parse the command line - final CommandLineParser parser = new DefaultParser(); - CommandLine line = null; - - try { - line = parser.parse(options, args); - } catch (ParseException e) { - System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } - - if (line.hasOption(Commands.HELP_ARG.charAt(0))) { - // Output the usage - new HelpFormatter().printHelp(Mobibot.class.getName(), options); - } else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) { - for (final String s : INFO_STRS) { - System.out.println(s); - } - } else { - final Properties p = new Properties(); - - try (final InputStream fis = Files.newInputStream(Paths.get( - line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { - // Load the properties files - p.load(fis); - } catch (FileNotFoundException e) { - System.err.println("Unable to find properties file."); - e.printStackTrace(System.err); - System.exit(1); - } catch (IOException e) { - System.err.println("Unable to open properties file."); - e.printStackTrace(System.err); - System.exit(1); - } - - final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase()); - final String channel = p.getProperty("channel"); - final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); - - // Redirect the stdout and stderr - if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { - try { - final PrintStream stdout = new PrintStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); - System.setOut(stdout); - } catch (IOException e) { - System.err.println("Unable to open output (stdout) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - - try { - final PrintStream stderr = new PrintStream( - new FileOutputStream(logsDir + nickname + ".err", true)); - System.setErr(stderr); - } catch (IOException e) { - System.err.println("Unable to open error (stderr) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - } - - // Create the bot - final Mobibot bot = new Mobibot(nickname, channel, logsDir, p); - - // Connect - bot.connect(); - } - } - - /** - * Sleeps for the specified number of seconds. - * - * @param secs The number of seconds to sleep for. - */ - private static void sleep(final int secs) { - try { - Thread.sleep((long) (secs * 1000)); - } catch (InterruptedException ignore) { - // Do nothing. - } - } - /** * Sends an action to the current channel. * @@ -449,7 +348,7 @@ public class Mobibot extends PircBot { /** * Connects to the server and joins the channel. */ - @SuppressFBWarnings(value = {"DM_EXIT", "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE"}) + @SuppressFBWarnings({"DM_EXIT", "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE"}) public final void connect() { try { connect(ircServer, ircPort); @@ -523,15 +422,6 @@ public class Mobibot extends PircBot { return backLogsUrl; } - /** - * Sets the backlogs URL. - * - * @param url The backlogs URL. - */ - final void setBacklogsUrl(final String url) { - backLogsUrl = url; - } - /** * Returns the current channel. * @@ -618,15 +508,6 @@ public class Mobibot extends PircBot { return weblogUrl; } - /** - * Sets the weblog URL. - * - * @param url The weblog URL. - */ - final void setWeblogUrl(final String url) { - weblogUrl = url; - } - /** * Returns indented and bold help string. * @@ -857,7 +738,7 @@ public class Mobibot extends PircBot { } } - final StringBuilder info = new StringBuilder("Uptime: "); + final StringBuilder info = new StringBuilder(28).append("Uptime: "); long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; @@ -891,9 +772,8 @@ public class Mobibot extends PircBot { final long minutes = timeInSeconds / 60L; - info.append(minutes).append(Utils.plural(minutes, " minute ", " minutes ")); - - info.append("[Entries: ").append(entries.size()); + info.append(minutes).append(Utils.plural(minutes, " minute ", " minutes ")) + .append("[Entries: ").append(entries.size()); if (tell.isEnabled() && isOp(sender)) { info.append(", Messages: ").append(tell.size()); @@ -1576,7 +1456,6 @@ public class Mobibot extends PircBot { send(who, message, false); } - /** * Sends a message. * @@ -1599,7 +1478,6 @@ public class Mobibot extends PircBot { send(who, Utils.colorize(message, color), isPrivate); } - /** * Sends a message. * @@ -1611,6 +1489,14 @@ public class Mobibot extends PircBot { send(who, Utils.colorize(message, color), false); } + /** + * Sets the backlogs URL. + * + * @param url The backlogs URL. + */ + final void setBacklogsUrl(final String url) { + backLogsUrl = url; + } /** * Sets the feed URL. @@ -1669,6 +1555,28 @@ public class Mobibot extends PircBot { defaultTags = tags; } + /** + * Sets the weblog URL. + * + * @param url The weblog URL. + */ + final void setWeblogUrl(final String url) { + weblogUrl = url; + } + + /** + * Sleeps for the specified number of seconds. + * + * @param secs The number of seconds to sleep for. + */ + private static void sleep(final int secs) { + try { + Thread.sleep((long) (secs * 1000)); + } catch (InterruptedException ignore) { + // Do nothing. + } + } + /** * Stores the last 10 public messages and actions. * From 0ed3207a319c54287c2d09996de5c1a64737790d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:08:49 -0700 Subject: [PATCH 283/842] Added PMD. Updated dependencies. --- .idea/modules/mobibot.iml | 20 +- build.gradle | 16 +- gradle/wrapper/gradle-wrapper.properties | 2 +- mobibot.ipr | 1052 +++++++++++++++++++++- version.mustache | 16 +- 5 files changed, 1044 insertions(+), 62 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index a223542..4d50b07 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+204" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+345" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -25,27 +25,27 @@ <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0" level="project" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.2.3" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.4.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.4.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.0" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.1" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20180813" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.11.3" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.11" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta1" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.30" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.30" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> diff --git a/build.gradle b/build.gradle index 7b837d1..d7053d3 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,10 @@ plugins { id 'idea' id 'jacoco' id 'java' + id 'pmd' id "com.github.ben-manes.versions" version "0.21.0" id "com.github.spotbugs" version "1.7.1" - id "net.thauvin.erik.gradle.semver" version "0.9.9-beta" + id "net.thauvin.erik.gradle.semver" version "1.0.0" id "org.sonarqube" version "2.7" } @@ -18,7 +19,7 @@ defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -final def semverProcessor = "net.thauvin.erik:semver:1.0.1" +final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' @@ -28,7 +29,7 @@ compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${proje ext { log4j = '2.11.2' - spotbugs_annotations = '3.1.12' + spotbugs_annotations = '4.0.0-beta1' } repositories { @@ -76,10 +77,15 @@ dependencies { test { useTestNG() { - suites 'src/test/resources/testng.xml' + suites('src/test/resources/testng.xml') } } +pmd { + ruleSetFiles = files("config/pmd.xml") + ruleSets = [] +} + compileJava { dependsOn('incrementBuildMeta') options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' @@ -155,7 +161,7 @@ task copyToDeployLib(type: Copy) { into deployDir + '/lib' } -task deploy(dependsOn: ['build']) { +task deploy(dependsOn: ['build', 'jar']) { description = 'Copies all needed files to the ${deployDir} directory.' group = 'Publishing' outputs.dir deployDir diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5f1b120..f4d7b2b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/mobibot.ipr b/mobibot.ipr index 5efd501..12cb55c 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1,5 +1,943 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> + <component name="AnalysisProjectProfileManager"> + <option name="PROJECT_PROFILE" value="Project Default" /> + <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" /> + <scopes /> + <profiles> + <profile profile_name="Project Default" version="1.0" is_locked="false"> + <coding_rule class="AM_CREATES_EMPTY_JAR_FILE_ENTRY" level="MAJOR" enabled="true" /> + <coding_rule class="AM_CREATES_EMPTY_ZIP_FILE_ENTRY" level="MAJOR" enabled="true" /> + <coding_rule class="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION" level="CRITICAL" enabled="false" /> + <coding_rule class="AbstractClassNameCheck" level="MAJOR" enabled="false"> + <param name="ignoreModifier" value="false" /> + <param name="ignoreName" value="false" /> + </coding_rule> + <coding_rule class="AnnotationUseStyleCheck" level="MAJOR" enabled="false" /> + <coding_rule class="AnonInnerLengthCheck" level="MAJOR" enabled="true" /> + <coding_rule class="ArrayTrailingCommaCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ArrayTypeStyleCheck" level="INFO" enabled="false" /> + <coding_rule class="AvoidInlineConditionalsCheck" level="INFO" enabled="false" /> + <coding_rule class="AvoidNestedBlocksCheck" level="MAJOR" enabled="false" /> + <coding_rule class="AvoidStarImportCheck" level="INFO" enabled="false"> + <param name="allowClassImports" value="false" /> + <param name="allowStaticMemberImports" value="false" /> + </coding_rule> + <coding_rule class="AvoidStaticImportCheck" level="MAJOR" enabled="false" /> + <coding_rule class="BAC_BAD_APPLET_CONSTRUCTOR" level="MAJOR" enabled="false" /> + <coding_rule class="BC_BAD_CAST_TO_ABSTRACT_COLLECTION" level="MAJOR" enabled="true" /> + <coding_rule class="BC_BAD_CAST_TO_CONCRETE_COLLECTION" level="CRITICAL" enabled="true" /> + <coding_rule class="BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS" level="CRITICAL" enabled="true" /> + <coding_rule class="BC_IMPOSSIBLE_CAST" level="BLOCKER" enabled="true" /> + <coding_rule class="BC_IMPOSSIBLE_DOWNCAST" level="MAJOR" enabled="false" /> + <coding_rule class="BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY" level="MAJOR" enabled="false" /> + <coding_rule class="BC_IMPOSSIBLE_INSTANCEOF" level="CRITICAL" enabled="true" /> + <coding_rule class="BC_UNCONFIRMED_CAST" level="CRITICAL" enabled="true" /> + <coding_rule class="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" level="CRITICAL" enabled="false" /> + <coding_rule class="BC_VACUOUS_INSTANCEOF" level="CRITICAL" enabled="true" /> + <coding_rule class="BIT_ADD_OF_SIGNED_BYTE" level="CRITICAL" enabled="true" /> + <coding_rule class="BIT_AND" level="CRITICAL" enabled="true" /> + <coding_rule class="BIT_AND_ZZ" level="CRITICAL" enabled="true" /> + <coding_rule class="BIT_IOR" level="CRITICAL" enabled="true" /> + <coding_rule class="BIT_IOR_OF_SIGNED_BYTE" level="CRITICAL" enabled="true" /> + <coding_rule class="BIT_SIGNED_CHECK" level="CRITICAL" enabled="true" /> + <coding_rule class="BIT_SIGNED_CHECK_HIGH_BIT" level="CRITICAL" enabled="true" /> + <coding_rule class="BOA_BADLY_OVERRIDDEN_ADAPTER" level="CRITICAL" enabled="true" /> + <coding_rule class="BSHIFT_WRONG_ADD_PRIORITY" level="MAJOR" enabled="false" /> + <coding_rule class="BX_BOXING_IMMEDIATELY_UNBOXED" level="MAJOR" enabled="true" /> + <coding_rule class="BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION" level="MAJOR" enabled="true" /> + <coding_rule class="BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR" level="MAJOR" enabled="true" /> + <coding_rule class="BX_UNBOXING_IMMEDIATELY_REBOXED" level="CRITICAL" enabled="false" /> + <coding_rule class="BestPracticesAbstractClassWithoutAbstractMethod" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesAccessorClassGeneration" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesAccessorMethodGeneration" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesArrayIsStoredDirectly" level="CRITICAL" enabled="true" /> + <coding_rule class="BestPracticesAvoidPrintStackTrace" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesAvoidReassigningParameters" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesAvoidStringBufferField" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesAvoidUsingHardCodedIP" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesCheckResultSet" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesConstantsInInterface" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesDefaultLabelNotLastInSwitchStmt" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesDontNestJsfInJstlIteration" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesForLoopCanBeForeach" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesGuardDebugLogging" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesGuardLogStatement" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesGuardLogStatementJavaUtil" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnit4SuitesShouldUseSuiteAnnotation" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnit4TestShouldUseAfterAnnotation" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnit4TestShouldUseBeforeAnnotation" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnit4TestShouldUseTestAnnotation" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnitAssertionsShouldIncludeMessage" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnitTestContainsTooManyAsserts" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnitTestsShouldIncludeAssert" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesJUnitUseExpected" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesLooseCoupling" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesMethodReturnsInternalArray" level="CRITICAL" enabled="false" /> + <coding_rule class="BestPracticesNoClassAttribute" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesNoHtmlComments" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesNoJspForward" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesOneDeclarationPerLine" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesPositionLiteralsFirstInCaseInsensitiveComparisons" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesPositionLiteralsFirstInComparisons" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesPreserveStackTrace" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesReplaceEnumerationWithIterator" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesReplaceHashtableWithMap" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesReplaceVectorWithList" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesSwitchStmtsShouldHaveDefault" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesSystemPrintln" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesUnusedFormalParameter" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesUnusedImports" level="INFO" enabled="false" /> + <coding_rule class="BestPracticesUnusedLocalVariable" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesUnusedPrivateField" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesUnusedPrivateMethod" level="MAJOR" enabled="true" /> + <coding_rule class="BestPracticesUseAssertEqualsInsteadOfAssertTrue" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesUseAssertNullInsteadOfAssertTrue" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesUseAssertSameInsteadOfAssertTrue" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesUseAssertTrueInsteadOfAssertEquals" level="MAJOR" enabled="false" /> + <coding_rule class="BestPracticesUseCollectionIsEmpty" level="MINOR" enabled="false" /> + <coding_rule class="BestPracticesUseVarargs" level="MAJOR" enabled="false" /> + <coding_rule class="BooleanExpressionComplexityCheck" level="MAJOR" enabled="true" /> + <coding_rule class="CAA_COVARIANT_ARRAY_ELEMENT_STORE" level="MAJOR" enabled="false" /> + <coding_rule class="CAA_COVARIANT_ARRAY_FIELD" level="MAJOR" enabled="false" /> + <coding_rule class="CAA_COVARIANT_ARRAY_LOCAL" level="MAJOR" enabled="false" /> + <coding_rule class="CAA_COVARIANT_ARRAY_RETURN" level="MAJOR" enabled="false" /> + <coding_rule class="CD_CIRCULAR_DEPENDENCY" level="MAJOR" enabled="false" /> + <coding_rule class="CI_CONFUSED_INHERITANCE" level="MINOR" enabled="true" /> + <coding_rule class="CNT_ROUGH_CONSTANT_VALUE" level="MAJOR" enabled="false" /> + <coding_rule class="CN_IDIOM" level="MAJOR" enabled="true" /> + <coding_rule class="CN_IDIOM_NO_SUPER_CALL" level="MAJOR" enabled="true" /> + <coding_rule class="CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE" level="MAJOR" enabled="true" /> + <coding_rule class="CO_ABSTRACT_SELF" level="MAJOR" enabled="true" /> + <coding_rule class="CO_COMPARETO_INCORRECT_FLOATING" level="MAJOR" enabled="false" /> + <coding_rule class="CO_COMPARETO_RESULTS_MIN_VALUE" level="CRITICAL" enabled="false" /> + <coding_rule class="CO_SELF_NO_OBJECT" level="MAJOR" enabled="true" /> + <coding_rule class="ClassDataAbstractionCouplingCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ClassFanOutComplexityCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ClassTypeParameterNameCheck" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleAbstractNaming" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleAtLeastOneConstructor" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleAvoidDollarSigns" level="MINOR" enabled="true" /> + <coding_rule class="CodeStyleAvoidFinalLocalVariable" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleAvoidPrefixingMethodParameters" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleAvoidProtectedFieldInFinalClass" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleAvoidProtectedMethodInFinalClassNotExtending" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleAvoidUsingNativeCode" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleBooleanGetMethodName" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleCallSuperInConstructor" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleClassNamingConventions" level="MAJOR" enabled="true" /> + <coding_rule class="CodeStyleCommentDefaultAccessModifier" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleConfusingTernary" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleDefaultPackage" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleDontImportJavaLang" level="MINOR" enabled="true" /> + <coding_rule class="CodeStyleDuplicateImports" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleDuplicateJspImports" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleEmptyMethodInAbstractClassShouldBeAbstract" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleExtendsObject" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleFieldDeclarationsShouldBeAtStartOfClass" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleForLoopShouldBeWhileLoop" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleForLoopsMustUseBraces" level="MAJOR" enabled="true" /> + <coding_rule class="CodeStyleGenericsNaming" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleIfElseStmtsMustUseBraces" level="MAJOR" enabled="true" /> + <coding_rule class="CodeStyleIfStmtsMustUseBraces" level="MAJOR" enabled="true" /> + <coding_rule class="CodeStyleLocalHomeNamingConvention" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleLocalInterfaceSessionNamingConvention" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleLocalVariableCouldBeFinal" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleLongVariable" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleMDBAndSessionBeanNamingConvention" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleMIsLeadingVariableName" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleMethodArgumentCouldBeFinal" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleMethodNamingConventions" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleMisleadingVariableName" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleNoPackage" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleOnlyOneReturn" level="MINOR" enabled="false" /> + <coding_rule class="CodeStylePackageCase" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStylePrematureDeclaration" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleRemoteInterfaceNamingConvention" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleRemoteSessionInterfaceNamingConvention" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleShortClassName" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleShortMethodName" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleShortVariable" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleSuspiciousConstantFieldName" level="MAJOR" enabled="true" /> + <coding_rule class="CodeStyleTooManyStaticImports" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleUnnecessaryConstructor" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleUnnecessaryFinalModifier" level="INFO" enabled="false" /> + <coding_rule class="CodeStyleUnnecessaryFullyQualifiedName" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleUnnecessaryLocalBeforeReturn" level="MAJOR" enabled="true" /> + <coding_rule class="CodeStyleUnnecessaryModifier" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleUnnecessaryParentheses" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleUnnecessaryReturn" level="MINOR" enabled="false" /> + <coding_rule class="CodeStyleUselessParentheses" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleUselessQualifiedThis" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleVariableNamingConventions" level="MAJOR" enabled="false" /> + <coding_rule class="CodeStyleWhileLoopsMustUseBraces" level="MAJOR" enabled="true" /> + <coding_rule class="ConstantNameCheck" level="INFO" enabled="true" /> + <coding_rule class="CovariantEqualsCheck" level="MAJOR" enabled="false" /> + <coding_rule class="CyclomaticComplexityCheck" level="MAJOR" enabled="true" /> + <coding_rule class="DB_DUPLICATE_BRANCHES" level="CRITICAL" enabled="true" /> + <coding_rule class="DB_DUPLICATE_SWITCH_CLAUSES" level="CRITICAL" enabled="true" /> + <coding_rule class="DC_DOUBLECHECK" level="MAJOR" enabled="true" /> + <coding_rule class="DC_PARTIALLY_CONSTRUCTED" level="MAJOR" enabled="false" /> + <coding_rule class="DE_MIGHT_DROP" level="MAJOR" enabled="true" /> + <coding_rule class="DE_MIGHT_IGNORE" level="MAJOR" enabled="true" /> + <coding_rule class="DLS_DEAD_LOCAL_INCREMENT_IN_RETURN" level="MAJOR" enabled="false" /> + <coding_rule class="DLS_DEAD_LOCAL_STORE" level="CRITICAL" enabled="true" /> + <coding_rule class="DLS_DEAD_LOCAL_STORE_IN_RETURN" level="CRITICAL" enabled="true" /> + <coding_rule class="DLS_DEAD_LOCAL_STORE_OF_NULL" level="CRITICAL" enabled="true" /> + <coding_rule class="DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD" level="CRITICAL" enabled="false" /> + <coding_rule class="DLS_DEAD_STORE_OF_CLASS_LITERAL" level="CRITICAL" enabled="true" /> + <coding_rule class="DLS_OVERWRITTEN_INCREMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="DL_SYNCHRONIZATION_ON_BOOLEAN" level="CRITICAL" enabled="true" /> + <coding_rule class="DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE" level="CRITICAL" enabled="true" /> + <coding_rule class="DL_SYNCHRONIZATION_ON_SHARED_CONSTANT" level="CRITICAL" enabled="true" /> + <coding_rule class="DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION" level="MAJOR" enabled="true" /> + <coding_rule class="DMI_ARGUMENTS_WRONG_ORDER" level="CRITICAL" enabled="false" /> + <coding_rule class="DMI_BAD_MONTH" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE" level="CRITICAL" enabled="false" /> + <coding_rule class="DMI_BLOCKING_METHODS_ON_URL" level="BLOCKER" enabled="true" /> + <coding_rule class="DMI_CALLING_NEXT_FROM_HASNEXT" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_COLLECTION_OF_URLS" level="BLOCKER" enabled="true" /> + <coding_rule class="DMI_CONSTANT_DB_PASSWORD" level="BLOCKER" enabled="true" /> + <coding_rule class="DMI_DOH" level="CRITICAL" enabled="false" /> + <coding_rule class="DMI_EMPTY_DB_PASSWORD" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS" level="CRITICAL" enabled="false" /> + <coding_rule class="DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR" level="MINOR" enabled="true" /> + <coding_rule class="DMI_HARDCODED_ABSOLUTE_FILENAME" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_INVOKING_HASHCODE_ON_ARRAY" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_INVOKING_TOSTRING_ON_ARRAY" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_NONSERIALIZABLE_OBJECT_WRITTEN" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_RANDOM_USED_ONLY_ONCE" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS" level="MINOR" enabled="true" /> + <coding_rule class="DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED" level="MAJOR" enabled="true" /> + <coding_rule class="DMI_UNSUPPORTED_METHOD" level="MAJOR" enabled="true" /> + <coding_rule class="DMI_USELESS_SUBSTRING" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION" level="CRITICAL" enabled="true" /> + <coding_rule class="DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD" level="MINOR" enabled="true" /> + <coding_rule class="DMI_VACUOUS_SELF_COLLECTION_CALL" level="CRITICAL" enabled="true" /> + <coding_rule class="DM_BOOLEAN_CTOR" level="MAJOR" enabled="true" /> + <coding_rule class="DM_BOXED_PRIMITIVE_FOR_COMPARE" level="MAJOR" enabled="false" /> + <coding_rule class="DM_BOXED_PRIMITIVE_FOR_PARSING" level="MAJOR" enabled="false" /> + <coding_rule class="DM_BOXED_PRIMITIVE_TOSTRING" level="MAJOR" enabled="true" /> + <coding_rule class="DM_CONVERT_CASE" level="INFO" enabled="true" /> + <coding_rule class="DM_DEFAULT_ENCODING" level="CRITICAL" enabled="false" /> + <coding_rule class="DM_EXIT" level="MAJOR" enabled="true" /> + <coding_rule class="DM_FP_NUMBER_CTOR" level="MAJOR" enabled="true" /> + <coding_rule class="DM_GC" level="MAJOR" enabled="true" /> + <coding_rule class="DM_INVALID_MIN_MAX" level="MAJOR" enabled="false" /> + <coding_rule class="DM_MONITOR_WAIT_ON_CONDITION" level="MAJOR" enabled="true" /> + <coding_rule class="DM_NEW_FOR_GETCLASS" level="MAJOR" enabled="true" /> + <coding_rule class="DM_NEXTINT_VIA_NEXTDOUBLE" level="MAJOR" enabled="true" /> + <coding_rule class="DM_NUMBER_CTOR" level="CRITICAL" enabled="true" /> + <coding_rule class="DM_RUN_FINALIZERS_ON_EXIT" level="MAJOR" enabled="true" /> + <coding_rule class="DM_STRING_CTOR" level="MAJOR" enabled="true" /> + <coding_rule class="DM_STRING_TOSTRING" level="INFO" enabled="true" /> + <coding_rule class="DM_STRING_VOID_CTOR" level="MAJOR" enabled="true" /> + <coding_rule class="DM_USELESS_THREAD" level="MAJOR" enabled="true" /> + <coding_rule class="DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED" level="MAJOR" enabled="true" /> + <coding_rule class="DP_DO_INSIDE_DO_PRIVILEGED" level="MAJOR" enabled="true" /> + <coding_rule class="DeclarationOrderCheck" level="INFO" enabled="false"> + <param name="ignoreConstructors" value="false" /> + <param name="ignoreMethods" value="false" /> + <param name="ignoreModifiers" value="false" /> + </coding_rule> + <coding_rule class="DefaultComesLastCheck" level="MAJOR" enabled="true" /> + <coding_rule class="DescendantTokenCheck" level="MAJOR" enabled="false" /> + <coding_rule class="DesignAbstractClassWithoutAnyMethod" level="MAJOR" enabled="false" /> + <coding_rule class="DesignAvoidCatchingGenericException" level="MAJOR" enabled="false" /> + <coding_rule class="DesignAvoidDeeplyNestedIfStmts" level="MAJOR" enabled="false" /> + <coding_rule class="DesignAvoidRethrowingException" level="MAJOR" enabled="true" /> + <coding_rule class="DesignAvoidThrowingNewInstanceOfSameException" level="MAJOR" enabled="false" /> + <coding_rule class="DesignAvoidThrowingNullPointerException" level="MAJOR" enabled="true" /> + <coding_rule class="DesignAvoidThrowingRawExceptionTypes" level="MAJOR" enabled="true" /> + <coding_rule class="DesignClassWithOnlyPrivateConstructorsShouldBeFinal" level="MAJOR" enabled="false" /> + <coding_rule class="DesignCollapsibleIfStatements" level="MINOR" enabled="true" /> + <coding_rule class="DesignCouplingBetweenObjects" level="MAJOR" enabled="false" /> + <coding_rule class="DesignCyclomaticComplexity" level="MAJOR" enabled="false" /> + <coding_rule class="DesignDataClass" level="MAJOR" enabled="false" /> + <coding_rule class="DesignDoNotExtendJavaLangError" level="MAJOR" enabled="false" /> + <coding_rule class="DesignExceptionAsFlowControl" level="MAJOR" enabled="true" /> + <coding_rule class="DesignExcessiveClassLength" level="MAJOR" enabled="false" /> + <coding_rule class="DesignExcessiveImports" level="MAJOR" enabled="false" /> + <coding_rule class="DesignExcessiveMethodLength" level="MAJOR" enabled="false" /> + <coding_rule class="DesignExcessiveParameterList" level="MAJOR" enabled="false" /> + <coding_rule class="DesignExcessivePublicCount" level="MAJOR" enabled="false" /> + <coding_rule class="DesignFinalFieldCouldBeStatic" level="MINOR" enabled="true" /> + <coding_rule class="DesignForExtensionCheck" level="INFO" enabled="true" /> + <coding_rule class="DesignGodClass" level="MAJOR" enabled="false" /> + <coding_rule class="DesignImmutableField" level="MAJOR" enabled="false" /> + <coding_rule class="DesignLawOfDemeter" level="MAJOR" enabled="false" /> + <coding_rule class="DesignLogicInversion" level="MAJOR" enabled="false" /> + <coding_rule class="DesignLoosePackageCoupling" level="MAJOR" enabled="false" /> + <coding_rule class="DesignModifiedCyclomaticComplexity" level="MAJOR" enabled="false" /> + <coding_rule class="DesignNPathComplexity" level="MAJOR" enabled="false" /> + <coding_rule class="DesignNcssConstructorCount" level="MAJOR" enabled="false" /> + <coding_rule class="DesignNcssCount" level="MAJOR" enabled="false" /> + <coding_rule class="DesignNcssMethodCount" level="MAJOR" enabled="true" /> + <coding_rule class="DesignNcssTypeCount" level="MAJOR" enabled="true" /> + <coding_rule class="DesignNoInlineScript" level="MAJOR" enabled="false" /> + <coding_rule class="DesignNoInlineStyleInformation" level="MAJOR" enabled="false" /> + <coding_rule class="DesignNoLongScripts" level="MAJOR" enabled="false" /> + <coding_rule class="DesignNoScriptlets" level="MAJOR" enabled="false" /> + <coding_rule class="DesignSignatureDeclareThrowsException" level="MAJOR" enabled="true" /> + <coding_rule class="DesignSimplifiedTernary" level="MAJOR" enabled="false" /> + <coding_rule class="DesignSimplifyBooleanAssertion" level="MAJOR" enabled="false" /> + <coding_rule class="DesignSimplifyBooleanExpressions" level="MINOR" enabled="false" /> + <coding_rule class="DesignSimplifyBooleanReturns" level="MINOR" enabled="false" /> + <coding_rule class="DesignSimplifyConditional" level="MAJOR" enabled="true" /> + <coding_rule class="DesignSingularField" level="MINOR" enabled="true" /> + <coding_rule class="DesignStdCyclomaticComplexity" level="MAJOR" enabled="false" /> + <coding_rule class="DesignSwitchDensity" level="MAJOR" enabled="false" /> + <coding_rule class="DesignTooManyFields" level="MAJOR" enabled="false" /> + <coding_rule class="DesignTooManyMethods" level="MAJOR" enabled="false" /> + <coding_rule class="DesignUseObjectForClearerAPI" level="MAJOR" enabled="false" /> + <coding_rule class="DesignUseUtilityClass" level="MAJOR" enabled="false" /> + <coding_rule class="DesignUselessOverridingMethod" level="MAJOR" enabled="true" /> + <coding_rule class="DocumentationCommentContent" level="MAJOR" enabled="false" /> + <coding_rule class="DocumentationCommentRequired" level="MAJOR" enabled="false" /> + <coding_rule class="DocumentationCommentSize" level="MAJOR" enabled="false" /> + <coding_rule class="DocumentationUncommentedEmptyConstructor" level="MAJOR" enabled="false" /> + <coding_rule class="DocumentationUncommentedEmptyMethodBody" level="MAJOR" enabled="false" /> + <coding_rule class="EC_ARRAY_AND_NONARRAY" level="CRITICAL" enabled="true" /> + <coding_rule class="EC_BAD_ARRAY_COMPARE" level="CRITICAL" enabled="true" /> + <coding_rule class="EC_INCOMPATIBLE_ARRAY_COMPARE" level="MAJOR" enabled="false" /> + <coding_rule class="EC_NULL_ARG" level="CRITICAL" enabled="true" /> + <coding_rule class="EC_UNRELATED_CLASS_AND_INTERFACE" level="CRITICAL" enabled="true" /> + <coding_rule class="EC_UNRELATED_INTERFACES" level="CRITICAL" enabled="true" /> + <coding_rule class="EC_UNRELATED_TYPES" level="CRITICAL" enabled="true" /> + <coding_rule class="EC_UNRELATED_TYPES_USING_POINTER_EQUALITY" level="CRITICAL" enabled="true" /> + <coding_rule class="EI_EXPOSE_REP" level="MAJOR" enabled="true" /> + <coding_rule class="EI_EXPOSE_REP2" level="MAJOR" enabled="true" /> + <coding_rule class="EI_EXPOSE_STATIC_REP2" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_ABSTRACT_SELF" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_ALWAYS_FALSE" level="BLOCKER" enabled="true" /> + <coding_rule class="EQ_ALWAYS_TRUE" level="BLOCKER" enabled="true" /> + <coding_rule class="EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_COMPARETO_USE_OBJECT_EQUALS" level="CRITICAL" enabled="true" /> + <coding_rule class="EQ_COMPARING_CLASS_NAMES" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_DOESNT_OVERRIDE_EQUALS" level="MAJOR" enabled="false" /> + <coding_rule class="EQ_DONT_DEFINE_EQUALS_FOR_ENUM" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_GETCLASS_AND_CLASS_CONSTANT" level="CRITICAL" enabled="true" /> + <coding_rule class="EQ_OTHER_NO_OBJECT" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_OTHER_USE_OBJECT" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_SELF_NO_OBJECT" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_SELF_USE_OBJECT" level="MAJOR" enabled="true" /> + <coding_rule class="EQ_UNUSUAL" level="MINOR" enabled="true" /> + <coding_rule class="ES_COMPARING_PARAMETER_STRING_WITH_EQ" level="MAJOR" enabled="true" /> + <coding_rule class="ES_COMPARING_STRINGS_WITH_EQ" level="MAJOR" enabled="true" /> + <coding_rule class="ESync_EMPTY_SYNC" level="MAJOR" enabled="true" /> + <coding_rule class="EmptyBlockCheck" level="MAJOR" enabled="false" /> + <coding_rule class="EmptyForInitializerPadCheck" level="INFO" enabled="false" /> + <coding_rule class="EmptyForIteratorPadCheck" level="INFO" enabled="false" /> + <coding_rule class="EmptyStatementCheck" level="INFO" enabled="true" /> + <coding_rule class="EqualsAvoidNullCheck" level="MAJOR" enabled="false"> + <param name="ignoreEqualsIgnoreCase" value="false" /> + </coding_rule> + <coding_rule class="EqualsHashCodeCheck" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneAssignmentInOperand" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAssignmentToNonFinalStatic" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidAccessibilityAlteration" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidAssertAsIdentifier" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneAvoidBranchingStatementAsLastInLoop" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidCallingFinalize" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneAvoidCatchingNPE" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneAvoidCatchingThrowable" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneAvoidDecimalLiteralsInBigDecimalConstructor" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneAvoidDuplicateLiterals" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneAvoidEnumAsIdentifier" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneAvoidFieldNameMatchingMethodName" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidFieldNameMatchingTypeName" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidInstanceofChecksInCatchClause" level="MINOR" enabled="true" /> + <coding_rule class="ErrorProneAvoidLiteralsInIfCondition" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidLosingExceptionInformation" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidMultipleUnaryOperators" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneAvoidUsingOctalValues" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneBadComparison" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneBeanMembersShouldSerialize" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneBrokenNullCheck" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneCallSuperFirst" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneCallSuperLast" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneCheckSkipResult" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneClassCastExceptionWithToArray" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneCloneMethodMustBePublic" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneCloneMethodMustImplementCloneable" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneCloneMethodReturnTypeMustMatchClassName" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneCloneThrowsCloneNotSupportedException" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneCloseResource" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneCompareObjectsWithEquals" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneConstructorCallsOverridableMethod" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneDataflowAnomalyAnalysis" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneDoNotCallGarbageCollectionExplicitly" level="CRITICAL" enabled="false" /> + <coding_rule class="ErrorProneDoNotCallSystemExit" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneDoNotHardCodeSDCard" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneDoNotThrowExceptionInFinally" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneDontImportSun" level="MINOR" enabled="true" /> + <coding_rule class="ErrorProneDontUseFloatTypeForLoopIndices" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneEmptyCatchBlock" level="CRITICAL" enabled="false" /> + <coding_rule class="ErrorProneEmptyFinalizer" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneEmptyFinallyBlock" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneEmptyIfStmt" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneEmptyInitializer" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneEmptyStatementBlock" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneEmptyStatementNotInLoop" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneEmptyStaticInitializer" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneEmptySwitchStatements" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneEmptySynchronizedBlock" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneEmptyTryBlock" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneEmptyWhileStmt" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneEqualsNull" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneFinalizeDoesNotCallSuperFinalize" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneFinalizeOnlyCallsSuperFinalize" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneFinalizeOverloaded" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneFinalizeShouldBeProtected" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneIdempotentOperations" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneImportFromSamePackage" level="MINOR" enabled="false" /> + <coding_rule class="ErrorProneInstantiationToGetClass" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneInvalidSlf4jMessageFormat" level="MINOR" enabled="false" /> + <coding_rule class="ErrorProneJUnitSpelling" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneJUnitStaticSuite" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneJspEncoding" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneJumbledIncrementer" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneLoggerIsNotStaticFinal" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneMethodWithSameNameAsEnclosingClass" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneMisplacedNullCheck" level="CRITICAL" enabled="false" /> + <coding_rule class="ErrorProneMissingBreakInSwitch" level="CRITICAL" enabled="false" /> + <coding_rule class="ErrorProneMissingSerialVersionUID" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneMissingStaticMethodInNonInstantiatableClass" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneMoreThanOneLogger" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneNonCaseLabelInSwitchStatement" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneNonStaticInitializer" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneNullAssignment" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneOverrideBothEqualsAndHashcode" level="CRITICAL" enabled="false" /> + <coding_rule class="ErrorProneProperCloneImplementation" level="CRITICAL" enabled="false" /> + <coding_rule class="ErrorProneProperLogger" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneReturnEmptyArrayRatherThanNull" level="MINOR" enabled="false" /> + <coding_rule class="ErrorProneReturnFromFinallyBlock" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneSimpleDateFormatNeedsLocale" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneSingleMethodSingleton" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneSingletonClassReturningNewInstance" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneStaticEJBFieldShouldBeFinal" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneStringBufferInstantiationWithChar" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneSuspiciousEqualsMethodName" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneSuspiciousHashcodeMethodName" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneSuspiciousOctalEscape" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneTestClassWithoutTestCases" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneUnconditionalIfStatement" level="CRITICAL" enabled="true" /> + <coding_rule class="ErrorProneUnnecessaryBooleanAssertion" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneUnnecessaryCaseChange" level="MINOR" enabled="true" /> + <coding_rule class="ErrorProneUnnecessaryConversionTemporary" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneUnusedNullCheckInEquals" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneUseCorrectExceptionLogging" level="MAJOR" enabled="true" /> + <coding_rule class="ErrorProneUseEqualsToCompareStrings" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneUseLocaleWithCaseConversions" level="MAJOR" enabled="false" /> + <coding_rule class="ErrorProneUseProperClassLoader" level="CRITICAL" enabled="false" /> + <coding_rule class="ErrorProneUselessOperationOnImmutable" level="CRITICAL" enabled="true" /> + <coding_rule class="ExecutableStatementCountCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ExplicitInitializationCheck" level="MAJOR" enabled="false" /> + <coding_rule class="FB_MISSING_EXPECTED_WARNING" level="CRITICAL" enabled="false" /> + <coding_rule class="FB_UNEXPECTED_WARNING" level="CRITICAL" enabled="false" /> + <coding_rule class="FE_FLOATING_POINT_EQUALITY" level="CRITICAL" enabled="true" /> + <coding_rule class="FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER" level="CRITICAL" enabled="true" /> + <coding_rule class="FI_EMPTY" level="MAJOR" enabled="true" /> + <coding_rule class="FI_EXPLICIT_INVOCATION" level="MAJOR" enabled="true" /> + <coding_rule class="FI_FINALIZER_NULLS_FIELDS" level="MAJOR" enabled="true" /> + <coding_rule class="FI_FINALIZER_ONLY_NULLS_FIELDS" level="MAJOR" enabled="true" /> + <coding_rule class="FI_MISSING_SUPER_CALL" level="MAJOR" enabled="true" /> + <coding_rule class="FI_NULLIFY_SUPER" level="CRITICAL" enabled="true" /> + <coding_rule class="FI_PUBLIC_SHOULD_BE_PROTECTED" level="MAJOR" enabled="true" /> + <coding_rule class="FI_USELESS" level="MINOR" enabled="true" /> + <coding_rule class="FL_MATH_USING_FLOAT_PRECISION" level="CRITICAL" enabled="true" /> + <coding_rule class="FallThroughCheck" level="MAJOR" enabled="false" /> + <coding_rule class="FileLengthCheck" level="MAJOR" enabled="false" /> + <coding_rule class="FileTabCharacterCheck" level="MAJOR" enabled="false" /> + <coding_rule class="FinalClassCheck" level="MAJOR" enabled="true" /> + <coding_rule class="FinalLocalVariableCheck" level="INFO" enabled="false" /> + <coding_rule class="FinalParametersCheck" level="INFO" enabled="false" /> + <coding_rule class="GC_UNCHECKED_TYPE_IN_GENERIC_CALL" level="CRITICAL" enabled="true" /> + <coding_rule class="GC_UNRELATED_TYPES" level="CRITICAL" enabled="true" /> + <coding_rule class="GenericWhitespaceCheck" level="MAJOR" enabled="false" /> + <coding_rule class="HE_EQUALS_NO_HASHCODE" level="MAJOR" enabled="true" /> + <coding_rule class="HE_EQUALS_USE_HASHCODE" level="CRITICAL" enabled="true" /> + <coding_rule class="HE_HASHCODE_NO_EQUALS" level="CRITICAL" enabled="true" /> + <coding_rule class="HE_HASHCODE_USE_OBJECT_EQUALS" level="CRITICAL" enabled="true" /> + <coding_rule class="HE_INHERITS_EQUALS_USE_HASHCODE" level="CRITICAL" enabled="true" /> + <coding_rule class="HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS" level="CRITICAL" enabled="true" /> + <coding_rule class="HE_USE_OF_UNHASHABLE_CLASS" level="CRITICAL" enabled="true" /> + <coding_rule class="HRS_REQUEST_PARAMETER_TO_COOKIE" level="MAJOR" enabled="true" /> + <coding_rule class="HRS_REQUEST_PARAMETER_TO_HTTP_HEADER" level="MAJOR" enabled="true" /> + <coding_rule class="HSC_HUGE_SHARED_STRING_CONSTANT" level="CRITICAL" enabled="true" /> + <coding_rule class="HeaderCheck" level="MAJOR" enabled="false" /> + <coding_rule class="HiddenFieldCheck" level="MAJOR" enabled="true" /> + <coding_rule class="HideUtilityClassConstructorCheck" level="MAJOR" enabled="true" /> + <coding_rule class="IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD" level="MAJOR" enabled="true" /> + <coding_rule class="ICAST_BAD_SHIFT_AMOUNT" level="CRITICAL" enabled="true" /> + <coding_rule class="ICAST_IDIV_CAST_TO_DOUBLE" level="CRITICAL" enabled="true" /> + <coding_rule class="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG" level="CRITICAL" enabled="true" /> + <coding_rule class="ICAST_INT_2_LONG_AS_INSTANT" level="CRITICAL" enabled="false" /> + <coding_rule class="ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL" level="CRITICAL" enabled="true" /> + <coding_rule class="ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND" level="CRITICAL" enabled="true" /> + <coding_rule class="ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT" level="CRITICAL" enabled="true" /> + <coding_rule class="IC_INIT_CIRCULARITY" level="CRITICAL" enabled="true" /> + <coding_rule class="IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION" level="MAJOR" enabled="true" /> + <coding_rule class="IIL_ELEMENTS_GET_LENGTH_IN_LOOP" level="MAJOR" enabled="false" /> + <coding_rule class="IIL_PATTERN_COMPILE_IN_LOOP" level="MAJOR" enabled="false" /> + <coding_rule class="IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT" level="MAJOR" enabled="false" /> + <coding_rule class="IIL_PREPARE_STATEMENT_IN_LOOP" level="MAJOR" enabled="false" /> + <coding_rule class="IIO_INEFFICIENT_INDEX_OF" level="MAJOR" enabled="false" /> + <coding_rule class="IIO_INEFFICIENT_LAST_INDEX_OF" level="MAJOR" enabled="false" /> + <coding_rule class="IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD" level="CRITICAL" enabled="true" /> + <coding_rule class="IJU_BAD_SUITE_METHOD" level="CRITICAL" enabled="true" /> + <coding_rule class="IJU_NO_TESTS" level="CRITICAL" enabled="true" /> + <coding_rule class="IJU_SETUP_NO_SUPER" level="CRITICAL" enabled="true" /> + <coding_rule class="IJU_SUITE_NOT_STATIC" level="CRITICAL" enabled="true" /> + <coding_rule class="IJU_TEARDOWN_NO_SUPER" level="CRITICAL" enabled="true" /> + <coding_rule class="IL_CONTAINER_ADDED_TO_ITSELF" level="CRITICAL" enabled="true" /> + <coding_rule class="IL_INFINITE_LOOP" level="CRITICAL" enabled="true" /> + <coding_rule class="IL_INFINITE_RECURSIVE_LOOP" level="CRITICAL" enabled="true" /> + <coding_rule class="IMA_INEFFICIENT_MEMBER_ACCESS" level="MAJOR" enabled="false" /> + <coding_rule class="IMSE_DONT_CATCH_IMSE" level="MAJOR" enabled="true" /> + <coding_rule class="IM_AVERAGE_COMPUTATION_COULD_OVERFLOW" level="CRITICAL" enabled="true" /> + <coding_rule class="IM_BAD_CHECK_FOR_ODD" level="CRITICAL" enabled="true" /> + <coding_rule class="IM_MULTIPLYING_RESULT_OF_IREM" level="CRITICAL" enabled="true" /> + <coding_rule class="INT_BAD_COMPARISON_WITH_INT_VALUE" level="CRITICAL" enabled="false" /> + <coding_rule class="INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE" level="CRITICAL" enabled="true" /> + <coding_rule class="INT_BAD_COMPARISON_WITH_SIGNED_BYTE" level="CRITICAL" enabled="true" /> + <coding_rule class="INT_BAD_REM_BY_1" level="CRITICAL" enabled="true" /> + <coding_rule class="INT_VACUOUS_BIT_OPERATION" level="CRITICAL" enabled="true" /> + <coding_rule class="INT_VACUOUS_COMPARISON" level="CRITICAL" enabled="true" /> + <coding_rule class="IO_APPENDING_TO_OBJECT_OUTPUT_STREAM" level="CRITICAL" enabled="true" /> + <coding_rule class="IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN" level="CRITICAL" enabled="true" /> + <coding_rule class="IS2_INCONSISTENT_SYNC" level="CRITICAL" enabled="true" /> + <coding_rule class="ISC_INSTANTIATE_STATIC_CLASS" level="MAJOR" enabled="true" /> + <coding_rule class="IS_FIELD_NOT_GUARDED" level="CRITICAL" enabled="true" /> + <coding_rule class="ITA_INEFFICIENT_TO_ARRAY" level="CRITICAL" enabled="true" /> + <coding_rule class="IT_NO_SUCH_ELEMENT" level="MINOR" enabled="true" /> + <coding_rule class="IllegalCatchCheck" level="MAJOR" enabled="false" /> + <coding_rule class="IllegalImportCheck" level="MAJOR" enabled="false" /> + <coding_rule class="IllegalInstantiationCheck" level="MAJOR" enabled="false" /> + <coding_rule class="IllegalThrowsCheck" level="MAJOR" enabled="true" /> + <coding_rule class="IllegalTokenCheck" level="MAJOR" enabled="false" /> + <coding_rule class="IllegalTokenTextCheck" level="MAJOR" enabled="false" /> + <coding_rule class="IllegalTypeCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ImportControlCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ImportOrderCheck" level="INFO" enabled="false" /> + <coding_rule class="IndentationCheck" level="INFO" enabled="false" /> + <coding_rule class="InnerAssignmentCheck" level="MAJOR" enabled="true" /> + <coding_rule class="InnerTypeLastCheck" level="MAJOR" enabled="true" /> + <coding_rule class="InterfaceIsTypeCheck" level="MAJOR" enabled="false" /> + <coding_rule class="J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION" level="CRITICAL" enabled="true" /> + <coding_rule class="JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS" level="MINOR" enabled="true" /> + <coding_rule class="JLM_JSR166_LOCK_MONITORENTER" level="CRITICAL" enabled="true" /> + <coding_rule class="JLM_JSR166_UTILCONCURRENT_MONITORENTER" level="CRITICAL" enabled="false" /> + <coding_rule class="JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT" level="CRITICAL" enabled="false" /> + <coding_rule class="JUnitTestCaseCheck" level="MAJOR" enabled="false" /> + <coding_rule class="JavaNCSSCheck" level="MAJOR" enabled="false" /> + <coding_rule class="JavadocMethodCheck" level="MAJOR" enabled="false" /> + <coding_rule class="JavadocPackageCheck" level="MAJOR" enabled="false" /> + <coding_rule class="JavadocStyleCheck" level="MAJOR" enabled="false" /> + <coding_rule class="JavadocTypeCheck" level="MAJOR" enabled="false" /> + <coding_rule class="JavadocVariableCheck" level="MAJOR" enabled="false" /> + <coding_rule class="LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE" level="MAJOR" enabled="false" /> + <coding_rule class="LI_LAZY_INIT_STATIC" level="CRITICAL" enabled="true" /> + <coding_rule class="LI_LAZY_INIT_UPDATE_STATIC" level="CRITICAL" enabled="true" /> + <coding_rule class="LeftCurlyCheck" level="INFO" enabled="false" /> + <coding_rule class="LineLengthCheck" level="MAJOR" enabled="false" /> + <coding_rule class="LocalFinalVariableNameCheck" level="MAJOR" enabled="true" /> + <coding_rule class="LocalVariableNameCheck" level="MAJOR" enabled="true" /> + <coding_rule class="ME_ENUM_FIELD_SETTER" level="MAJOR" enabled="false" /> + <coding_rule class="ME_MUTABLE_ENUM_FIELD" level="MAJOR" enabled="false" /> + <coding_rule class="MF_CLASS_MASKS_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="MF_METHOD_MASKS_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="ML_SYNC_ON_UPDATED_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="MSF_MUTABLE_SERVLET_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="MS_CANNOT_BE_FINAL" level="MAJOR" enabled="true" /> + <coding_rule class="MS_EXPOSE_REP" level="CRITICAL" enabled="true" /> + <coding_rule class="MS_FINAL_PKGPROTECT" level="MAJOR" enabled="true" /> + <coding_rule class="MS_MUTABLE_ARRAY" level="MAJOR" enabled="true" /> + <coding_rule class="MS_MUTABLE_COLLECTION" level="MAJOR" enabled="false" /> + <coding_rule class="MS_MUTABLE_COLLECTION_PKGPROTECT" level="MAJOR" enabled="false" /> + <coding_rule class="MS_MUTABLE_HASHTABLE" level="MAJOR" enabled="true" /> + <coding_rule class="MS_OOI_PKGPROTECT" level="MAJOR" enabled="true" /> + <coding_rule class="MS_PKGPROTECT" level="MAJOR" enabled="true" /> + <coding_rule class="MS_SHOULD_BE_FINAL" level="MAJOR" enabled="true" /> + <coding_rule class="MS_SHOULD_BE_REFACTORED_TO_BE_FINAL" level="CRITICAL" enabled="false" /> + <coding_rule class="MTIA_SUSPECT_SERVLET_INSTANCE_FIELD" level="CRITICAL" enabled="true" /> + <coding_rule class="MTIA_SUSPECT_STRUTS_INSTANCE_FIELD" level="CRITICAL" enabled="true" /> + <coding_rule class="MWN_MISMATCHED_NOTIFY" level="CRITICAL" enabled="true" /> + <coding_rule class="MWN_MISMATCHED_WAIT" level="CRITICAL" enabled="true" /> + <coding_rule class="MagicNumberCheck" level="INFO" enabled="true"> + <param name="ignoreHashCodeMethod" value="false" /> + <param name="ignoreAnnotation" value="false" /> + </coding_rule> + <coding_rule class="MemberNameCheck" level="MAJOR" enabled="true" /> + <coding_rule class="MethodCountCheck" level="MAJOR" enabled="false"> + <param name="maxTotal" value="100" /> + <param name="maxPrivate" value="100" /> + <param name="maxPackage" value="100" /> + <param name="maxProtected" value="100" /> + <param name="maxPublic" value="100" /> + </coding_rule> + <coding_rule class="MethodLengthCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MethodNameCheck" level="MAJOR" enabled="true" /> + <coding_rule class="MethodParamPadCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MethodTypeParameterNameCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MissingCtorCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MissingDeprecatedCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MissingOverrideCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MissingSwitchDefaultCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ModifiedControlVariableCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ModifierOrderCheck" level="INFO" enabled="true" /> + <coding_rule class="MultipleStringLiteralsCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MultipleVariableDeclarationsCheck" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingAvoidSynchronizedAtMethodLevel" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingAvoidThreadGroup" level="CRITICAL" enabled="false" /> + <coding_rule class="MultithreadingAvoidUsingVolatile" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingDoNotUseThreads" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingDontCallThreadRun" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingDoubleCheckedLocking" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingNonThreadSafeSingleton" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingUnsynchronizedStaticDateFormatter" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingUseConcurrentHashMap" level="MAJOR" enabled="false" /> + <coding_rule class="MultithreadingUseNotifyAllInsteadOfNotify" level="MAJOR" enabled="false" /> + <coding_rule class="MutableExceptionCheck" level="MAJOR" enabled="false" /> + <coding_rule class="NM_BAD_EQUAL" level="MAJOR" enabled="false" /> + <coding_rule class="NM_CLASS_NAMING_CONVENTION" level="MAJOR" enabled="false" /> + <coding_rule class="NM_CLASS_NOT_EXCEPTION" level="MAJOR" enabled="true" /> + <coding_rule class="NM_CONFUSING" level="MAJOR" enabled="true" /> + <coding_rule class="NM_FIELD_NAMING_CONVENTION" level="MAJOR" enabled="false" /> + <coding_rule class="NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER" level="MAJOR" enabled="true" /> + <coding_rule class="NM_FUTURE_KEYWORD_USED_AS_MEMBER_IDENTIFIER" level="MAJOR" enabled="true" /> + <coding_rule class="NM_LCASE_HASHCODE" level="MAJOR" enabled="false" /> + <coding_rule class="NM_LCASE_TOSTRING" level="MAJOR" enabled="false" /> + <coding_rule class="NM_METHOD_CONSTRUCTOR_CONFUSION" level="MAJOR" enabled="true" /> + <coding_rule class="NM_METHOD_NAMING_CONVENTION" level="MAJOR" enabled="false" /> + <coding_rule class="NM_SAME_SIMPLE_NAME_AS_INTERFACE" level="MAJOR" enabled="true" /> + <coding_rule class="NM_SAME_SIMPLE_NAME_AS_SUPERCLASS" level="MAJOR" enabled="true" /> + <coding_rule class="NM_VERY_CONFUSING" level="MAJOR" enabled="true" /> + <coding_rule class="NM_VERY_CONFUSING_INTENTIONAL" level="MAJOR" enabled="true" /> + <coding_rule class="NM_WRONG_PACKAGE" level="MAJOR" enabled="true" /> + <coding_rule class="NM_WRONG_PACKAGE_INTENTIONAL" level="MAJOR" enabled="true" /> + <coding_rule class="NN_NAKED_NOTIFY" level="CRITICAL" enabled="true" /> + <coding_rule class="NOISE_FIELD_REFERENCE" level="MAJOR" enabled="false" /> + <coding_rule class="NOISE_METHOD_CALL" level="MAJOR" enabled="false" /> + <coding_rule class="NOISE_NULL_DEREFERENCE" level="MAJOR" enabled="false" /> + <coding_rule class="NOISE_OPERATION" level="MAJOR" enabled="false" /> + <coding_rule class="NO_NOTIFY_NOT_NOTIFYALL" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_ALWAYS_NULL" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_ALWAYS_NULL_EXCEPTION" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_ARGUMENT_MIGHT_BE_NULL" level="MAJOR" enabled="true" /> + <coding_rule class="NP_BOOLEAN_RETURN_NULL" level="MAJOR" enabled="true" /> + <coding_rule class="NP_CLONE_COULD_RETURN_NULL" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_CLOSING_NULL" level="MAJOR" enabled="false" /> + <coding_rule class="NP_DEREFERENCE_OF_READLINE_VALUE" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_GUARANTEED_DEREF" level="BLOCKER" enabled="true" /> + <coding_rule class="NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_IMMEDIATE_DEREFERENCE_OF_READLINE" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_LOAD_OF_KNOWN_NULL_VALUE" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION" level="MAJOR" enabled="false" /> + <coding_rule class="NP_METHOD_RETURN_RELAXING_ANNOTATION" level="MAJOR" enabled="false" /> + <coding_rule class="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" level="CRITICAL" enabled="false" /> + <coding_rule class="NP_NONNULL_PARAM_VIOLATION" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NONNULL_RETURN_VIOLATION" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NULL_INSTANCEOF" level="BLOCKER" enabled="true" /> + <coding_rule class="NP_NULL_ON_SOME_PATH" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NULL_ON_SOME_PATH_EXCEPTION" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NULL_PARAM_DEREF" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_NULL_PARAM_DEREF_NONVIRTUAL" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_OPTIONAL_RETURN_NULL" level="MAJOR" enabled="false" /> + <coding_rule class="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_STORE_INTO_NONNULL_FIELD" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_SYNC_AND_NULL_CHECK_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="NP_TOSTRING_COULD_RETURN_NULL" level="CRITICAL" enabled="true" /> + <coding_rule class="NP_UNWRITTEN_FIELD" level="MAJOR" enabled="false" /> + <coding_rule class="NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> + <coding_rule class="NPathComplexityCheck" level="MAJOR" enabled="false" /> + <coding_rule class="NS_DANGEROUS_NON_SHORT_CIRCUIT" level="CRITICAL" enabled="true" /> + <coding_rule class="NS_NON_SHORT_CIRCUIT" level="MAJOR" enabled="true" /> + <coding_rule class="NeedBracesCheck" level="INFO" enabled="false" /> + <coding_rule class="NestedForDepthCheck" level="MAJOR" enabled="false"> + <param name="max" value="1" /> + </coding_rule> + <coding_rule class="NestedIfDepthCheck" level="MAJOR" enabled="false" /> + <coding_rule class="NestedTryDepthCheck" level="MAJOR" enabled="false" /> + <coding_rule class="NewlineAtEndOfFileCheck" level="INFO" enabled="false" /> + <coding_rule class="NoCloneCheck" level="MAJOR" enabled="false" /> + <coding_rule class="NoFinalizerCheck" level="MAJOR" enabled="false" /> + <coding_rule class="NoWhitespaceAfterCheck" level="INFO" enabled="false" /> + <coding_rule class="NoWhitespaceBeforeCheck" level="INFO" enabled="false" /> + <coding_rule class="OBL_UNSATISFIED_OBLIGATION" level="CRITICAL" enabled="false" /> + <coding_rule class="OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE" level="CRITICAL" enabled="false" /> + <coding_rule class="ODR_OPEN_DATABASE_RESOURCE" level="CRITICAL" enabled="true" /> + <coding_rule class="ODR_OPEN_DATABASE_RESOURCE_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> + <coding_rule class="OS_OPEN_STREAM" level="CRITICAL" enabled="true" /> + <coding_rule class="OS_OPEN_STREAM_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> + <coding_rule class="OneStatementPerLineCheck" level="INFO" enabled="false" /> + <coding_rule class="OperatorWrapCheck" level="INFO" enabled="false" /> + <coding_rule class="OuterTypeFilenameCheck" level="INFO" enabled="false" /> + <coding_rule class="OuterTypeNumberCheck" level="MAJOR" enabled="false" /> + <coding_rule class="PS_PUBLIC_SEMAPHORES" level="CRITICAL" enabled="true" /> + <coding_rule class="PT_ABSOLUTE_PATH_TRAVERSAL" level="CRITICAL" enabled="false" /> + <coding_rule class="PT_RELATIVE_PATH_TRAVERSAL" level="CRITICAL" enabled="false" /> + <coding_rule class="PZLA_PREFER_ZERO_LENGTH_ARRAYS" level="MAJOR" enabled="true" /> + <coding_rule class="PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS" level="CRITICAL" enabled="false" /> + <coding_rule class="PackageAnnotationCheck" level="MAJOR" enabled="false" /> + <coding_rule class="PackageDeclarationCheck" level="MAJOR" enabled="false"> + <param name="ignoreDirectoryName" value="false" /> + </coding_rule> + <coding_rule class="PackageNameCheck" level="MAJOR" enabled="true" /> + <coding_rule class="ParameterAssignmentCheck" level="MAJOR" enabled="true" /> + <coding_rule class="ParameterNameCheck" level="MAJOR" enabled="true" /> + <coding_rule class="ParameterNumberCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ParenPadCheck" level="INFO" enabled="false" /> + <coding_rule class="PerformanceAddEmptyString" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceAppendCharacterWithChar" level="MINOR" enabled="false" /> + <coding_rule class="PerformanceAvoidArrayLoops" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceAvoidInstantiatingObjectsInLoops" level="MINOR" enabled="false" /> + <coding_rule class="PerformanceAvoidUsingShortType" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceBigIntegerInstantiation" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceBooleanInstantiation" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceByteInstantiation" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceConsecutiveAppendsShouldReuse" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceConsecutiveLiteralAppends" level="MINOR" enabled="false" /> + <coding_rule class="PerformanceInefficientEmptyStringCheck" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceInefficientStringBuffering" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceInsufficientStringBufferDeclaration" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceIntegerInstantiation" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceLongInstantiation" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceOptimizableToArrayCall" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceRedundantFieldInitializer" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceShortInstantiation" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceSimplifyStartsWith" level="MINOR" enabled="false" /> + <coding_rule class="PerformanceStringInstantiation" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceStringToString" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceTooFewBranchesForASwitchStatement" level="MINOR" enabled="false" /> + <coding_rule class="PerformanceUnnecessaryWrapperObjectCreation" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceUseArrayListInsteadOfVector" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceUseArraysAsList" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceUseIndexOfChar" level="MAJOR" enabled="true" /> + <coding_rule class="PerformanceUseStringBufferForStringAppends" level="MAJOR" enabled="false" /> + <coding_rule class="PerformanceUseStringBufferLength" level="MINOR" enabled="true" /> + <coding_rule class="PerformanceUselessStringValueOf" level="MINOR" enabled="true" /> + <coding_rule class="QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="QF_QUESTIONABLE_FOR_LOOP" level="CRITICAL" enabled="true" /> + <coding_rule class="RANGE_ARRAY_INDEX" level="MAJOR" enabled="false" /> + <coding_rule class="RANGE_ARRAY_LENGTH" level="MAJOR" enabled="false" /> + <coding_rule class="RANGE_ARRAY_OFFSET" level="MAJOR" enabled="false" /> + <coding_rule class="RANGE_STRING_INDEX" level="MAJOR" enabled="false" /> + <coding_rule class="RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE" level="CRITICAL" enabled="true" /> + <coding_rule class="RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES" level="CRITICAL" enabled="true" /> + <coding_rule class="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" level="CRITICAL" enabled="true" /> + <coding_rule class="RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE" level="CRITICAL" enabled="true" /> + <coding_rule class="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" level="CRITICAL" enabled="true" /> + <coding_rule class="RC_REF_COMPARISON" level="CRITICAL" enabled="true" /> + <coding_rule class="RC_REF_COMPARISON_BAD_PRACTICE" level="MAJOR" enabled="false" /> + <coding_rule class="RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN" level="MAJOR" enabled="false" /> + <coding_rule class="REC_CATCH_EXCEPTION" level="MAJOR" enabled="true" /> + <coding_rule class="RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION" level="CRITICAL" enabled="true" /> + <coding_rule class="RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION" level="CRITICAL" enabled="true" /> + <coding_rule class="RE_POSSIBLE_UNINTENDED_PATTERN" level="CRITICAL" enabled="true" /> + <coding_rule class="RI_REDUNDANT_INTERFACES" level="MAJOR" enabled="true" /> + <coding_rule class="RR_NOT_CHECKED" level="MAJOR" enabled="true" /> + <coding_rule class="RS_READOBJECT_SYNC" level="CRITICAL" enabled="true" /> + <coding_rule class="RU_INVOKE_RUN" level="MAJOR" enabled="true" /> + <coding_rule class="RV_01_TO_INT" level="MAJOR" enabled="true" /> + <coding_rule class="RV_ABSOLUTE_VALUE_OF_HASHCODE" level="CRITICAL" enabled="true" /> + <coding_rule class="RV_ABSOLUTE_VALUE_OF_RANDOM_INT" level="CRITICAL" enabled="true" /> + <coding_rule class="RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE" level="CRITICAL" enabled="false" /> + <coding_rule class="RV_CHECK_FOR_POSITIVE_INDEXOF" level="MINOR" enabled="true" /> + <coding_rule class="RV_DONT_JUST_NULL_CHECK_READLINE" level="MAJOR" enabled="true" /> + <coding_rule class="RV_EXCEPTION_NOT_THROWN" level="CRITICAL" enabled="true" /> + <coding_rule class="RV_NEGATING_RESULT_OF_COMPARETO" level="CRITICAL" enabled="false" /> + <coding_rule class="RV_REM_OF_HASHCODE" level="CRITICAL" enabled="true" /> + <coding_rule class="RV_REM_OF_RANDOM_INT" level="CRITICAL" enabled="true" /> + <coding_rule class="RV_RETURN_VALUE_IGNORED" level="MINOR" enabled="true" /> + <coding_rule class="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" level="MAJOR" enabled="true" /> + <coding_rule class="RV_RETURN_VALUE_IGNORED_INFERRED" level="CRITICAL" enabled="false" /> + <coding_rule class="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" level="MAJOR" enabled="false" /> + <coding_rule class="RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED" level="MAJOR" enabled="false" /> + <coding_rule class="RedundantImportCheck" level="INFO" enabled="false" /> + <coding_rule class="RedundantModifierCheck" level="INFO" enabled="true" /> + <coding_rule class="RedundantThrowsCheck" level="INFO" enabled="true" /> + <coding_rule class="RegexpCheck" level="MAJOR" enabled="false" /> + <coding_rule class="RegexpHeaderCheck" level="MAJOR" enabled="false" /> + <coding_rule class="RegexpMultilineCheck" level="MAJOR" enabled="false"> + <param name="message" value="TODO item found" /> + <param name="minimum" value="0" /> + <param name="maximum" value="0" /> + <param name="format" value="TODO" /> + <param name="ignoreCase" value="true" /> + </coding_rule> + <coding_rule class="RegexpSinglelineCheck" level="MAJOR" enabled="false"> + <param name="message" value="TODO item found" /> + <param name="minimum" value="0" /> + <param name="maximum" value="0" /> + <param name="format" value="TODO" /> + <param name="ignoreCase" value="true" /> + </coding_rule> + <coding_rule class="RegexpSinglelineJavaCheck" level="MAJOR" enabled="false"> + <param name="message" value="TODO item found" /> + <param name="minimum" value="0" /> + <param name="maximum" value="0" /> + <param name="ignoreComments" value="false" /> + <param name="format" value="TODO" /> + <param name="ignoreCase" value="true" /> + </coding_rule> + <coding_rule class="RequireThisCheck" level="MAJOR" enabled="false" /> + <coding_rule class="ReturnCountCheck" level="MAJOR" enabled="false" /> + <coding_rule class="RightCurlyCheck" level="INFO" enabled="false" /> + <coding_rule class="RpC_REPEATED_CONDITIONAL_TEST" level="MAJOR" enabled="true" /> + <coding_rule class="SA_FIELD_DOUBLE_ASSIGNMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="SA_FIELD_SELF_ASSIGNMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="SA_FIELD_SELF_COMPARISON" level="CRITICAL" enabled="true" /> + <coding_rule class="SA_FIELD_SELF_COMPUTATION" level="CRITICAL" enabled="true" /> + <coding_rule class="SA_LOCAL_DOUBLE_ASSIGNMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="SA_LOCAL_SELF_ASSIGNMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD" level="CRITICAL" enabled="false" /> + <coding_rule class="SA_LOCAL_SELF_COMPARISON" level="CRITICAL" enabled="true" /> + <coding_rule class="SA_LOCAL_SELF_COMPUTATION" level="CRITICAL" enabled="true" /> + <coding_rule class="SBSC_USE_STRINGBUFFER_CONCATENATION" level="CRITICAL" enabled="true" /> + <coding_rule class="SC_START_IN_CTOR" level="CRITICAL" enabled="true" /> + <coding_rule class="SE_BAD_FIELD" level="MINOR" enabled="false" /> + <coding_rule class="SE_BAD_FIELD_INNER_CLASS" level="MINOR" enabled="true" /> + <coding_rule class="SE_BAD_FIELD_STORE" level="CRITICAL" enabled="true" /> + <coding_rule class="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE" level="MAJOR" enabled="true" /> + <coding_rule class="SE_INNER_CLASS" level="MAJOR" enabled="true" /> + <coding_rule class="SE_METHOD_MUST_BE_PRIVATE" level="MAJOR" enabled="true" /> + <coding_rule class="SE_NONFINAL_SERIALVERSIONID" level="CRITICAL" enabled="true" /> + <coding_rule class="SE_NONLONG_SERIALVERSIONID" level="MAJOR" enabled="true" /> + <coding_rule class="SE_NONSTATIC_SERIALVERSIONID" level="MAJOR" enabled="true" /> + <coding_rule class="SE_NO_SERIALVERSIONID" level="MAJOR" enabled="true" /> + <coding_rule class="SE_NO_SUITABLE_CONSTRUCTOR" level="MAJOR" enabled="true" /> + <coding_rule class="SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION" level="MAJOR" enabled="true" /> + <coding_rule class="SE_PRIVATE_READ_RESOLVE_NOT_INHERITED" level="MAJOR" enabled="true" /> + <coding_rule class="SE_READ_RESOLVE_IS_STATIC" level="MAJOR" enabled="true" /> + <coding_rule class="SE_READ_RESOLVE_MUST_RETURN_OBJECT" level="MAJOR" enabled="true" /> + <coding_rule class="SE_TRANSIENT_FIELD_NOT_RESTORED" level="MAJOR" enabled="true" /> + <coding_rule class="SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS" level="MAJOR" enabled="true" /> + <coding_rule class="SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH" level="MAJOR" enabled="false" /> + <coding_rule class="SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW" level="MAJOR" enabled="false" /> + <coding_rule class="SF_SWITCH_FALLTHROUGH" level="MAJOR" enabled="false" /> + <coding_rule class="SF_SWITCH_NO_DEFAULT" level="MAJOR" enabled="false" /> + <coding_rule class="SIC_INNER_SHOULD_BE_STATIC" level="MAJOR" enabled="true" /> + <coding_rule class="SIC_INNER_SHOULD_BE_STATIC_ANON" level="MAJOR" enabled="true" /> + <coding_rule class="SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS" level="MAJOR" enabled="true" /> + <coding_rule class="SIC_THREADLOCAL_DEADLY_EMBRACE" level="MAJOR" enabled="false" /> + <coding_rule class="SIO_SUPERFLUOUS_INSTANCEOF" level="CRITICAL" enabled="true" /> + <coding_rule class="SI_INSTANCE_BEFORE_FINALS_ASSIGNED" level="CRITICAL" enabled="true" /> + <coding_rule class="SP_SPIN_ON_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="SQL_BAD_PREPARED_STATEMENT_ACCESS" level="CRITICAL" enabled="true" /> + <coding_rule class="SQL_BAD_RESULTSET_ACCESS" level="CRITICAL" enabled="true" /> + <coding_rule class="SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE" level="CRITICAL" enabled="true" /> + <coding_rule class="SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" level="CRITICAL" enabled="true" /> + <coding_rule class="SR_NOT_CHECKED" level="MAJOR" enabled="true" /> + <coding_rule class="SS_SHOULD_BE_STATIC" level="MAJOR" enabled="true" /> + <coding_rule class="STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE" level="CRITICAL" enabled="true" /> + <coding_rule class="STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE" level="CRITICAL" enabled="true" /> + <coding_rule class="STCAL_STATIC_CALENDAR_INSTANCE" level="CRITICAL" enabled="true" /> + <coding_rule class="STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE" level="CRITICAL" enabled="true" /> + <coding_rule class="STI_INTERRUPTED_ON_CURRENTTHREAD" level="CRITICAL" enabled="true" /> + <coding_rule class="STI_INTERRUPTED_ON_UNKNOWNTHREAD" level="CRITICAL" enabled="true" /> + <coding_rule class="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" level="CRITICAL" enabled="true" /> + <coding_rule class="SWL_SLEEP_WITH_LOCK_HELD" level="CRITICAL" enabled="true" /> + <coding_rule class="SW_SWING_METHODS_INVOKED_IN_SWING_THREAD" level="MAJOR" enabled="true" /> + <coding_rule class="SecurityIframeMissingSrcAttribute" level="MAJOR" enabled="false" /> + <coding_rule class="SecurityNoUnsanitizedJSPExpression" level="MAJOR" enabled="false" /> + <coding_rule class="SimplifyBooleanExpressionCheck" level="MAJOR" enabled="true" /> + <coding_rule class="SimplifyBooleanReturnCheck" level="MAJOR" enabled="true" /> + <coding_rule class="StaticVariableNameCheck" level="MAJOR" enabled="true" /> + <coding_rule class="StrictDuplicateCodeCheck" level="MAJOR" enabled="false" /> + <coding_rule class="StringLiteralEqualityCheck" level="MAJOR" enabled="true" /> + <coding_rule class="SuperCloneCheck" level="MAJOR" enabled="false" /> + <coding_rule class="SuperFinalizeCheck" level="MAJOR" enabled="false" /> + <coding_rule class="SuppressWarningsCheck" level="MAJOR" enabled="false" /> + <coding_rule class="TLW_TWO_LOCK_WAIT" level="MAJOR" enabled="true" /> + <coding_rule class="TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED" level="CRITICAL" enabled="true" /> + <coding_rule class="TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS" level="CRITICAL" enabled="false" /> + <coding_rule class="TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK" level="CRITICAL" enabled="true" /> + <coding_rule class="TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK" level="CRITICAL" enabled="true" /> + <coding_rule class="TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK" level="CRITICAL" enabled="true" /> + <coding_rule class="TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK" level="CRITICAL" enabled="true" /> + <coding_rule class="TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED" level="CRITICAL" enabled="true" /> + <coding_rule class="TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED" level="CRITICAL" enabled="false" /> + <coding_rule class="ThrowsCountCheck" level="MAJOR" enabled="false" /> + <coding_rule class="TodoCommentCheck" level="INFO" enabled="false" /> + <coding_rule class="TrailingCommentCheck" level="INFO" enabled="false" /> + <coding_rule class="TranslationCheck" level="INFO" enabled="false" /> + <coding_rule class="TypeNameCheck" level="MAJOR" enabled="false" /> + <coding_rule class="TypecastParenPadCheck" level="MAJOR" enabled="false" /> + <coding_rule class="UCF_USELESS_CONTROL_FLOW" level="CRITICAL" enabled="true" /> + <coding_rule class="UCF_USELESS_CONTROL_FLOW_NEXT_LINE" level="CRITICAL" enabled="true" /> + <coding_rule class="UC_USELESS_CONDITION" level="MAJOR" enabled="false" /> + <coding_rule class="UC_USELESS_CONDITION_TYPE" level="MAJOR" enabled="false" /> + <coding_rule class="UC_USELESS_OBJECT" level="MAJOR" enabled="false" /> + <coding_rule class="UC_USELESS_OBJECT_STACK" level="MAJOR" enabled="false" /> + <coding_rule class="UC_USELESS_VOID_METHOD" level="MAJOR" enabled="false" /> + <coding_rule class="UG_SYNC_SET_UNSYNC_GET" level="MAJOR" enabled="true" /> + <coding_rule class="UI_INHERITANCE_UNSAFE_GETRESOURCE" level="MAJOR" enabled="true" /> + <coding_rule class="UL_UNRELEASED_LOCK" level="CRITICAL" enabled="true" /> + <coding_rule class="UL_UNRELEASED_LOCK_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> + <coding_rule class="UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS" level="CRITICAL" enabled="true" /> + <coding_rule class="UM_UNNECESSARY_MATH" level="CRITICAL" enabled="true" /> + <coding_rule class="UPM_UNCALLED_PRIVATE_METHOD" level="CRITICAL" enabled="true" /> + <coding_rule class="URF_UNREAD_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> + <coding_rule class="UR_UNINIT_READ" level="MAJOR" enabled="true" /> + <coding_rule class="UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR" level="MAJOR" enabled="false" /> + <coding_rule class="USM_USELESS_ABSTRACT_METHOD" level="MAJOR" enabled="false" /> + <coding_rule class="USM_USELESS_SUBCLASS_METHOD" level="MAJOR" enabled="false" /> + <coding_rule class="UUF_UNUSED_FIELD" level="MAJOR" enabled="true" /> + <coding_rule class="UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> + <coding_rule class="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" level="MAJOR" enabled="false" /> + <coding_rule class="UWF_NULL_FIELD" level="CRITICAL" enabled="true" /> + <coding_rule class="UWF_UNWRITTEN_FIELD" level="MAJOR" enabled="false" /> + <coding_rule class="UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> + <coding_rule class="UW_UNCOND_WAIT" level="MAJOR" enabled="true" /> + <coding_rule class="UncommentedMainCheck" level="MAJOR" enabled="false" /> + <coding_rule class="UnnecessaryParenthesesCheck" level="INFO" enabled="false" /> + <coding_rule class="UnusedImportsCheck" level="INFO" enabled="true"> + <param name="processJavadoc" value="false" /> + </coding_rule> + <coding_rule class="UpperEllCheck" level="INFO" enabled="false" /> + <coding_rule class="VA_FORMAT_STRING_BAD_ARGUMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION" level="CRITICAL" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION_FROM_ARRAY" level="MAJOR" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION_TO_BOOLEAN" level="MAJOR" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED" level="MAJOR" enabled="false" /> + <coding_rule class="VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED" level="MAJOR" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_ILLEGAL" level="CRITICAL" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_MISSING_ARGUMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT" level="CRITICAL" enabled="true" /> + <coding_rule class="VA_FORMAT_STRING_USES_NEWLINE" level="CRITICAL" enabled="false" /> + <coding_rule class="VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG" level="CRITICAL" enabled="true" /> + <coding_rule class="VO_VOLATILE_INCREMENT" level="CRITICAL" enabled="false" /> + <coding_rule class="VO_VOLATILE_REFERENCE_TO_ARRAY" level="MAJOR" enabled="true" /> + <coding_rule class="VR_UNRESOLVABLE_REFERENCE" level="MAJOR" enabled="false" /> + <coding_rule class="VisibilityModifierCheck" level="MAJOR" enabled="true" /> + <coding_rule class="WA_AWAIT_NOT_IN_LOOP" level="CRITICAL" enabled="true" /> + <coding_rule class="WA_NOT_IN_LOOP" level="CRITICAL" enabled="true" /> + <coding_rule class="WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL" level="CRITICAL" enabled="true" /> + <coding_rule class="WMI_WRONG_MAP_ITERATOR" level="CRITICAL" enabled="true" /> + <coding_rule class="WS_WRITEOBJECT_SYNC" level="CRITICAL" enabled="true" /> + <coding_rule class="WhitespaceAfterCheck" level="INFO" enabled="false" /> + <coding_rule class="WhitespaceAroundCheck" level="INFO" enabled="false" /> + <coding_rule class="WriteTagCheck" level="INFO" enabled="false" /> + <coding_rule class="XFB_XML_FACTORY_BYPASS" level="CRITICAL" enabled="true" /> + <coding_rule class="XSS_REQUEST_PARAMETER_TO_JSP_WRITER" level="CRITICAL" enabled="true" /> + <coding_rule class="XSS_REQUEST_PARAMETER_TO_SEND_ERROR" level="CRITICAL" enabled="true" /> + <coding_rule class="XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER" level="CRITICAL" enabled="true" /> + </profile> + </profiles> + <list size="0" /> + </component> <component name="CheckStyle-IDEA"> <option name="configuration"> <map> @@ -15,6 +953,25 @@ </map> </option> </component> + <component name="CheckstyleConfigurable"> + <option name="suppFilterFilename" value="" /> + <option name="suppCommentFilter" value="false" /> + <option name="offComment" value="CHECKSTYLE\:OFF" /> + <option name="onComment" value="CHECKSTYLE\:ON" /> + <option name="checkFormat" value=".*" /> + <option name="messageFormat" value="" /> + <option name="checkCPP" value="true" /> + <option name="checkC" value="true" /> + <option name="suppNearbyCommentFilter" value="false" /> + <option name="snCommentFormat" value="SUPPRESS CHECKSTYLE (\w+)" /> + <option name="snCheckFormat" value="$1" /> + <option name="snMessageFormat" value="" /> + <option name="snInfluenceFormat" value="0" /> + <option name="snCheckCPP" value="true" /> + <option name="snCheckC" value="true" /> + <option name="pathToUserRulesConfiguration" value="" /> + <option name="pathToJarWithRules" value="" /> + </component> <component name="ClientPropertiesManager"> <properties class="javax.swing.AbstractButton"> <property name="hideActionText" class="java.lang.Boolean" /> @@ -74,6 +1031,12 @@ <component name="Encoding"> <file url="PROJECT" charset="UTF-8" /> </component> + <component name="FindBugsConfigurable"> + <option name="make" value="true" /> + <option name="effort" value="default" /> + <option name="priority" value="Medium" /> + <option name="excludeFilter" value="" /> + </component> <component name="GradleLocalSettings"> <option name="modificationStamps"> <map> @@ -165,6 +1128,7 @@ <option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" /> </inspection_tool> </profile> + <option name="PROJECT_PROFILE" value="Erik's Default" /> <option name="USE_PROJECT_PROFILE" value="false" /> <version value="1.0" /> </component> @@ -191,6 +1155,19 @@ </MavenGeneralSettings> </option> </component> + <component name="PDMPlugin"> + <option name="customRuleSets"> + <list> + <option value="K:\java\mobibot\config\pmd.xml" /> + </list> + </option> + <option name="options"> + <map> + <entry key="Encoding" value="" /> + <entry key="Target JDK" value="1.8" /> + </map> + </option> + </component> <component name="Palette2"> <group name="Swing"> <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> @@ -456,6 +1433,9 @@ <method /> </configuration> </component> + <component name="SuppressionsComponent"> + <option name="suppComments" value="[]" /> + </component> <component name="VcsDirectoryMappings"> <mapping directory="" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" /> @@ -470,18 +1450,18 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spotbugs:spotbugs-annotations:3.1.11"> + <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.11/5380cad296d13aae1dff9d55c72a42e10a62b345/spotbugs-annotations-3.1.11.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta1/c017c1646dc8e74a2eff232298ba31151874d797/spotbugs-annotations-4.0.0-beta1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/3.1.11/9d24d67fd213fb77c4b3e6c5e2458a93666cca1b/spotbugs-annotations-3.1.11-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta1/339b39795ac29f89077c55ba1c7f33cf0207f036/spotbugs-annotations-4.0.0-beta1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.6/1b8707299c34406ed0ba40bbf8513352ac4765c9/compiler-0.9.6.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> @@ -524,13 +1504,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.0/75e65a94c7663e232de3f5b9cb68004cb449cf3/rome-1.12.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:3.14.0"> + <library name="Gradle: com.squareup.okhttp3:okhttp:3.14.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.0/c3a9efd10f26a802da2c47dbe54a8d4c44a6019f/okhttp-3.14.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.1/67612a22d4b8f33c55263b188bf5a72774d06d18/okhttp-3.14.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.0/775ec91f22324e24657fb832a9fc47b90738b25d/okhttp-3.14.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.1/f8319293dad84c8ac79282b26d835f6edb9f637d/okhttp-3.14.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okio:okio:1.17.2"> @@ -542,22 +1522,22 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/331ecaeba2fd87c06a0766e8ebe718e1e294f27d/okio-1.17.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.retrofit2:converter-gson:2.4.0"> + <library name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.4.0/15d7790ee311d961379c51b00aba12d5967cb7ea/converter-gson-2.4.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.4.0/5da04ef58bc122d13185fd4317ad7bfbc554d881/converter-gson-2.4.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/e33b9ee015798ca86bc1ce78b4a17aa9f9a53e63/converter-gson-2.5.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.retrofit2:retrofit:2.4.0"> + <library name="Gradle: com.squareup.retrofit2:retrofit:2.5.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.4.0/fc4aa382632bfaa7be7b41579efba41d5a71ecf3/retrofit-2.4.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.4.0/451207353948708405a08287e2a315c3f23553fe/retrofit-2.4.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/5c17035548a41ec60f00291163d36596a23ddd1a/retrofit-2.5.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: commons-cli:commons-cli:1.4"> @@ -578,13 +1558,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/961dc27eabbe71bf32478baffe0e1be915ce7689/commons-net-3.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.aksingh:owm-japis:2.5.2.3"> + <library name="Gradle: net.aksingh:owm-japis:2.5.3.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.3/c6e92f68f97a9956375f020937230d5ed226c3a3/owm-japis-2.5.2.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.2.3/bc50cd23c52e26569365961029bc5f44eff0684d/owm-japis-2.5.2.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/bddd258f8737de9f96014e44e19168629ccb113a/owm-japis-2.5.3.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: net.objecthunter:exp4j:0.4.8"> @@ -661,52 +1641,48 @@ </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71" type="kotlin.common"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.30" type="kotlin.common"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.71/ba18ca1aa0e40eb6f1865b324af2f4cbb691c1ec/kotlin-stdlib-common-1.2.71.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.30/kotlin-stdlib-common-1.3.30.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/bf0edcf669e446e0d903a0681190d1e3df969ac4/kotlin-stdlib-jdk7-1.3.30.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.71/ff62b6a76e3200cac3d7df0859d7665fe6d54955/kotlin-stdlib-common-1.2.71-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/2b561cb97f3fbf555f328e9c87ec625b12ee6c17/kotlin-stdlib-jdk7-1.3.30-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.71/4ce93f539e2133f172f1167291a911f83400a5d0/kotlin-stdlib-jdk7-1.2.71.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/459999be0d6ac844dd3d2ca85c2daf14deb5f7f0/kotlin-stdlib-jdk8-1.3.30.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.71/1e054c1ef9afdfc7c79bc09dd5eed35791cef7a2/kotlin-stdlib-jdk7-1.2.71-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/269416550424b90ce1c27727d0d5c65b6bb730c9/kotlin-stdlib-jdk8-1.3.30-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.30"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.71/5470d1f752cd342edb77e1062bac07e838d2cea4/kotlin-stdlib-jdk8-1.2.71.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.3.30/kotlin-stdlib-1.3.30.jar!/" /> </CLASSES> <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.71/f3016e6e03b6a6adb777b403d856e0f2c36ea646/kotlin-stdlib-jdk8-1.2.71-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.71"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.71/d9717625bb3c731561251f8dd2c67a1011d6764c/kotlin-stdlib-1.2.71.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.71/2e8041d08283cbbb58ad131617486fc6449f6c85/kotlin-stdlib-1.2.71-sources.jar!/" /> - </SOURCES> + <SOURCES /> </library> <library name="Gradle: org.jetbrains:annotations:13.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> diff --git a/version.mustache b/version.mustache index 860bfe4..5314f35 100644 --- a/version.mustache +++ b/version.mustache @@ -12,15 +12,15 @@ import java.time.*; * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> */ public final class {{className}} { - public final static String PROJECT = "{{project}}"; - public final static LocalDateTime BUILDDATE = + public static final String PROJECT = "{{project}}"; + public static final LocalDateTime BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); - public final static int MAJOR = {{major}}; - public final static int MINOR = {{minor}}; - public final static int PATCH = {{patch}}; - public final static String PRERELEASE = "{{preRelease}}"; - public final static String BUILDMETA = "{{buildMeta}}"; - public final static String VERSION = "{{version}}"; + public static final int MAJOR = {{major}}; + public static final int MINOR = {{minor}}; + public static final int PATCH = {{patch}}; + public static final String PRERELEASE = "{{preRelease}}"; + public static final String BUILDMETA = "{{buildMeta}}"; + public static final String VERSION = "{{version}}"; /** * Disables the default constructor. From da85ecfeb2ebca6ce8d557b3342fec294ff2bdc2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:15:54 -0700 Subject: [PATCH 284/842] Semver 1.2.0 is not out yet. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d7053d3..b73199b 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' def isRelease = 'release' in gradle.startParameter.taskNames -final def semverProcessor = "net.thauvin.erik:semver:1.2.0" +final def semverProcessor = "net.thauvin.erik:semver:1.1.1" mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' From 82f4cf7cfbc4ae95a589e225eb3c746d438286ec Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:26:43 -0700 Subject: [PATCH 285/842] Fix for CI. --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 18 +++++++++--------- version.properties | 5 +++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 94e0a12..6d5acc0 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -12,15 +12,15 @@ import java.time.*; * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> */ public final class ReleaseInfo { - public final static String PROJECT = "mobibot"; - public final static LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1555742153981L), ZoneId.systemDefault()); - public final static int MAJOR = 0; - public final static int MINOR = 7; - public final static int PATCH = 3; - public final static String PRERELEASE = "beta"; - public final static String BUILDMETA = "306"; - public final static String VERSION = "0.7.3-beta+306"; + public static final String PROJECT = "mobibot"; + public static final LocalDateTime BUILDDATE = + LocalDateTime.ofInstant(Instant.ofEpochMilli(1556357432557L), ZoneId.systemDefault()); + public static final int MAJOR = 0; + public static final int MINOR = 7; + public static final int PATCH = 3; + public static final String PRERELEASE = "beta"; + public static final String BUILDMETA = "430"; + public static final String VERSION = "0.7.3-beta+430"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 09b80cc..d144833 100644 --- a/version.properties +++ b/version.properties @@ -1,8 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Apr 19 23:35:53 PDT 2019 -version.buildmeta=306 +#Sat Apr 27 02:30:31 PDT 2019 +version.buildmeta=430 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot +version.semver=0.7.3-beta+430 From af090e794067b6ba48f9223d689d68485f4a5333 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:27:03 -0700 Subject: [PATCH 286/842] Cleanup. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b73199b..143dc03 100644 --- a/build.gradle +++ b/build.gradle @@ -130,7 +130,7 @@ sonarqube { jacocoTestReport { reports { - html.enabled true + html.enabled = true xml.enabled = true } } From 8976686286943a9c15e479f9fee189a63e12a59d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 03:52:14 -0700 Subject: [PATCH 287/842] Temporarily using mobibot.mustache to fix CI until Semver 1.2.0 is out. --- build.gradle | 1 - mobibot.mustache | 31 +++++++++++++++++++ .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 ++-- .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- version.properties | 6 ++-- 5 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 mobibot.mustache diff --git a/build.gradle b/build.gradle index 143dc03..a06d9c9 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,6 @@ defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' -def isRelease = 'release' in gradle.startParameter.taskNames final def semverProcessor = "net.thauvin.erik:semver:1.1.1" mainClassName = packageName + '.Mobibot' diff --git a/mobibot.mustache b/mobibot.mustache new file mode 100644 index 0000000..5314f35 --- /dev/null +++ b/mobibot.mustache @@ -0,0 +1,31 @@ +/* + * This file is automatically generated. + * Do not modify! -- ALL CHANGES WILL BE ERASED! + */ +package {{packageName}}; + +import java.time.*; + +/** + * Provides semantic version information. + * + * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> + */ +public final class {{className}} { + public static final String PROJECT = "{{project}}"; + public static final LocalDateTime BUILDDATE = + LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); + public static final int MAJOR = {{major}}; + public static final int MINOR = {{minor}}; + public static final int PATCH = {{patch}}; + public static final String PRERELEASE = "{{preRelease}}"; + public static final String BUILDMETA = "{{buildMeta}}"; + public static final String VERSION = "{{version}}"; + + /** + * Disables the default constructor. + */ + private {{className}}() { + throw new UnsupportedOperationException("Illegal constructor call."); + } +} diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 6d5acc0..3d814ec 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1556357432557L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1556362138167L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "430"; - public static final String VERSION = "0.7.3-beta+430"; + public static final String BUILDMETA = "438"; + public static final String VERSION = "0.7.3-beta+438"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 0caaa6c..93c75c2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -99,7 +99,7 @@ import java.util.StringTokenizer; * @since 1.0 */ @SuppressWarnings("WeakerAccess") -@Version(properties = "version.properties", className = "ReleaseInfo") +@Version(properties = "version.properties", template = "mobibot.mustache", className = "ReleaseInfo") public class Mobibot extends PircBot { // The default port. diff --git a/version.properties b/version.properties index d144833..055074f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Apr 27 02:30:31 PDT 2019 -version.buildmeta=430 +#Sat Apr 27 03:48:57 PDT 2019 +version.buildmeta=438 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+430 +version.semver=0.7.3-beta+438 From 43e31ddf63edae041fca19cd4422b59c053a522f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 27 Apr 2019 20:30:12 -0700 Subject: [PATCH 288/842] Added twitterNotification method. --- .../net/thauvin/erik/mobibot/Mobibot.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 93c75c2..3ad36c0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -818,21 +818,7 @@ public class Mobibot extends PircBot { public final void joinChannel() { joinChannel(ircChannel); - if (twitterModule.isEnabled() && Utils.isValidString(twitterHandle)) { - new Thread(() -> { - try { - twitterModule.post( - twitterHandle, - getName() + ReleaseInfo.VERSION + " has joined " + ircChannel, - true); - } catch (ModuleException e) { - if (logger.isWarnEnabled()) { - logger.warn( - "Failed to notify " + twitterHandle + " of joining " + ircChannel + " on Twitter.", e); - } - } - }).start(); - } + twitterNotification("has joined " + ircChannel); } /** @@ -1273,6 +1259,7 @@ public class Mobibot extends PircBot { System.exit(0); } else if (Commands.DIE_CMD.equals(cmd) && isOp(sender)) { send(ircChannel, sender + " has just signed my death sentence."); + twitterNotification("killed by " + sender + " on " + ircChannel); saveEntries(true); sleep(3); quitServer("The Bot Is Out There!"); @@ -1593,6 +1580,28 @@ public class Mobibot extends PircBot { } } + /** + * Send a notification to the registered Twitter handle. + * + * @param msg The twitter message. + */ + final void twitterNotification(final String msg) { + if (twitterModule.isEnabled() && Utils.isValidString(twitterHandle)) { + new Thread(() -> { + try { + twitterModule.post( + twitterHandle, + getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true); + } catch (ModuleException e) { + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to notify @" + twitterHandle + ": " + msg, e); + } + } + }).start(); + } + } + /** * Responds with the users on a channel. * From 19898d2c1c6e2fb408d4d54dc49df1edf3c06aa5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 May 2019 00:08:05 -0700 Subject: [PATCH 289/842] Code cleanup. --- config/pmd.xml | 3 ++- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 5 ++--- .../java/net/thauvin/erik/mobibot/entries/EntriesMgr.java | 5 +++-- .../java/net/thauvin/erik/mobibot/entries/EntryComment.java | 2 +- .../java/net/thauvin/erik/mobibot/entries/EntryLink.java | 2 +- .../net/thauvin/erik/mobibot/modules/ModuleException.java | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/config/pmd.xml b/config/pmd.xml index 547f745..6e60806 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -4,6 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>Erik's Ruleset</description> + <!-- BEST PRACTICES --> <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/> <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/> <rule ref="category/java/bestpractices.xml/CheckResultSet"/> @@ -103,7 +104,7 @@ <rule ref="category/java/documentation.xml/UncommentedEmptyMethodBody"/> - <!-- ERRORPRONE --> + <!-- ERROR PRONE --> <rule ref="category/java/errorprone.xml/AssignmentInOperand"> <properties> <property name="allowWhile" value="true"/> diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 3ad36c0..c018f81 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -99,7 +99,7 @@ import java.util.StringTokenizer; * @since 1.0 */ @SuppressWarnings("WeakerAccess") -@Version(properties = "version.properties", template = "mobibot.mustache", className = "ReleaseInfo") +@Version(properties = "version.properties", className = "ReleaseInfo") public class Mobibot extends PircBot { // The default port. @@ -1653,10 +1653,9 @@ public class Mobibot extends PircBot { * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ private void viewResponse(final String sender, final String args, final boolean isPrivate) { - String lcArgs = args.toLowerCase(Constants.LOCALE); - if (!entries.isEmpty()) { final int max = entries.size(); + String lcArgs = args.toLowerCase(Constants.LOCALE); int i = 0; if ((lcArgs.length() <= 0) && (max > MAX_ENTRIES)) { diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index fc8b401..38b9e6d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -54,6 +54,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; import java.util.List; /** @@ -99,7 +100,7 @@ public final class EntriesMgr { * @throws IOException If the file was not found or could not be read. * @throws FeedException If an error occurred while reading the feed. */ - public static void loadBacklogs(final String file, final List<String> history) + public static void loadBacklogs(final String file, final Collection<String> history) throws IOException, FeedException { history.clear(); @@ -131,7 +132,7 @@ public final class EntriesMgr { * @throws FeedException If an error occurred while reading the feed. */ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public static String loadEntries(final String file, final String channel, final List<EntryLink> entries) + public static String loadEntries(final String file, final String channel, final Collection<EntryLink> entries) throws IOException, FeedException { entries.clear(); diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java index 825ed3c..1dcf4eb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java @@ -44,7 +44,7 @@ import java.time.LocalDateTime; */ public class EntryComment implements Serializable { // The serial version UID. - static final long serialVersionUID = 6957415292233553224L; + static final long serialVersionUID = 1L; // The creation date. private final LocalDateTime date = LocalDateTime.now(); diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index f96871b..485bbd5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -52,7 +52,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class EntryLink implements Serializable { // The serial version UID. - static final long serialVersionUID = 3676245542270899086L; + static final long serialVersionUID = 1L; // The link's comments private final List<EntryComment> comments = new CopyOnWriteArrayList<>(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 4e143ef..88f5af3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -45,7 +45,7 @@ import java.util.regex.Pattern; * @since 1.0 */ public class ModuleException extends Exception { - private static final long serialVersionUID = -3036774290621088107L; + private static final long serialVersionUID = 1L; private final String debugMessage; private final Pattern urlPattern = Pattern.compile("(https?://\\S+)(\\?\\S+)"); From 47e7ab38dd7955257b7bfe3653333b7c3b33614e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 May 2019 00:08:21 -0700 Subject: [PATCH 290/842] Dependencies update. --- build.gradle | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index a06d9c9..5d47df2 100644 --- a/build.gradle +++ b/build.gradle @@ -6,9 +6,9 @@ plugins { id 'java' id 'pmd' id "com.github.ben-manes.versions" version "0.21.0" - id "com.github.spotbugs" version "1.7.1" - id "net.thauvin.erik.gradle.semver" version "1.0.0" - id "org.sonarqube" version "2.7" + id "com.github.spotbugs" version "2.0.0" + id "net.thauvin.erik.gradle.semver" version "1.0.2" + id "org.sonarqube" version "2.7.1" } @@ -18,7 +18,7 @@ defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' -final def semverProcessor = "net.thauvin.erik:semver:1.1.1" +final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' @@ -28,7 +28,7 @@ compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${proje ext { log4j = '2.11.2' - spotbugs_annotations = '4.0.0-beta1' + spotbugs_annotations = '4.0.0-beta2' } repositories { @@ -51,16 +51,16 @@ dependencies { compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.14.1' + compile 'com.squareup.okhttp3:okhttp:3.14.2' compile 'com.rometools:rome:1.12.0' compile 'org.json:json:20180813' - compile 'org.jsoup:jsoup:1.11.3' + compile 'org.jsoup:jsoup:1.12.1' compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.7' - compile 'net.thauvin.erik:pinboard-poster:1.0.0' + compile 'net.thauvin.erik:pinboard-poster:1.0.1' compile 'net.aksingh:owm-japis:2.5.3.0' @@ -68,7 +68,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.12.2' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' - spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.3.sb' + spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.5' compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" @@ -87,7 +87,7 @@ pmd { compileJava { dependsOn('incrementBuildMeta') - options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' + options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation', "-Asemver.project.dir=$projectDir"] } javadoc { @@ -104,7 +104,9 @@ jar { } clean { - delete deployDir + doLast { + project.delete(fileTree(deployDir)) + } } run { From c276c8f42c28aa5915d6806c3a88a209373c6827 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 May 2019 00:08:52 -0700 Subject: [PATCH 291/842] Version 0.7.3-beta+450 --- .idea/modules/mobibot.iml | 14 +++---- mobibot.ipr | 40 ++++++++++--------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +-- version.properties | 6 +-- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 4d50b07..e41bece 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+345" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+447" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -24,28 +24,28 @@ <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.11.2" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20180813" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.11.3" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta2" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.30" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.31" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.30" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> diff --git a/mobibot.ipr b/mobibot.ipr index 12cb55c..8839df2 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1450,13 +1450,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta1"> + <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta1/c017c1646dc8e74a2eff232298ba31151874d797/spotbugs-annotations-4.0.0-beta1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta2/8d6fda3f5e668802f52b8f6632c12c3b5feb596f/spotbugs-annotations-4.0.0-beta2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta1/339b39795ac29f89077c55ba1c7f33cf0207f036/spotbugs-annotations-4.0.0-beta1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta2/33e9a2747415c2f856cba49beabd886a21481431/spotbugs-annotations-4.0.0-beta2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6"> @@ -1504,13 +1504,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.0/75e65a94c7663e232de3f5b9cb68004cb449cf3/rome-1.12.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:3.14.1"> + <library name="Gradle: com.squareup.okhttp3:okhttp:3.14.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.1/67612a22d4b8f33c55263b188bf5a72774d06d18/okhttp-3.14.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.1/f8319293dad84c8ac79282b26d835f6edb9f637d/okhttp-3.14.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/155f20f50fc3754b139c115cccd6fe752af655ed/okhttp-3.14.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okio:okio:1.17.2"> @@ -1576,13 +1576,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/8d86f148ff1f0d5b624eae9bb0882198ab5cd07/exp4j-0.4.8-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: net.thauvin.erik:pinboard-poster:1.0.0"> + <library name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.0/pinboard-poster-1.0.0.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.0/pinboard-poster-1.0.0-sources.jar!/" /> + <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: net.thauvin.erik:semver:1.2.0"> @@ -1648,12 +1648,14 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.30" type="kotlin.common"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31" type="kotlin.common"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.30/kotlin-stdlib-common-1.3.30.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.31/20c34a04ea25cb1ef0139598bd67c764562cb170/kotlin-stdlib-common-1.3.31.jar!/" /> </CLASSES> <JAVADOC /> - <SOURCES /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.31/958c401b129f64de74af4b11a0894693edfa168d/kotlin-stdlib-common-1.3.31-sources.jar!/" /> + </SOURCES> </library> <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30"> <CLASSES> @@ -1673,12 +1675,14 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/269416550424b90ce1c27727d0d5c65b6bb730c9/kotlin-stdlib-jdk8-1.3.30-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.30"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.31"> <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.3.30/kotlin-stdlib-1.3.30.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.31/11289d20fd95ae219333f3456072be9f081c30cc/kotlin-stdlib-1.3.31.jar!/" /> </CLASSES> <JAVADOC /> - <SOURCES /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.31/d16c788b755be19bf8a7eace113755519a1d7182/kotlin-stdlib-1.3.31-sources.jar!/" /> + </SOURCES> </library> <library name="Gradle: org.jetbrains:annotations:13.0"> <CLASSES> @@ -1698,13 +1702,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180813/60753006d96d5fc85b61b0897f3326b5c4e1c4f0/json-20180813-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jsoup:jsoup:1.11.3"> + <library name="Gradle: org.jsoup:jsoup:1.12.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.3/36da09a8f68484523fa2aaa100399d612b247d67/jsoup-1.11.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.12.1/55819a28fc834c2f2bcf4dcdb278524dc3cf088f/jsoup-1.12.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.11.3/871302b15d8cee9bfb393c4f1d0386b17646d8d1/jsoup-1.11.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.12.1/1ec738903bf781d7218455e314c6dc9831a2f9a6/jsoup-1.12.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.slf4j:slf4j-api:1.7.25"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 3d814ec..3fd837b 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1556362138167L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1558940690905L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "438"; - public static final String VERSION = "0.7.3-beta+438"; + public static final String BUILDMETA = "450"; + public static final String VERSION = "0.7.3-beta+450"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 055074f..53e0c75 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Apr 27 03:48:57 PDT 2019 -version.buildmeta=438 +#Mon May 27 00:04:45 PDT 2019 +version.buildmeta=450 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+438 +version.semver=0.7.3-beta+450 From 2dc206dadbf457b375c73024b28b0f2d29042867 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 15:24:01 -0700 Subject: [PATCH 292/842] Gradle 5.5.1 update. --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- gradlew.bat | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f4d7b2b..4b7e1f3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b0d6d0a..8e25e6c 100755 --- a/gradlew +++ b/gradlew @@ -7,7 +7,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, diff --git a/gradlew.bat b/gradlew.bat index 9991c50..9618d8d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, From be9a1464408d691c6fc61d1fae6831d3702097ee Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 15:27:06 -0700 Subject: [PATCH 293/842] Added toString method. --- src/main/java/net/thauvin/erik/mobibot/msg/Message.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 8acf324..290d3c9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -181,4 +181,10 @@ public class Message { public void setPrivate(final boolean isPrivate) { this.isPrivate = isPrivate; } + + @Override + public String toString() { + return "Message{" + "color='" + color + '\'' + ", isError=" + isError + ", isNotice=" + isNotice + + ", isPrivate=" + isPrivate + ", msg='" + msg + '\'' + '}'; + } } From 2d3d13f36074d6ee01f9dd650d46211c8e78dcae Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 15:56:48 -0700 Subject: [PATCH 294/842] Updated dependencies. --- build.gradle | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 5d47df2..d89fbb1 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { id 'pmd' id "com.github.ben-manes.versions" version "0.21.0" id "com.github.spotbugs" version "2.0.0" - id "net.thauvin.erik.gradle.semver" version "1.0.2" + id "net.thauvin.erik.gradle.semver" version "1.0.4" id "org.sonarqube" version "2.7.1" } @@ -21,14 +21,10 @@ final def deployDir = 'deploy' final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' - -compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") - ext { - log4j = '2.11.2' - spotbugs_annotations = '4.0.0-beta2' + log4j = '2.12.0' + spotbugs_annotations = '4.0.0-beta3' } repositories { @@ -51,9 +47,9 @@ dependencies { compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:3.14.2' + compile 'com.squareup.okhttp3:okhttp:4.0.1' - compile 'com.rometools:rome:1.12.0' + compile 'com.rometools:rome:1.12.1' compile 'org.json:json:20180813' compile 'org.jsoup:jsoup:1.12.1' @@ -68,7 +64,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.12.2' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' - spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.5' + spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.6' compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" @@ -85,9 +81,17 @@ pmd { ruleSets = [] } +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") + if (JavaVersion.current().isJava12Compatible()) { + options.compilerArgs += [ "-Asemver.project.dir=$projectDir" ] + } +} + compileJava { dependsOn('incrementBuildMeta') - options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation', "-Asemver.project.dir=$projectDir"] + options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation'] } javadoc { From 106accdcf5da8c92bfe26906e03a39ca55c37fbc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 15:57:28 -0700 Subject: [PATCH 295/842] FindBugs false positive suppression. --- .../java/net/thauvin/erik/mobibot/modules/ModuleException.java | 2 ++ src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 88f5af3..5c516ea 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import okhttp3.HttpUrl; import java.util.regex.Matcher; @@ -97,6 +98,7 @@ public class ModuleException extends Exception { * * @return The sanitized message. */ + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?") String getSanitizedMessage() { if (hasCause()) { final String causeMessage = getCause().getMessage(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 2053d88..41c5cce 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; @@ -82,6 +83,7 @@ public final class StockQuote extends ThreadedModule { * @return The {@link Message} array containing the stock quote. * @throws ModuleException If an errors occurs. */ + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?") static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { if (!Utils.isValidString(apiKey)) { throw new ModuleException(Utils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); From b8539a72954450fb3897a81ab6bb51e601ede7e8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 15:58:00 -0700 Subject: [PATCH 296/842] Handled invalid city ID. --- .../erik/mobibot/modules/Weather2.java | 19 +++++++++++++++---- .../erik/mobibot/modules/Weather2Test.java | 6 ++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 4f2adac..6899ae6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -44,6 +44,7 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; +import okhttp3.HttpUrl; import org.jibble.pircbot.Colors; import java.util.ArrayList; @@ -167,10 +168,20 @@ public class Weather2 extends ThreadedModule { messages.add(new NoticeMessage(condition.toString())); } } - messages.add(new NoticeMessage("https://openweathermap.org/city/" - + cwd.getCityId(), Colors.GREEN)); - } + if (cwd.getCityId() != null) { + if (cwd.getCityId() > 0) { + messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(), + Colors.GREEN)); + } else { + final HttpUrl url = + HttpUrl.parse("https://openweathermap.org/find").newBuilder().addQueryParameter( + "q", city).build(); + messages.add( + new NoticeMessage(url.toString(), Colors.GREEN)); + } + } + } } catch (APIException | NullPointerException e) { throw new ModuleException("getWeather(" + query + ')', "Unable to perform weather lookup.", e); } @@ -194,7 +205,7 @@ public class Weather2 extends ThreadedModule { bot.send(sender, "For example:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") - + ". Zip codes are supported in most countries."); + + ". Zip codes are supported in most countries."); } /** diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index 6f549dc..faf75bc 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -51,11 +51,13 @@ public class Weather2Test extends LocalProperties { @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") @Test public void testWeather() throws ModuleException { - List<Message> messages = Weather2.getWeather("98204", - LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); + List<Message> messages = Weather2.getWeather("98204", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett"); + assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Search").endsWith("98204"); + messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getMessage()).as("is UK").contains("UK"); + assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Code").endsWith("4298960"); try { Weather2.getWeather("test", ""); From 9a95980e4cd8de4ed6caae2f5c128b81df293423 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 16:20:52 -0700 Subject: [PATCH 297/842] Cleanup deploy tasks. --- build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d89fbb1..8058dbc 100644 --- a/build.gradle +++ b/build.gradle @@ -162,11 +162,13 @@ task copyToDeploy(type: Copy) { } task copyToDeployLib(type: Copy) { - from configurations.compile + from(configurations.runtime) { + exclude 'annotations-*.jar' + } into deployDir + '/lib' } -task deploy(dependsOn: ['build', 'jar']) { +task deploy(dependsOn: ['clean', 'build', 'jar']) { description = 'Copies all needed files to the ${deployDir} directory.' group = 'Publishing' outputs.dir deployDir From b73e9597c1bd7fcb3bffc672bea4d73146fa0f5e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 16:22:38 -0700 Subject: [PATCH 298/842] Version 0.7.3-beta+468 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 3fd837b..10e57f9 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1558940690905L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1564355149782L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "450"; - public static final String VERSION = "0.7.3-beta+450"; + public static final String BUILDMETA = "468"; + public static final String VERSION = "0.7.3-beta+468"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 53e0c75..a2c77a2 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon May 27 00:04:45 PDT 2019 -version.buildmeta=450 +#Sun Jul 28 16:05:43 PDT 2019 +version.buildmeta=468 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+450 +version.semver=0.7.3-beta+468 From 351f117d38279de648d65e5431d1edd2d37917f6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 17:00:27 -0700 Subject: [PATCH 299/842] Fixed CI issues. --- .travis.yml | 1 + build.gradle | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 018f0e5..f8946bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: java +dist: trusty env: global: diff --git a/build.gradle b/build.gradle index 8058dbc..3bf8c83 100644 --- a/build.gradle +++ b/build.gradle @@ -84,9 +84,7 @@ pmd { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") - if (JavaVersion.current().isJava12Compatible()) { - options.compilerArgs += [ "-Asemver.project.dir=$projectDir" ] - } + options.compilerArgs += [ "-Asemver.project.dir=$projectDir" ] } compileJava { From 34c7adddfffda8fbba46491fb65d7ea0e21bdbcd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 23:13:47 -0700 Subject: [PATCH 300/842] Added country to invalid city ID search. --- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/Weather2Test.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 6899ae6..a166eba 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -169,14 +169,14 @@ public class Weather2 extends ThreadedModule { } } - if (cwd.getCityId() != null) { + if (cwd.hasCityId() && cwd.getCityId() != null) { if (cwd.getCityId() > 0) { messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(), Colors.GREEN)); } else { final HttpUrl url = HttpUrl.parse("https://openweathermap.org/find").newBuilder().addQueryParameter( - "q", city).build(); + "q", city + ',' + country).build(); messages.add( new NoticeMessage(url.toString(), Colors.GREEN)); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index faf75bc..b347fc2 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -53,7 +53,7 @@ public class Weather2Test extends LocalProperties { public void testWeather() throws ModuleException { List<Message> messages = Weather2.getWeather("98204", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett"); - assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Search").endsWith("98204"); + assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Search").endsWith("98204%2CUS"); messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getMessage()).as("is UK").contains("UK"); From 6a1b3976edfcabc218a528c8cff2250eb115f1c2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Jul 2019 23:14:19 -0700 Subject: [PATCH 301/842] Version 0.7.3-beta+471 --- .idea/modules/mobibot.iml | 24 ++++---- mobibot.ipr | 60 +++++++++---------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- version.properties | 6 +- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index e41bece..880e624 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+447" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+469" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -19,33 +19,33 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.11.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.11.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.12.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.12.0" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.0.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.1" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20180813" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta3" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.1" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.2.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.31" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.40" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.40" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> diff --git a/mobibot.ipr b/mobibot.ipr index 8839df2..ccfa327 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1450,13 +1450,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta2"> + <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta3"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta2/8d6fda3f5e668802f52b8f6632c12c3b5feb596f/spotbugs-annotations-4.0.0-beta2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta3/65ce8bfb2bcc1abd1adc5816fc19c687cdad77b5/spotbugs-annotations-4.0.0-beta3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta2/33e9a2747415c2f856cba49beabd886a21481431/spotbugs-annotations-4.0.0-beta2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta3/672866c39247ef05484380d72024dec31ca0cf82/spotbugs-annotations-4.0.0-beta3-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6"> @@ -1486,40 +1486,40 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/c5b4c491aecb72e7c32a78da0b5c6b9cda8dee0f/gson-2.8.5-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome-utils:1.12.0"> + <library name="Gradle: com.rometools:rome-utils:1.12.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.0/c714531d168f733f1ebed7ee8f6ebd255bac52dc/rome-utils-1.12.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.1/e14b9757402f0971fabe245f8a3ee7c889151f26/rome-utils-1.12.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.0/4087c6c81afefde27b14143ff4123bf36c0bec88/rome-utils-1.12.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.1/4823407506599821643d09519ba2193f520c259a/rome-utils-1.12.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.12.0"> + <library name="Gradle: com.rometools:rome:1.12.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.0/a7057d89b443792277662380cc9f020f1707927b/rome-1.12.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.1/e9038b34b001007b2a1f3823c532f3524222075f/rome-1.12.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.0/75e65a94c7663e232de3f5b9cb68004cb449cf3/rome-1.12.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.1/46920b2dae8ec033a2948a07f58cfc133276070b/rome-1.12.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:3.14.2"> + <library name="Gradle: com.squareup.okhttp3:okhttp:4.0.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.0.1/4e606430c80c69b744d19c6ca1b269b68e297040/okhttp-4.0.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/155f20f50fc3754b139c115cccd6fe752af655ed/okhttp-3.14.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.0.1/54d824f82c8e609588bd101056e362e2e2421bd3/okhttp-4.0.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okio:okio:1.17.2"> + <library name="Gradle: com.squareup.okio:okio:2.2.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.2.2/36f483536153f15339a8b48d508e22be7c9c531a/okio-2.2.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/331ecaeba2fd87c06a0766e8ebe718e1e294f27d/okio-1.17.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.2.2/6904b2c4ae9d612465f6fdd45ddda28aebb45921/okio-2.2.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0"> @@ -1603,31 +1603,31 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache-extras.beanshell/bsh/2.0b6/76a65f674e8f2df409934824c49ca338987c63ae/bsh-2.0b6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-api:2.11.2"> + <library name="Gradle: org.apache.logging.log4j:log4j-api:2.12.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.2/f5e9a2ffca496057d6891a3de65128efc636e26e/log4j-api-2.11.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.0/8b50b487ac0c0bff1af79d56c6454ea68f40c150/log4j-api-2.12.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.2/a11bdc0e8f95da31527b4f34f1a35c23e197498d/log4j-api-2.11.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.0/b52dca271c11244ac235fc56e5f59f5cd22b7876/log4j-api-2.12.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-core:2.11.2"> + <library name="Gradle: org.apache.logging.log4j:log4j-core:2.12.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.2/6c2fb3f5b7cd27504726aef1b674b542a0c9cf53/log4j-core-2.11.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.0/1723837573e4c5dbc8840f9f6e8f79b245b94bb/log4j-core-2.12.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.2/338d2678d341eedbc5273f983766dc65ffed13c2/log4j-core-2.11.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.0/259b7766eec03263c1c1a1f40484b624108b01c9/log4j-core-2.12.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.11.2"> + <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.2/4d44e4edc4a7fb39f09b95b09f560a15976fa1ba/log4j-slf4j-impl-2.11.2.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.0/7f44f5201f79d6f7010a1515699a5f6c40e49cb/log4j-slf4j-impl-2.12.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.2/6bb10559db88828dac3627de26974035a5dd4ddb/log4j-slf4j-impl-2.11.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.0/428316adc5e470af7032beeb53d84b3b879254ed/log4j-slf4j-impl-2.12.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.assertj:assertj-core:3.12.2"> @@ -1648,13 +1648,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31" type="kotlin.common"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.40" type="kotlin.common"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.31/20c34a04ea25cb1ef0139598bd67c764562cb170/kotlin-stdlib-common-1.3.31.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.40/ff8f3da514fc2877d1303d55e22d6da8156c29fb/kotlin-stdlib-common-1.3.40.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.31/958c401b129f64de74af4b11a0894693edfa168d/kotlin-stdlib-common-1.3.31-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.40/682cd7821872d1a3c1e8a3e6bdaabd17f2b71004/kotlin-stdlib-common-1.3.40-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30"> @@ -1675,13 +1675,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/269416550424b90ce1c27727d0d5c65b6bb730c9/kotlin-stdlib-jdk8-1.3.30-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.31"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.40"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.31/11289d20fd95ae219333f3456072be9f081c30cc/kotlin-stdlib-1.3.31.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.40/b8a521c687329303778548e2f09b0ba5b2665236/kotlin-stdlib-1.3.40.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.31/d16c788b755be19bf8a7eace113755519a1d7182/kotlin-stdlib-1.3.31-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.40/7af6fd6b9293e80fc7408493a764858a7f66b022/kotlin-stdlib-1.3.40-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jetbrains:annotations:13.0"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 10e57f9..4ca5b99 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1564355149782L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1564380207330L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "468"; - public static final String VERSION = "0.7.3-beta+468"; + public static final String BUILDMETA = "471"; + public static final String VERSION = "0.7.3-beta+471"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index a2c77a2..b7a1eea 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Jul 28 16:05:43 PDT 2019 -version.buildmeta=468 +#Sun Jul 28 23:03:26 PDT 2019 +version.buildmeta=471 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+468 +version.semver=0.7.3-beta+471 From 65b61aa5a3d5e3e1a3baa5e55b76ad08bb5899bc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 29 Jul 2019 10:48:19 -0700 Subject: [PATCH 302/842] Added TellMessage tests. --- .../erik/mobibot/tell/TellMessage.java | 2 +- .../erik/mobibot/tell/TellMessageTest.java | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java index cc73893..9d39e22 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java @@ -91,7 +91,7 @@ public class TellMessage implements Serializable { } /** - * Returns the state of the queue flag. + * Returns the queued date/time. * * @return <code>true</code> if the message is queued. */ diff --git a/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java b/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java new file mode 100644 index 0000000..da3666d --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java @@ -0,0 +1,81 @@ +/* + * TellMessageTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.tell; + +import org.testng.annotations.Test; + +import java.time.Duration; +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The <code>TellMessageTest</code> class. + * + * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @created 2019-07-29 + * @since 1.0 + */ +public class TellMessageTest { + private final String message = "Test message."; + private final String recipient = "recipient"; + private final String sender = "sender"; + private final TellMessage tellMessage = new TellMessage(sender, recipient, message); + + private boolean isValidDate(final LocalDateTime date) { + return Duration.between(date, LocalDateTime.now()).toMinutes() < 1; + } + + @Test + void testTellMessage() { + assertThat(tellMessage.getSender()).as(sender).isEqualTo(sender); + assertThat(tellMessage.getRecipient()).as(recipient).isEqualTo(recipient); + assertThat(tellMessage.getMessage()).as(message).isEqualTo(message); + assertThat(isValidDate(tellMessage.getQueued())).as("queued is valid date/time").isTrue(); + + assertThat(tellMessage.isMatch(sender)).as("match sender").isTrue(); + assertThat(tellMessage.isMatch(recipient)).as("match recipient").isTrue(); + assertThat(tellMessage.isMatch("foo")).as("foo is no match").isFalse(); + + assertThat(tellMessage.isMatchId(tellMessage.getId())).as("is match ID").isTrue(); + + tellMessage.setIsReceived(); + assertThat(tellMessage.isReceived()).as("is received").isTrue(); + assertThat(isValidDate(tellMessage.getReceived())).as("received is valid date/time").isTrue(); + + tellMessage.setIsNotified(); + assertThat(tellMessage.isNotified()).as("is notified").isTrue(); + } + +} + From 65dcbcc4e3abd98a94e31bc2ee5d7e7fd89b1fe8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 29 Jul 2019 10:49:19 -0700 Subject: [PATCH 303/842] Reworked exception testing. --- .../mobibot/modules/GoogleSearchTest.java | 25 ++++++------------- .../erik/mobibot/modules/StockQuoteTest.java | 17 ++++--------- .../erik/mobibot/modules/Weather2Test.java | 24 +++++++++--------- 3 files changed, 24 insertions(+), 42 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 88e005c..ad25467 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -39,6 +39,7 @@ import org.testng.annotations.Test; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * The <code>GoogleSearchTest</code> class. @@ -68,26 +69,14 @@ public class GoogleSearchTest extends LocalProperties { assertThat(messages).as("aapl results not empty").isNotEmpty(); assertThat(messages.get(0).getMessage()).as("found apple").containsIgnoringCase("apple"); - try { - GoogleSearch.searchGoogle("test", "", "apiKey"); - } catch (Exception e) { - assertThat(e).as("no API key").isInstanceOf(ModuleException.class); - assertThat(e).as("no API key exception has no cause").hasNoCause(); - } + assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "", "apiKey")).as("no API key").isInstanceOf( + ModuleException.class).hasNoCause(); - try { - GoogleSearch.searchGoogle("test", "apiKey", ""); - } catch (Exception e) { - assertThat(e).as("no CSE API key").isInstanceOf(ModuleException.class); - assertThat(e).as("no CSE API key exception has no cause").hasNoCause(); - } + assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "apiKey", "")).as("no CSE API key").isInstanceOf( + ModuleException.class).hasNoCause(); - try { - GoogleSearch.searchGoogle("", "apikey", "apiKey"); - } catch (Exception e) { - assertThat(e).as("no query").isInstanceOf(ModuleException.class); - assertThat(e).as("no query exception has no cause").hasNoCause(); - } + assertThatThrownBy(() -> GoogleSearch.searchGoogle("", "apikey", "apiKey")).as("no query").isInstanceOf( + ModuleException.class).hasNoCause(); } catch (ModuleException e) { // Avoid displaying api keys in CI logs. if ("true".equals(System.getenv("CI"))) { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 488de37..4b6e665 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -39,6 +39,7 @@ import org.testng.annotations.Test; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * The <code>StockQuoteTest</code> class. @@ -62,19 +63,11 @@ public class StockQuoteTest extends LocalProperties { messages = StockQuote.getQuote("012", apiKey); assertThat(messages.get(0).isError()).as("invalid symbol error").isTrue(); - try { - StockQuote.getQuote("test", ""); - } catch (Exception e) { - assertThat(e).as("no API key").isInstanceOf(ModuleException.class); - assertThat(e).as("no API key exception has no cause").hasNoCause(); - } + assertThatThrownBy(() -> StockQuote.getQuote("test", "")).as("no API key").isInstanceOf( + ModuleException.class).hasNoCause(); - try { - StockQuote.getQuote("", "apikey"); - } catch (Exception e) { - assertThat(e).as("no symbol").isInstanceOf(ModuleException.class); - assertThat(e).as("no symbol exception has no cause").hasNoCause(); - } + assertThatThrownBy(() -> StockQuote.getQuote("", "apikey")).as("no symbol").isInstanceOf( + ModuleException.class).hasNoCause(); } catch (ModuleException e) { // Avoid displaying api keys in CI logs. diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index b347fc2..b26e7ad 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -33,12 +33,14 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.aksingh.owmjapis.api.APIException; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * The <code>Weather2Test</code> class. @@ -59,19 +61,17 @@ public class Weather2Test extends LocalProperties { assertThat(messages.get(0).getMessage()).as("is UK").contains("UK"); assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Code").endsWith("4298960"); - try { - Weather2.getWeather("test", ""); - } catch (Exception e) { - assertThat(e).as("no API key").isInstanceOf(ModuleException.class); - assertThat(e).as("no API key exception has no cause").hasNoCause(); - } + assertThatThrownBy( + () -> Weather2.getWeather("Montpellier, FR", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP))).as( + "Montpellier not found").hasCauseInstanceOf(APIException.class); + + assertThatThrownBy( + () -> Weather2.getWeather("test", "")).as( + "no API key").isInstanceOf(ModuleException.class).hasNoCause(); + + messages = Weather2.getWeather("", "apikey"); + assertThat(messages.get(0).isError()).as("No api key.").isTrue(); - try { - Weather2.getWeather("", "apikey"); - } catch (Exception e) { - assertThat(e).as("no query").isInstanceOf(ModuleException.class); - assertThat(e).as("no query exception has no cause").hasNoCause(); - } } @Test From 0e04a40a767a24dcb3d900eb3212c0c169620168 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 29 Jul 2019 10:50:09 -0700 Subject: [PATCH 304/842] Cleanup tests. --- .../net/thauvin/erik/mobibot/modules/CalcTest.java | 10 +++++----- .../erik/mobibot/modules/CurrencyConverterTest.java | 5 +++++ .../thauvin/erik/mobibot/modules/WordTimeTest.java | 12 ++++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java index efb3f81..0d5de03 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java @@ -44,11 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class CalcTest { - @Test - public void testCalcImpl() { - AbstractModuleTest.testAbstractModule(new Calc()); - } - @Test public void testCalc() { assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = 2"); @@ -56,4 +51,9 @@ public class CalcTest { assertThat(Calc.calc("pi+π+e+φ")).as("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = 10.62"); assertThat(Calc.calc("one + one")).as("calc(one + one)").startsWith("No idea."); } + + @Test + public void testCalcImpl() { + AbstractModuleTest.testAbstractModule(new Calc()); + } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index b255c9a..a437408 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -32,6 +32,8 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.msg.ErrorMessage; import org.testng.annotations.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -55,9 +57,12 @@ public class CurrencyConverterTest { } @Test + @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") public void testConvertCurrency() throws ModuleException { assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMessage()) .as("100 USD to EUR").startsWith("100.00 USD = "); + assertThat(CurrencyConverter.convertCurrency("100 USD to USD")) + .as("100 USD to USD").isInstanceOf(ErrorMessage.class); assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).isNotice()) .as(CurrencyConverter.CURRENCY_RATES_KEYWORD + " is notice").isTrue(); assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).getMessage()) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java index de5cd1f..b7cfc43 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java @@ -44,15 +44,15 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class WordTimeTest { - @Test - public void testWorldTimeImpl() { - AbstractModuleTest.testAbstractModule(new Lookup()); - } - @Test public void testWorldTime() { - assertThat(WorldTime.worldTime("PST").getMessage()).as("PST").startsWith("The time is "); + assertThat(WorldTime.worldTime("PST").getMessage()).as("PST").endsWith("Los Angeles"); assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue(); assertThat(WorldTime.worldTime("BEATS").getMessage()).as("BEATS").contains("@"); } + + @Test + public void testWorldTimeImpl() { + AbstractModuleTest.testAbstractModule(new Lookup()); + } } From 167670c86e9e306e963ccea47c09f8a9e6154388 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 29 Jul 2019 11:19:08 -0700 Subject: [PATCH 305/842] Cleanup. --- .../thauvin/erik/mobibot/tell/TellMessageTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java b/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java index da3666d..28f0fec 100644 --- a/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java @@ -36,6 +36,7 @@ import org.testng.annotations.Test; import java.time.Duration; import java.time.LocalDateTime; +import java.time.temporal.Temporal; import static org.assertj.core.api.Assertions.assertThat; @@ -47,17 +48,17 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class TellMessageTest { - private final String message = "Test message."; - private final String recipient = "recipient"; - private final String sender = "sender"; - private final TellMessage tellMessage = new TellMessage(sender, recipient, message); - - private boolean isValidDate(final LocalDateTime date) { + private boolean isValidDate(final Temporal date) { return Duration.between(date, LocalDateTime.now()).toMinutes() < 1; } @Test void testTellMessage() { + final String message = "Test message."; + final String recipient = "recipient"; + final String sender = "sender"; + final TellMessage tellMessage = new TellMessage(sender, recipient, message); + assertThat(tellMessage.getSender()).as(sender).isEqualTo(sender); assertThat(tellMessage.getRecipient()).as(recipient).isEqualTo(recipient); assertThat(tellMessage.getMessage()).as(message).isEqualTo(message); @@ -76,6 +77,5 @@ public class TellMessageTest { tellMessage.setIsNotified(); assertThat(tellMessage.isNotified()).as("is notified").isTrue(); } - } From 349ed0e6714492ec92cd2e8255685fb380fae6c5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 29 Jul 2019 11:32:50 -0700 Subject: [PATCH 306/842] Cleanup. --- .../java/net/thauvin/erik/mobibot/modules/Weather2Test.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index b26e7ad..bfc80eb 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -54,11 +54,11 @@ public class Weather2Test extends LocalProperties { @Test public void testWeather() throws ModuleException { List<Message> messages = Weather2.getWeather("98204", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett"); + assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett").contains("US"); assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Search").endsWith("98204%2CUS"); messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getMessage()).as("is UK").contains("UK"); + assertThat(messages.get(0).getMessage()).as("is UK").contains("London").contains("UK"); assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Code").endsWith("4298960"); assertThatThrownBy( @@ -70,7 +70,7 @@ public class Weather2Test extends LocalProperties { "no API key").isInstanceOf(ModuleException.class).hasNoCause(); messages = Weather2.getWeather("", "apikey"); - assertThat(messages.get(0).isError()).as("No api key.").isTrue(); + assertThat(messages.get(0).isError()).as("no query").isTrue(); } From e6ced56e1b9f0721fa581bea802d1de29fe32f44 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 29 Jul 2019 11:57:40 -0700 Subject: [PATCH 307/842] Switched to com.mebigfatguy.sb-contrib. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3bf8c83..1e9f534 100644 --- a/build.gradle +++ b/build.gradle @@ -64,7 +64,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.12.2' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' - spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.6' + spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.6' compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" From 44e8d91603e93b543192521217a777f0fb7c0cca Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 30 Jul 2019 15:46:47 -0700 Subject: [PATCH 308/842] Cleanup. --- .idea/modules/mobibot.iml | 2 +- LICENCE.txt => LICENSE.txt | 0 build.gradle | 30 ++++++++++++++++++------------ 3 files changed, 19 insertions(+), 13 deletions(-) rename LICENCE.txt => LICENSE.txt (100%) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 880e624..2113f38 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+469" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+484" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> diff --git a/LICENCE.txt b/LICENSE.txt similarity index 100% rename from LICENCE.txt rename to LICENSE.txt diff --git a/build.gradle b/build.gradle index 1e9f534..7c452a2 100644 --- a/build.gradle +++ b/build.gradle @@ -23,8 +23,11 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' ext { - log4j = '2.12.0' - spotbugs_annotations = '4.0.0-beta3' + versions = [ + kotlin : '1.3.40', + log4j : '2.12.0', + spotbugs_annotations : '4.0.0-beta3' + ] } repositories { @@ -40,9 +43,9 @@ dependencies { compile 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile "org.apache.logging.log4j:log4j-api:$log4j" - compile "org.apache.logging.log4j:log4j-core:$log4j" - compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j" + compile "org.apache.logging.log4j:log4j-api:$versions.log4j" + compile "org.apache.logging.log4j:log4j-core:$versions.log4j" + compile "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" compile 'commons-cli:commons-cli:1.4' @@ -59,15 +62,18 @@ dependencies { compile 'net.thauvin.erik:pinboard-poster:1.0.1' compile 'net.aksingh:owm-japis:2.5.3.0' + // Override own-japis dependencies with newer version of Kotlin + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" testImplementation 'org.testng:testng:6.14.3' - testImplementation 'org.assertj:assertj-core:3.12.2' + testImplementation 'org.assertj:assertj-core:3.13.1' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.6' - compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" - testCompileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs_annotations" + compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs_annotations" + testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs_annotations" } test { @@ -153,6 +159,10 @@ tasks.withType(Checkstyle) { } } +tasks.sonarqube { + dependsOn("jacocoTestReport") +} + task copyToDeploy(type: Copy) { from('properties') from jar @@ -182,7 +192,3 @@ task release(dependsOn: ['wrapper', 'clean', 'deploy']) { group = 'Publishing' description = 'Releases new version.' } - -tasks.sonarqube { - dependsOn("jacocoTestReport") -} From 8484e2fc0cf1d0b4933aa42bac6c5664d1d2309d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 9 Sep 2019 01:08:01 -0700 Subject: [PATCH 309/842] Updated dependencies. --- build.gradle | 14 +++++++------- config/pmd.xml | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 7c452a2..9310af6 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'jacoco' id 'java' id 'pmd' - id "com.github.ben-manes.versions" version "0.21.0" + id "com.github.ben-manes.versions" version "0.24.0" id "com.github.spotbugs" version "2.0.0" id "net.thauvin.erik.gradle.semver" version "1.0.4" id "org.sonarqube" version "2.7.1" @@ -24,8 +24,8 @@ mainClassName = packageName + '.Mobibot' ext { versions = [ - kotlin : '1.3.40', - log4j : '2.12.0', + kotlin : '1.3.50', + log4j : '2.12.1', spotbugs_annotations : '4.0.0-beta3' ] } @@ -50,11 +50,11 @@ dependencies { compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:4.0.1' + compile 'com.squareup.okhttp3:okhttp:4.1.1' compile 'com.rometools:rome:1.12.1' - compile 'org.json:json:20180813' + compile 'org.json:json:20190722' compile 'org.jsoup:jsoup:1.12.1' compile 'net.objecthunter:exp4j:0.4.8' @@ -66,8 +66,8 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" - testImplementation 'org.testng:testng:6.14.3' - testImplementation 'org.assertj:assertj-core:3.13.1' + testImplementation 'org.testng:testng:7.0.0' + testImplementation 'org.assertj:assertj-core:3.13.2' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.6' diff --git a/config/pmd.xml b/config/pmd.xml index 6e60806..5cfeaf2 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -154,7 +154,6 @@ <rule ref="category/java/errorprone.xml/JumbledIncrementer"/> <rule ref="category/java/errorprone.xml/JUnitSpelling"/> <rule ref="category/java/errorprone.xml/JUnitStaticSuite"/> - <rule ref="category/java/errorprone.xml/LoggerIsNotStaticFinal"/> <rule ref="category/java/errorprone.xml/MethodWithSameNameAsEnclosingClass"/> <rule ref="category/java/errorprone.xml/MisplacedNullCheck"/> <rule ref="category/java/errorprone.xml/MissingBreakInSwitch"/> From 1cabb74eb80fdc022dda8118139202faac5cfc6f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 9 Sep 2019 01:09:00 -0700 Subject: [PATCH 310/842] Workaround for API change for invalid symbol. --- .../net/thauvin/erik/mobibot/modules/StockQuote.java | 7 ++++++- .../net/thauvin/erik/mobibot/modules/StockQuoteTest.java | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 41c5cce..3a00a5d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -113,7 +113,12 @@ public final class StockQuote extends ThreadedModule { try { final String error = json.getString("Error Message"); if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + if (error.startsWith("Invalid API call.")) { + throw new ModuleException(debugMessage + ": " + Utils.unescapeXml(error), + "Invalid symbol."); + } else { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } } } catch (JSONException ignore) { // Do nothing. diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 4b6e665..d9b7fb0 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -56,12 +56,15 @@ public class StockQuoteTest extends LocalProperties { public void testGetQuote() throws ModuleException { final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); try { - List<Message> messages = StockQuote.getQuote("AAPL", apiKey); + final List<Message> messages = StockQuote.getQuote("AAPL", apiKey); assertThat(messages).as("response not empty").isNotEmpty(); assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL"); - messages = StockQuote.getQuote("012", apiKey); - assertThat(messages.get(0).isError()).as("invalid symbol error").isTrue(); + try { + StockQuote.getQuote("012", apiKey); + } catch (ModuleException e) { + assertThat(e.getMessage()).as("invalid symbol").containsIgnoringCase("invalid symbol"); + } assertThatThrownBy(() -> StockQuote.getQuote("test", "")).as("no API key").isInstanceOf( ModuleException.class).hasNoCause(); From c0ba4cefd55c267f85d7932d24f5e5a3ca0084bc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 9 Sep 2019 01:09:35 -0700 Subject: [PATCH 311/842] Version 0.7.3-beta+499 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 4ca5b99..abf89a0 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1564380207330L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1568016152101L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "471"; - public static final String VERSION = "0.7.3-beta+471"; + public static final String BUILDMETA = "499"; + public static final String VERSION = "0.7.3-beta+499"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index b7a1eea..c9d71f1 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Jul 28 23:03:26 PDT 2019 -version.buildmeta=471 +#Mon Sep 09 01:02:31 PDT 2019 +version.buildmeta=499 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+471 +version.semver=0.7.3-beta+499 From 8931405ee92092d0615b6c1a42898cfb9befea6d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 01:00:50 -0700 Subject: [PATCH 312/842] Updated dependencies. --- .idea/modules/mobibot.iml | 25 +++--- build.gradle | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 4 +- mobibot.ipr | 96 +++++++++--------------- 5 files changed, 55 insertions(+), 78 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index 2113f38..f0bdc97 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+484" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+500" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -19,38 +19,37 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.12.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.12.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.12.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.12.1" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.0.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.1.1" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.1" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20180813" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta3" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.1" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.2.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.40" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.50" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.40" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.14.3" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.12.2" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:7.0.0" level="project" /> + <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.13.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.apache-extras.beanshell:bsh:2.0b6" level="project" /> </component> </module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9310af6..cf21967 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'jacoco' id 'java' id 'pmd' - id "com.github.ben-manes.versions" version "0.24.0" + id "com.github.ben-manes.versions" version "0.25.0" id "com.github.spotbugs" version "2.0.0" id "net.thauvin.erik.gradle.semver" version "1.0.4" id "org.sonarqube" version "2.7.1" @@ -50,7 +50,7 @@ dependencies { compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:4.1.1' + compile 'com.squareup.okhttp3:okhttp:4.2.0' compile 'com.rometools:rome:1.12.1' @@ -108,7 +108,7 @@ jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) - version = null + archiveVersion.set("") } clean { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4b7e1f3..7c4388a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 8e25e6c..83f2acf 100755 --- a/gradlew +++ b/gradlew @@ -125,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/mobibot.ipr b/mobibot.ipr index ccfa327..49557ed 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1060,9 +1060,12 @@ <projects_view /> </option> </component> + <component name="GradleMigrationSettings" migrationVersion="1" /> <component name="GradleSettings"> <option name="linkedExternalProjectsSettings"> <GradleProjectSettings> + <option name="delegatedBuild" value="false" /> + <option name="testRunner" value="PLATFORM" /> <option name="distributionType" value="LOCAL" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="gradleHome" value="C:/gradle" /> @@ -1132,22 +1135,6 @@ <option name="USE_PROJECT_PROFILE" value="false" /> <version value="1.0" /> </component> - <component name="KobaltSettings"> - <option name="linkedExternalProjectsSettings"> - <KobaltProjectSettings> - <option name="autoDownloadKobalt" value="true" /> - <option name="downloadSources" value="false" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="kobaltHome" value="$USER_HOME$/.kobalt/wrapper/dist/kobalt-1.0.122" /> - <option name="modules"> - <set> - <option value="$PROJECT_DIR$" /> - </set> - </option> - <option name="profiles" value="" /> - </KobaltProjectSettings> - </option> - </component> <component name="MavenImportPreferences"> <option name="generalSettings"> <MavenGeneralSettings> @@ -1333,7 +1320,7 @@ <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" group="mobibot" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="12" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_12" default="false" project-jdk-name="12" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="PropertiesComponent"> @@ -1504,13 +1491,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.1/46920b2dae8ec033a2948a07f58cfc133276070b/rome-1.12.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:4.0.1"> + <library name="Gradle: com.squareup.okhttp3:okhttp:4.1.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.0.1/4e606430c80c69b744d19c6ca1b269b68e297040/okhttp-4.0.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.1.1/4c8c6d55ec16c575131f293c9b2cb7b705809a83/okhttp-4.1.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.0.1/54d824f82c8e609588bd101056e362e2e2421bd3/okhttp-4.0.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.1.1/8b14260502684c226128e726b27a52a3a65ee721/okhttp-4.1.1-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okio:okio:2.2.2"> @@ -1594,49 +1581,40 @@ <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache-extras.beanshell:bsh:2.0b6"> + <library name="Gradle: org.apache.logging.log4j:log4j-api:2.12.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache-extras.beanshell/bsh/2.0b6/fb418f9b33a0b951e9a2978b4b6ee93b2707e72f/bsh-2.0b6.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.1/a55e6d987f50a515c9260b0451b4fa217dc539cb/log4j-api-2.12.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache-extras.beanshell/bsh/2.0b6/76a65f674e8f2df409934824c49ca338987c63ae/bsh-2.0b6-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.1/72dbe5460db61664f6bbfffb36665fa0bbe8e3ad/log4j-api-2.12.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-api:2.12.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-core:2.12.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.0/8b50b487ac0c0bff1af79d56c6454ea68f40c150/log4j-api-2.12.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.1/4382e93136c06bfb34ddfa0bb8a9fb4ea2f3df59/log4j-core-2.12.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.0/b52dca271c11244ac235fc56e5f59f5cd22b7876/log4j-api-2.12.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.1/1810a578053de4b4f8dd6d5c8b6b853db7ac4176/log4j-core-2.12.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-core:2.12.0"> + <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.1"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.0/1723837573e4c5dbc8840f9f6e8f79b245b94bb/log4j-core-2.12.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.1/14973e22497adaf0196d481fb99c5dc2a0b58d41/log4j-slf4j-impl-2.12.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.0/259b7766eec03263c1c1a1f40484b624108b01c9/log4j-core-2.12.0-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.1/49c6c7bed2c0074cedc53edca9681fa442f09b81/log4j-slf4j-impl-2.12.1-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.0"> + <library name="Gradle: org.assertj:assertj-core:3.13.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.0/7f44f5201f79d6f7010a1515699a5f6c40e49cb/log4j-slf4j-impl-2.12.0.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.13.2/bb7b963fe752f69f055df0025691eceb83ce0c5d/assertj-core-3.13.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.0/428316adc5e470af7032beeb53d84b3b879254ed/log4j-slf4j-impl-2.12.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.assertj:assertj-core:3.12.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.12.2/3c9724faa6090ab78a61d4e54011df72de2361c4/assertj-core-3.12.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.12.2/685240f44440706f41832ba4ff223204d3e9e06d/assertj-core-3.12.2-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.13.2/616a8ca81b9a1a58e3c8cd8c5d7395eb548ce57/assertj-core-3.13.2-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jdom:jdom2:2.0.6"> @@ -1648,40 +1626,40 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.40" type="kotlin.common"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50" type="kotlin.common"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.40/ff8f3da514fc2877d1303d55e22d6da8156c29fb/kotlin-stdlib-common-1.3.40.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.50/3d9cd3e1bc7b92e95f43d45be3bfbcf38e36ab87/kotlin-stdlib-common-1.3.50.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.40/682cd7821872d1a3c1e8a3e6bdaabd17f2b71004/kotlin-stdlib-common-1.3.40-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.50/8ac406cf33e942265c4abb33ee0acdee79292dd6/kotlin-stdlib-common-1.3.50-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/bf0edcf669e446e0d903a0681190d1e3df969ac4/kotlin-stdlib-jdk7-1.3.30.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.50/50ad05ea1c2595fb31b800e76db464d08d599af3/kotlin-stdlib-jdk7-1.3.50.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/2b561cb97f3fbf555f328e9c87ec625b12ee6c17/kotlin-stdlib-jdk7-1.3.30-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.50/854725f0e8cdf688647bdc9022d1b32228fefc14/kotlin-stdlib-jdk7-1.3.50-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/459999be0d6ac844dd3d2ca85c2daf14deb5f7f0/kotlin-stdlib-jdk8-1.3.30.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.50/bf65725d4ae2cf00010d84e945fcbc201f590e11/kotlin-stdlib-jdk8-1.3.50.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/269416550424b90ce1c27727d0d5c65b6bb730c9/kotlin-stdlib-jdk8-1.3.30-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.50/e1eaa82220f14bd12ae0bf211a08b2bad5700e20/kotlin-stdlib-jdk8-1.3.50-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.40"> + <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.50"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.40/b8a521c687329303778548e2f09b0ba5b2665236/kotlin-stdlib-1.3.40.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.50/b529d1738c7e98bbfa36a4134039528f2ce78ebf/kotlin-stdlib-1.3.50.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.40/7af6fd6b9293e80fc7408493a764858a7f66b022/kotlin-stdlib-1.3.40-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.50/bc0c767786c4a3c042a364e69d3f3dd5ff5253a5/kotlin-stdlib-1.3.50-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jetbrains:annotations:13.0"> @@ -1693,13 +1671,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.json:json:20180813"> + <library name="Gradle: org.json:json:20190722"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180813/8566b2b0391d9d4479ea225645c6ed47ef17fe41/json-20180813.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20180813/60753006d96d5fc85b61b0897f3326b5c4e1c4f0/json-20180813-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/6c40df0d93d36cdfc0fbf08e4e3b27eaa1208199/json-20190722-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.jsoup:jsoup:1.12.1"> @@ -1720,13 +1698,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/962153db4a9ea71b79d047dfd1b2a0d80d8f4739/slf4j-api-1.7.25-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: org.testng:testng:6.14.3"> + <library name="Gradle: org.testng:testng:7.0.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.14.3/d24515dc253e77e54b73df97e1fb2eb7faf34fdd/testng-6.14.3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.0.0/14b73f64988eda81a42b4584e9647d48633ef857/testng-7.0.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/6.14.3/bfd2538501a129a142fe8366169c48b91609dc0b/testng-6.14.3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.0.0/5a0241ace5a7b8eb3d31556a7d5dd2b3aae9baf/testng-7.0.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: org.twitter4j:twitter4j-core:4.0.7"> From 2775cca3aa764bdb61b033c129083e0538e0e569 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 01:01:17 -0700 Subject: [PATCH 313/842] Added keywords symbol lookup. --- .../erik/mobibot/modules/StockQuote.java | 145 +++++++++++------- .../erik/mobibot/modules/StockQuoteTest.java | 6 +- 2 files changed, 92 insertions(+), 59 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 3a00a5d..5cb57f0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -42,12 +42,14 @@ import net.thauvin.erik.mobibot.msg.PublicMessage; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * The StockQuote module. @@ -61,9 +63,12 @@ public final class StockQuote extends ThreadedModule { * The Alpha Advantage property key. */ static final String ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key"; - + /** + * The Invalid Symbol error string. + */ + static final String INVALID_SYMBOL = "Invalid symbol."; // The Alpha Advantage URL. - private static final String ALAPHADVANTAGE_URL = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE"; + private static final String ALAPHADVANTAGE_URL = "https://www.alphavantage.co/query?function="; // The quote command. private static final String STOCK_CMD = "stock"; @@ -93,74 +98,71 @@ public final class StockQuote extends ThreadedModule { final String debugMessage = "getQuote(" + symbol + ')'; final ArrayList<Message> messages = new ArrayList<>(); final OkHttpClient client = new OkHttpClient(); - final Request request = - new Request.Builder().url(ALAPHADVANTAGE_URL + "&symbol=" + symbol + "&apikey=" + apiKey).build(); try { - final Response response = client.newCall(request).execute(); + // Search for symbol/keywords + Request request = new Request.Builder().url( + ALAPHADVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build(); + Response response = client.newCall(request).execute(); + if (response.body() != null) { - final JSONObject json = new JSONObject(response.body().string()); - - try { - final String info = json.getString("Information"); - if (!info.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string()); + if (isValidJsonObject(json, debugMessage)) { + final JSONArray symbols = json.getJSONArray("bestMatches"); + if (symbols.isEmpty()) { + messages.add(new ErrorMessage(INVALID_SYMBOL)); + return messages; } - } catch (JSONException ignore) { - // Do nothing. - } - try { - final String error = json.getString("Error Message"); - if (!error.isEmpty()) { - if (error.startsWith("Invalid API call.")) { - throw new ModuleException(debugMessage + ": " + Utils.unescapeXml(error), - "Invalid symbol."); - } else { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + final JSONObject symbolInfo = symbols.getJSONObject(0); + + // Get quote for symbol + request = new Request.Builder().url( + ALAPHADVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" + + apiKey).build(); + response = client.newCall(request).execute(); + if (response.body() != null) { + json = new JSONObject(Objects.requireNonNull(response.body()).string()); + + if (isValidJsonObject(json, debugMessage)) { + final JSONObject quote = json.getJSONObject("Global Quote"); + + if (quote.isEmpty()) { + messages.add(new ErrorMessage(INVALID_SYMBOL)); + return messages; + } + + messages.add(new PublicMessage( + "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils + .unescapeXml(symbolInfo.getString("2. name") + ']'))); + messages.add(new PublicMessage( + " Price: " + Utils.unescapeXml(quote.getString("05. price")))); + messages.add(new PublicMessage( + " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")))); + messages.add(new NoticeMessage( + " Open: " + Utils.unescapeXml(quote.getString("02. open")))); + messages.add(new NoticeMessage( + " High: " + Utils.unescapeXml(quote.getString("03. high")))); + messages.add(new NoticeMessage( + " Low: " + Utils.unescapeXml(quote.getString("04. low")))); + messages.add(new NoticeMessage( + " Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); + messages.add(new NoticeMessage( + " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); + messages.add(new NoticeMessage( + " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils + .unescapeXml(quote.getString("10. change percent")) + ']')); } } - } catch (JSONException ignore) { - // Do nothing. } - - final JSONObject quote = json.getJSONObject("Global Quote"); - - if (quote.isEmpty()) { - messages.add(new ErrorMessage("Invalid symbol.")); - return messages; - } - - messages.add( - new PublicMessage("Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")))); - messages.add( - new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); - messages.add( - new PublicMessage(" Previous: " - + Utils.unescapeXml(quote.getString("08. previous close")))); - messages.add( - new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); - messages.add( - new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); - messages.add( - new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); - messages.add( - new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); - messages.add( - new NoticeMessage(" Latest: " - + Utils.unescapeXml(quote.getString("07. latest trading day")))); - messages.add( - new NoticeMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change")) - + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']')); } } catch (IOException e) { throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); } return messages; } else { - throw new ModuleException("Invalid symbol."); + throw new ModuleException(INVALID_SYMBOL); } - } /** @@ -169,7 +171,38 @@ public final class StockQuote extends ThreadedModule { @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { bot.send(sender, "To retrieve a stock quote:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol>")); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>")); + } + + private static boolean isValidJsonObject(final JSONObject json, final String debugMessage) throws ModuleException { + try { + final String info = json.getString("Information"); + if (!info.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + try { + final String error = json.getString("Note"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + try { + final String error = json.getString("Error Message"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + return true; } /** diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index d9b7fb0..77de00b 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -56,14 +56,14 @@ public class StockQuoteTest extends LocalProperties { public void testGetQuote() throws ModuleException { final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); try { - final List<Message> messages = StockQuote.getQuote("AAPL", apiKey); + final List<Message> messages = StockQuote.getQuote("apple inc", apiKey); assertThat(messages).as("response not empty").isNotEmpty(); - assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL"); + assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL").contains("Apple Inc."); try { StockQuote.getQuote("012", apiKey); } catch (ModuleException e) { - assertThat(e.getMessage()).as("invalid symbol").containsIgnoringCase("invalid symbol"); + assertThat(e.getMessage()).as("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL); } assertThatThrownBy(() -> StockQuote.getQuote("test", "")).as("no API key").isInstanceOf( From f161ac2e790510a4a238951a002ec7cede5455c6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 01:01:53 -0700 Subject: [PATCH 314/842] Version 0.7.3-beta+515 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index abf89a0..ee4ff02 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1568016152101L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1568359883863L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "499"; - public static final String VERSION = "0.7.3-beta+499"; + public static final String BUILDMETA = "515"; + public static final String VERSION = "0.7.3-beta+515"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index c9d71f1..4f2755a 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Sep 09 01:02:31 PDT 2019 -version.buildmeta=499 +#Fri Sep 13 00:31:23 PDT 2019 +version.buildmeta=515 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+499 +version.semver=0.7.3-beta+515 From 2166e166e82c28675adf42e1f365c25c4132317d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 01:23:48 -0700 Subject: [PATCH 315/842] Changed replaceAll() to replace() (sonarqube) --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 12 ++++++------ .../thauvin/erik/mobibot/entries/EntriesUtils.java | 2 +- .../net/thauvin/erik/mobibot/entries/EntryLink.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/Calc.java | 2 +- .../erik/mobibot/modules/CurrencyConverter.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/Joke.java | 4 ++-- .../erik/mobibot/modules/LocalProperties.java | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 1ff35f7..b32f261 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -247,12 +247,12 @@ public final class Utils { * @return The unescaped string. */ public static String unescapeXml(final String str) { - return str.replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll(""", "\"") - .replaceAll("'", "'") - .replaceAll("'", "'"); + return str.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace(""", "\"") + .replace("'", "'") + .replace("'", "'"); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java index 63e8561..636fbe4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -115,6 +115,6 @@ public final class EntriesUtils { * @return The entry's tags. */ public static String buildTags(final int entryIndex, final EntryLink entry) { - return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replaceAll(",", ", ")); + return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", ")); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 485bbd5..62fdfa9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -297,7 +297,7 @@ public class EntryLink implements Serializable { @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public final void setTags(final String tags) { if (tags != null) { - final String[] parts = tags.replaceAll(", ", " ").replaceAll(",", " ").split(" "); + final String[] parts = tags.replace(", ", " ").replace(",", " ").split(" "); SyndCategoryImpl tag; String part; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index b68cc4d..77dafa1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -71,7 +71,7 @@ public class Calc extends AbstractModule { try { final Expression calc = new ExpressionBuilder(query).build(); - return query.replaceAll(" ", "") + " = " + decimalFormat.format(calc.evaluate()); + return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()); } catch (Exception e) { return "No idea. This is the kind of math I don't get."; } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index a50c464..3baa613 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -175,7 +175,7 @@ public final class CurrencyConverter extends ThreadedModule { return new ErrorMessage("You're kidding, right?"); } else { try { - final double amt = Double.parseDouble(cmds[0].replaceAll(",", "")); + final double amt = Double.parseDouble(cmds[0].replace(",", "")); final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1] .toUpperCase(Constants.LOCALE))); final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase(Constants.LOCALE))); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index cfd5dad..b1b826a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -89,8 +89,8 @@ public final class Joke extends ThreadedModule { //noinspection RegExpRedundantEscape return new PublicMessage( - json.getJSONObject("value").get("joke").toString().replaceAll("\\'", "'") - .replaceAll("\\\"", "\"")); + json.getJSONObject("value").get("joke").toString().replace("\\'", "'") + .replace("\\\"", "\"")); } } catch (Exception e) { throw new ModuleException("randomJoke()", "An error has occurred retrieving a random joke.", e); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java index 0cd1082..900b7b9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -65,7 +65,7 @@ class LocalProperties { } private static String keyToEnv(final String key) { - return key.replaceAll("-", "_").toUpperCase(Constants.LOCALE); + return key.replace("-", "_").toUpperCase(Constants.LOCALE); } @BeforeSuite(alwaysRun = true) From 4ccdcb95c3519272d84ce6be8c7bac6c5a4ddf9e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 01:24:56 -0700 Subject: [PATCH 316/842] Changed isValidJsonObjec()t to validateJsonResponse() --- .../erik/mobibot/modules/StockQuote.java | 128 +++++++++--------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 5cb57f0..b0574d3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -107,53 +107,53 @@ public final class StockQuote extends ThreadedModule { if (response.body() != null) { JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string()); - if (isValidJsonObject(json, debugMessage)) { - final JSONArray symbols = json.getJSONArray("bestMatches"); - if (symbols.isEmpty()) { + validateJsonResponse(json, debugMessage); + + final JSONArray symbols = json.getJSONArray("bestMatches"); + if (symbols.isEmpty()) { + messages.add(new ErrorMessage(INVALID_SYMBOL)); + return messages; + } + + final JSONObject symbolInfo = symbols.getJSONObject(0); + + // Get quote for symbol + request = new Request.Builder().url( + ALAPHADVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" + + apiKey).build(); + response = client.newCall(request).execute(); + if (response.body() != null) { + json = new JSONObject(Objects.requireNonNull(response.body()).string()); + validateJsonResponse(json, debugMessage); + + final JSONObject quote = json.getJSONObject("Global Quote"); + + if (quote.isEmpty()) { messages.add(new ErrorMessage(INVALID_SYMBOL)); return messages; } - final JSONObject symbolInfo = symbols.getJSONObject(0); + messages.add(new PublicMessage( + "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils + .unescapeXml(symbolInfo.getString("2. name") + ']'))); + messages.add( + new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); + messages.add(new PublicMessage( + " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")))); + messages.add( + new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); + messages.add( + new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); + messages.add( + new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); + messages.add( + new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); + messages.add(new NoticeMessage( + " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); + messages.add(new NoticeMessage( + " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils + .unescapeXml(quote.getString("10. change percent")) + ']')); - // Get quote for symbol - request = new Request.Builder().url( - ALAPHADVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" - + apiKey).build(); - response = client.newCall(request).execute(); - if (response.body() != null) { - json = new JSONObject(Objects.requireNonNull(response.body()).string()); - - if (isValidJsonObject(json, debugMessage)) { - final JSONObject quote = json.getJSONObject("Global Quote"); - - if (quote.isEmpty()) { - messages.add(new ErrorMessage(INVALID_SYMBOL)); - return messages; - } - - messages.add(new PublicMessage( - "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils - .unescapeXml(symbolInfo.getString("2. name") + ']'))); - messages.add(new PublicMessage( - " Price: " + Utils.unescapeXml(quote.getString("05. price")))); - messages.add(new PublicMessage( - " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")))); - messages.add(new NoticeMessage( - " Open: " + Utils.unescapeXml(quote.getString("02. open")))); - messages.add(new NoticeMessage( - " High: " + Utils.unescapeXml(quote.getString("03. high")))); - messages.add(new NoticeMessage( - " Low: " + Utils.unescapeXml(quote.getString("04. low")))); - messages.add(new NoticeMessage( - " Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); - messages.add(new NoticeMessage( - " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); - messages.add(new NoticeMessage( - " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils - .unescapeXml(quote.getString("10. change percent")) + ']')); - } - } } } } catch (IOException e) { @@ -174,7 +174,27 @@ public final class StockQuote extends ThreadedModule { bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>")); } - private static boolean isValidJsonObject(final JSONObject json, final String debugMessage) throws ModuleException { + /** + * Returns the specified stock quote from Alpha Advantage. + */ + @Override + void run(final Mobibot bot, final String sender, final String symbol) { + if (Utils.isValidString(symbol)) { + try { + final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); + for (final Message msg : messages) { + bot.send(sender, msg); + } + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(e.getMessage()); + } + } else { + helpResponse(bot, sender, symbol, true); + } + } + + private static void validateJsonResponse(final JSONObject json, final String debugMessage) throws ModuleException { try { final String info = json.getString("Information"); if (!info.isEmpty()) { @@ -201,27 +221,5 @@ public final class StockQuote extends ThreadedModule { } catch (JSONException ignore) { // Do nothing. } - - return true; - } - - /** - * Returns the specified stock quote from Alpha Advantage. - */ - @Override - void run(final Mobibot bot, final String sender, final String symbol) { - if (Utils.isValidString(symbol)) { - try { - final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); - for (final Message msg : messages) { - bot.send(sender, msg); - } - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(e.getMessage()); - } - } else { - helpResponse(bot, sender, symbol, true); - } } } From d7e1cf91321e53d51017451613986adbcd3f3d6a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 01:37:22 -0700 Subject: [PATCH 317/842] replace(string, string) to replace(char, char) [spotbugs] --- src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java | 2 +- .../java/net/thauvin/erik/mobibot/modules/LocalProperties.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 62fdfa9..05e19bb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -297,7 +297,7 @@ public class EntryLink implements Serializable { @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public final void setTags(final String tags) { if (tags != null) { - final String[] parts = tags.replace(", ", " ").replace(",", " ").split(" "); + final String[] parts = tags.replace(", ", " ").replace(',', ' ').split(" "); SyndCategoryImpl tag; String part; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java index 900b7b9..1a62f34 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -65,7 +65,7 @@ class LocalProperties { } private static String keyToEnv(final String key) { - return key.replace("-", "_").toUpperCase(Constants.LOCALE); + return key.replace('-', '_').toUpperCase(Constants.LOCALE); } @BeforeSuite(alwaysRun = true) From 9c4eaa6cff573fa19fd1b64e9f22514cafafe0ed Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 01:37:47 -0700 Subject: [PATCH 318/842] Version 0.7.3-beta+519 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index ee4ff02..c9d679c 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1568359883863L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1568363740289L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "515"; - public static final String VERSION = "0.7.3-beta+515"; + public static final String BUILDMETA = "519"; + public static final String VERSION = "0.7.3-beta+519"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 4f2755a..1208aeb 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 13 00:31:23 PDT 2019 -version.buildmeta=515 +#Fri Sep 13 01:35:39 PDT 2019 +version.buildmeta=519 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+515 +version.semver=0.7.3-beta+519 From ec8d4754e46f643d0b7424e22bf115a44cc5f4b3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 12:36:44 -0700 Subject: [PATCH 319/842] Changed validateJsonResponse() to getJsonResponse(). --- .../erik/mobibot/modules/StockQuote.java | 161 +++++++++--------- 1 file changed, 82 insertions(+), 79 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index b0574d3..30c53dd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -81,6 +81,49 @@ public final class StockQuote extends ThreadedModule { properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); } + @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") + private static JSONObject getJsonResponse(final Response response, final String debugMessage) + throws IOException, ModuleException { + if (response.isSuccessful()) { + if (response.body() != null) { + final JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string()); + + try { + final String info = json.getString("Information"); + if (!info.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(info)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + try { + final String error = json.getString("Note"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + try { + final String error = json.getString("Error Message"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing. + } + + return json; + } else { + throw new ModuleException(debugMessage, "Invalid Response (" + response.code() + ')'); + } + } else { + throw new ModuleException(debugMessage, "Empty Response."); + } + } + /** * Retrieves a stock quote. * @@ -105,58 +148,47 @@ public final class StockQuote extends ThreadedModule { ALAPHADVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build(); Response response = client.newCall(request).execute(); - if (response.body() != null) { - JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string()); - validateJsonResponse(json, debugMessage); + JSONObject json = getJsonResponse(response, debugMessage); - final JSONArray symbols = json.getJSONArray("bestMatches"); - if (symbols.isEmpty()) { - messages.add(new ErrorMessage(INVALID_SYMBOL)); - return messages; - } - - final JSONObject symbolInfo = symbols.getJSONObject(0); - - // Get quote for symbol - request = new Request.Builder().url( - ALAPHADVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" - + apiKey).build(); - response = client.newCall(request).execute(); - if (response.body() != null) { - json = new JSONObject(Objects.requireNonNull(response.body()).string()); - validateJsonResponse(json, debugMessage); - - final JSONObject quote = json.getJSONObject("Global Quote"); - - if (quote.isEmpty()) { - messages.add(new ErrorMessage(INVALID_SYMBOL)); - return messages; - } - - messages.add(new PublicMessage( - "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils - .unescapeXml(symbolInfo.getString("2. name") + ']'))); - messages.add( - new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); - messages.add(new PublicMessage( - " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")))); - messages.add( - new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); - messages.add( - new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); - messages.add( - new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); - messages.add( - new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); - messages.add(new NoticeMessage( - " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); - messages.add(new NoticeMessage( - " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils - .unescapeXml(quote.getString("10. change percent")) + ']')); - - } + final JSONArray symbols = json.getJSONArray("bestMatches"); + if (symbols.isEmpty()) { + messages.add(new ErrorMessage(INVALID_SYMBOL)); + return messages; } - } catch (IOException e) { + + final JSONObject symbolInfo = symbols.getJSONObject(0); + + // Get quote for symbol + request = new Request.Builder().url( + ALAPHADVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" + + apiKey).build(); + response = client.newCall(request).execute(); + + json = getJsonResponse(response, debugMessage); + + final JSONObject quote = json.getJSONObject("Global Quote"); + + if (quote.isEmpty()) { + messages.add(new ErrorMessage(INVALID_SYMBOL)); + return messages; + } + + messages.add(new PublicMessage( + "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils + .unescapeXml(symbolInfo.getString("2. name") + ']'))); + messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); + messages.add( + new PublicMessage(" Previous: " + Utils.unescapeXml(quote.getString("08. previous close")))); + messages.add(new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); + messages.add(new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); + messages.add(new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); + messages.add(new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); + messages.add(new NoticeMessage( + " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); + messages.add(new NoticeMessage( + " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils + .unescapeXml(quote.getString("10. change percent")) + ']')); + } catch (IOException | NullPointerException e) { throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); } return messages; @@ -193,33 +225,4 @@ public final class StockQuote extends ThreadedModule { helpResponse(bot, sender, symbol, true); } } - - private static void validateJsonResponse(final JSONObject json, final String debugMessage) throws ModuleException { - try { - final String info = json.getString("Information"); - if (!info.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(info)); - } - } catch (JSONException ignore) { - // Do nothing. - } - - try { - final String error = json.getString("Note"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); - } - } catch (JSONException ignore) { - // Do nothing. - } - - try { - final String error = json.getString("Error Message"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); - } - } catch (JSONException ignore) { - // Do nothing. - } - } } From 330bdb2a2d9e72d51162cba490f00800df16a372 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 12:37:07 -0700 Subject: [PATCH 320/842] Version 0.7.3-beta+526 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index c9d679c..769bb12 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1568363740289L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1568402741804L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "519"; - public static final String VERSION = "0.7.3-beta+519"; + public static final String BUILDMETA = "526"; + public static final String VERSION = "0.7.3-beta+526"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 1208aeb..023af81 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 13 01:35:39 PDT 2019 -version.buildmeta=519 +#Fri Sep 13 12:25:40 PDT 2019 +version.buildmeta=526 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+519 +version.semver=0.7.3-beta+526 From f9d86c3cf05c00d29643e03d6c6af9e716b4eaa9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 16:28:24 -0700 Subject: [PATCH 321/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 30c53dd..912f287 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -81,7 +81,7 @@ public final class StockQuote extends ThreadedModule { properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); } - @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?") private static JSONObject getJsonResponse(final Response response, final String debugMessage) throws IOException, ModuleException { if (response.isSuccessful()) { @@ -131,7 +131,6 @@ public final class StockQuote extends ThreadedModule { * @return The {@link Message} array containing the stock quote. * @throws ModuleException If an errors occurs. */ - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?") static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { if (!Utils.isValidString(apiKey)) { throw new ModuleException(Utils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); From 5a40c1886e84cf036895cae7d413c6838b3ac54d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Sep 2019 16:32:12 -0700 Subject: [PATCH 322/842] Added more examples. --- website/index.html | 57 ++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/website/index.html b/website/index.html index ac5486d..a934ecd 100644 --- a/website/index.html +++ b/website/index.html @@ -33,36 +33,38 @@ <h3>About mobibot</h3> <p><strong>mobibot</strong> is the - <a href="http://www.mobitopia.org/"><strong>#mobitopia</strong></a> IRC channel bot. It is built on Paul Mutton's + <a href="https://www.mobitopia.org/"><strong>#mobitopia</strong></a> IRC channel bot. It is built on Paul Mutton's <a href="http://www.jibble.org/pircbot.php"><strong>PircBot Java-based Framework</strong></a>.</p> <p>mobibot is making extensive use of various <strong>open source libraries</strong>, including:</p> <ul> - <li><a href="http://commons.apache.org/proper/commons-cli/">Commons CLI</a></li> - <li><a href="http://hc.apache.org/httpclient-3.x/">Commons HTTPClient</a></li> - <li><a href="http://commons.apache.org/proper/commons-logging/">Commons Logging</a></li> - <li><a href="http://commons.apache.org/proper/commons-net/">Commons Net</a></li> - <li><a href="https://github.com/ethauvin/pinboard-poster">Pinboard Poster/a></li> - <li><a href="https://bitbucket.org/akapribot/owm-japis/">OWM JAPIs</a></li> - <li><a href="http://www.objecthunter.net/exp4j/">exp4j</a></li> - <li><a href="http://ostermiller.org/utils/">OstermillerUtils</a></li> - <li><a href="http://rometools.github.io/rome/">Rome</a></li> + <li><a href="https://commons.apache.org/proper/commons-cli/">Commons CLI</a></li> + <li><a href="https://hc.apache.org/httpclient-3.x/">Commons HTTPClient</a></li> + <li><a href="https://commons.apache.org/proper/commons-logging/">Commons Logging</a></li> + <li><a href="https://commons.apache.org/proper/commons-net/">Commons Net</a></li> + <li><a href="https://www.objecthunter.net/exp4j/">exp4j</a></li> + <li><a href="https://jsoup.org/">jsoup</a></li> + <li><a href="https://ostermiller.org/utils/">OstermillerUtils</a></li> + <li><a href="https://bitbucket.org/aksinghnet/owm-japis">OWM JAPIs</a></li> + <li><a href="https://github.com/ethauvin/pinboard-poster">Pinboard Poster</a></li> + <li><a href="https://rometools.github.io/rome/">Rome</a></li> <li><a href="http://twitter4j.org/en/index.html">Twitter4J</a></li> - <li><a href="http://jsoup.org/">jsoup</a></li> </ul> <p>mobibot was written by - <a href="http://erik.thauvin.net/"><strong>Erik C. Thauvin</strong></a> as a replacement for the channel's original + <a href="https://erik.thauvin.net/"><strong>Erik C. Thauvin</strong></a> as a replacement for the channel's original <a href="https://github.com/edumbill/chump">ChumpBot</a>. </p> <h3>Features</h3> <p>mobibot's main functionality is to <strong>capture URLs</strong> posted on the channel. The URLs are automatically gathered into a publishable - <a href="http://www.mobitopia.org/rss.xml"><strong>RSS feed</strong></a>. </p> + <a href="https://www.mobitopia.org/rss.xml"><strong>RSS feed</strong></a>. </p> <p>Other features include:</p> <ul> - <li>Displaying the latest entries on Mobitopia</li> + <li>Displaying the latest entries on Mobitopia + <div><code>mobibot: view</code></div> + </li> <li>Performing calculations <div><code>mobibot: calc (floor(sqrt(3)) + 3.14) * 3^2</code></div> </li> @@ -80,25 +82,36 @@ <li>Performing DNS lookups <div><code>mobibot: lookup www.apple.com</code></div> </li> - <li>Retrieving stock quotes + <li>Retrieving stock quotes from <a href="https://www.alphavantage.co/">Alpha Avantage</a> <div><code>mobibot: stock GOOG</code></div> + <div><code>mobibot: stock google</code></div> </li> <li>Displaying the time in various time zones <div><code>mobibot: time UK</code></div> + <div><code>mobibot: time GMT</code></div> </li> <li>Sending messages to people on join/activity: <div><code>mobibot: tell nickname Give me a call when you see this.</code></div> </li> - <li>Recapping public channel messages</li> - <li>Listing the users on the channel</li> - <li>Random jokes from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a></li> - <li>Rolling dice and playing war</li> - <li>Posting to <a href="http://twitter.com/mobitopia">Twitter</a></li> + <li>Recapping public channel messages + <div><code>/msg mobibot recap</code></div> + </li> + <li>Listing the users on the channel + <div><code>/msg mobibot users</code></div> + </li> + <li>Random jokes from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> + <div><code>mobibot: joke</code></div> + </li> + <li>Rolling dice and playing war + <div><code>mobibot: dice</code><div> + <div><code>mobibot: war</code></div> + </li> + <li>Posting to <a href="https://twitter.com/mobitopia">Twitter</a></li> </ul> <p>Some of the internal features include RSS feed backlogs, rolling logs, debugging toggle and much more.</p> <p>If you have any feature suggestions, please post them to the - <a href="http://erik.thauvin.net/wiki/mobibot"><strong>mobibot wiki</strong></a>. </p> + <a href="https://erik.thauvin.net/wiki/mobibot"><strong>mobibot wiki</strong></a>. </p> <h3>Using mobibot</h3> @@ -116,4 +129,4 @@ </p> </div> </body> -</html> \ No newline at end of file +</html> From e74b87d371ed9aaa12b2b21cf3bf015587308cf7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 Feb 2020 22:52:07 -0800 Subject: [PATCH 323/842] Now using StringUtils. --- .../erik/mobibot/entries/EntriesMgr.java | 7 ++-- .../erik/mobibot/modules/AbstractModule.java | 8 ++-- .../thauvin/erik/mobibot/modules/Calc.java | 4 +- .../mobibot/modules/CurrencyConverter.java | 5 ++- .../erik/mobibot/modules/GoogleSearch.java | 9 ++-- .../erik/mobibot/modules/ModuleException.java | 41 ++++++------------- .../erik/mobibot/modules/StockQuote.java | 15 +++---- .../erik/mobibot/modules/Weather2.java | 31 ++++++++------ .../net/thauvin/erik/mobibot/tell/Tell.java | 5 ++- 9 files changed, 59 insertions(+), 66 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index 38b9e6d..3c450b3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -44,6 +44,7 @@ import com.rometools.rome.io.SyndFeedOutput; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.InputStreamReader; @@ -200,7 +201,7 @@ public final class EntriesMgr { bot.getLogger().debug("Saving the feeds..."); } - if (Utils.isValidString(bot.getLogsDir()) && Utils.isValidString(bot.getWeblogUrl())) { + if (StringUtils.isNotBlank(bot.getLogsDir()) && StringUtils.isNotBlank(bot.getWeblogUrl())) { try { final SyndFeedOutput output = new SyndFeedOutput(); SyndFeed rss = new SyndFeedImpl(); @@ -279,8 +280,8 @@ public final class EntriesMgr { } if (isDayBackup) { - if (Utils.isValidString(bot.getBacklogsUrl())) { - if (history.indexOf(bot.getToday()) == -1) { + if (StringUtils.isNotBlank(bot.getBacklogsUrl())) { + if (!history.contains(bot.getToday())) { history.add(bot.getToday()); while (history.size() > MAX_BACKLOGS) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index a704eca..61e0871 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.List; @@ -103,7 +103,7 @@ public abstract class AbstractModule { public abstract void helpResponse(final Mobibot bot, final String sender, final String args, - final boolean isPrivate); + @SuppressWarnings("unused") final boolean isPrivate); /** * Returns <code>true</code> if the module is enabled. @@ -134,7 +134,7 @@ public abstract class AbstractModule { */ boolean isValidProperties() { for (final String s : getPropertyKeys()) { - if (!Utils.isValidString(properties.get(s))) { + if (StringUtils.isBlank(properties.get(s))) { return false; } } @@ -149,7 +149,7 @@ public abstract class AbstractModule { * @param value The value. */ public void setProperty(final String key, final String value) { - if (Utils.isValidString(key)) { + if (StringUtils.isNotBlank(key)) { properties.put(key, value); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 77dafa1..b852614 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -35,7 +35,7 @@ package net.thauvin.erik.mobibot.modules; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; +import org.apache.commons.lang3.StringUtils; import java.text.DecimalFormat; @@ -82,7 +82,7 @@ public class Calc extends AbstractModule { */ @Override public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - if (Utils.isValidString(args)) { + if (StringUtils.isNotBlank(args)) { bot.send(calc(args)); } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 3baa613..cb8ed9c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -40,6 +40,7 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; +import org.apache.commons.lang3.StringUtils; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; @@ -105,7 +106,7 @@ public final class CurrencyConverter extends ThreadedModule { @SuppressFBWarnings("REDOS") @Override void run(final Mobibot bot, final String sender, final String query) { - if (Utils.isValidString(sender) && Utils.isValidString(query)) { + if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(query)) { if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { try { final Message msg = convertCurrency(query); @@ -146,7 +147,7 @@ public final class CurrencyConverter extends ThreadedModule { pubDate = cubeTime.getAttribute("time").getValue(); - final List cubes = cubeTime.getChildren(); + final List<Element> cubes = cubeTime.getChildren(); Element cube; for (final Object rawCube : cubes) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index ed26c4a..32b685b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -37,6 +37,7 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; +import org.apache.commons.lang3.StringUtils; import org.jibble.pircbot.Colors; import org.json.JSONArray; import org.json.JSONObject; @@ -96,7 +97,7 @@ public final class GoogleSearch extends ThreadedModule { */ @Override void run(final Mobibot bot, final String sender, final String query) { - if (Utils.isValidString(query)) { + if (StringUtils.isNotBlank(query)) { try { final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), properties.get(GOOGLE_CSE_KEY_PROP)); @@ -125,11 +126,11 @@ public final class GoogleSearch extends ThreadedModule { @SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops")) static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey) throws ModuleException { - if (!Utils.isValidString(apiKey) || !Utils.isValidString(cseKey)) { - throw new ModuleException(Utils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing."); + if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) { + throw new ModuleException(StringUtils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing."); } - if (Utils.isValidString(query)) { + if (StringUtils.isNotBlank(query)) { final ArrayList<Message> results = new ArrayList<>(); try { final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java index 5c516ea..13ab2fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java @@ -32,11 +32,8 @@ package net.thauvin.erik.mobibot.modules; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import okhttp3.HttpUrl; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import net.thauvin.erik.mobibot.Utils; +import org.apache.commons.lang3.StringUtils; /** * The <code>ModuleException</code> class. @@ -49,7 +46,6 @@ public class ModuleException extends Exception { private static final long serialVersionUID = 1L; private final String debugMessage; - private final Pattern urlPattern = Pattern.compile("(https?://\\S+)(\\?\\S+)"); /** * Creates a new exception. @@ -94,33 +90,19 @@ public class ModuleException extends Exception { } /** - * Return the sanitized (URL query parameters are replaced by char count) message. + * Return the sanitized message (e.g. remove API keys, etc.) * + * @param sanitize The words to sanitize. * @return The sanitized message. */ - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?") - String getSanitizedMessage() { - if (hasCause()) { - final String causeMessage = getCause().getMessage(); - final Matcher matcher = urlPattern.matcher(causeMessage); - if (matcher.find()) { - final HttpUrl url = HttpUrl.parse(matcher.group(1) + matcher.group(2)); - if (url != null) { - final StringBuilder query = new StringBuilder("?"); - final int size = url.querySize(); - for (int i = 0; i < size; i++) { - if (i > 0) { - query.append('&'); - } - query.append(url.queryParameterName(i)).append("=[") - .append(url.queryParameterValue(i).length()).append(']'); - } - return getDebugMessage() + "\nCaused by: " + getCause().getClass().getName() + ": " - + causeMessage.replace(matcher.group(2), query); - } - } + String getSanitizedMessage(final String... sanitize) { + final String[] obfuscate = new String[sanitize.length]; + for (int i = 0; i < sanitize.length; i++) { + obfuscate[i] = Utils.obfuscate(sanitize[i]); } - return getDebugMessage() + "\nCaused by: " + getCause().getClass().getName() + ": " + getCause().getMessage(); + return getCause().getClass().getName() + ": " + StringUtils.replaceEach(getCause().getMessage(), + sanitize, + obfuscate); } /** @@ -128,6 +110,7 @@ public class ModuleException extends Exception { * * @return <code>true</code> or <code>false</code> */ + @SuppressWarnings("unused") boolean hasCause() { return getCause() != null; } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 912f287..0b0fc79 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -42,6 +42,7 @@ import net.thauvin.erik.mobibot.msg.PublicMessage; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -68,7 +69,7 @@ public final class StockQuote extends ThreadedModule { */ static final String INVALID_SYMBOL = "Invalid symbol."; // The Alpha Advantage URL. - private static final String ALAPHADVANTAGE_URL = "https://www.alphavantage.co/query?function="; + private static final String ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function="; // The quote command. private static final String STOCK_CMD = "stock"; @@ -132,11 +133,11 @@ public final class StockQuote extends ThreadedModule { * @throws ModuleException If an errors occurs. */ static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { - if (!Utils.isValidString(apiKey)) { - throw new ModuleException(Utils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); + if (StringUtils.isBlank(apiKey)) { + throw new ModuleException(StringUtils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); } - if (Utils.isValidString(symbol)) { + if (StringUtils.isNotBlank(symbol)) { final String debugMessage = "getQuote(" + symbol + ')'; final ArrayList<Message> messages = new ArrayList<>(); final OkHttpClient client = new OkHttpClient(); @@ -144,7 +145,7 @@ public final class StockQuote extends ThreadedModule { try { // Search for symbol/keywords Request request = new Request.Builder().url( - ALAPHADVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build(); + ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build(); Response response = client.newCall(request).execute(); JSONObject json = getJsonResponse(response, debugMessage); @@ -159,7 +160,7 @@ public final class StockQuote extends ThreadedModule { // Get quote for symbol request = new Request.Builder().url( - ALAPHADVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" + ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" + apiKey).build(); response = client.newCall(request).execute(); @@ -210,7 +211,7 @@ public final class StockQuote extends ThreadedModule { */ @Override void run(final Mobibot bot, final String sender, final String symbol) { - if (Utils.isValidString(symbol)) { + if (StringUtils.isNotBlank(symbol)) { try { final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); for (final Message msg : messages) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index a166eba..aad3e42 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -45,10 +45,12 @@ import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; import okhttp3.HttpUrl; +import org.apache.commons.lang3.StringUtils; import org.jibble.pircbot.Colors; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * The <code>Weather2</code> module. @@ -105,8 +107,8 @@ public class Weather2 extends ThreadedModule { * @throws ModuleException If an error occurs while retrieving the weather data. */ static List<Message> getWeather(final String query, final String apiKey) throws ModuleException { - if (!Utils.isValidString(apiKey)) { - throw new ModuleException(Utils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing."); + if (StringUtils.isBlank(apiKey)) { + throw new ModuleException(StringUtils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing."); } final OWM owm = new OWM(apiKey); @@ -114,13 +116,13 @@ public class Weather2 extends ThreadedModule { owm.setUnit(OWM.Unit.IMPERIAL); - if (Utils.isValidString(query)) { + if (StringUtils.isNotBlank(query)) { final String[] argv = query.split(","); if (argv.length >= 1 && argv.length <= 2) { final String country; final String city = argv[0].trim(); - if (argv.length > 1 && Utils.isValidString(argv[1])) { + if (argv.length > 1 && StringUtils.isNotBlank(argv[1])) { country = argv[1].trim(); } else { country = "US"; @@ -160,7 +162,7 @@ public class Weather2 extends ThreadedModule { if (list != null) { for (final Weather w : list) { if (condition.indexOf(",") == -1) { - condition.append(Utils.capitalize(w.getDescription())); + condition.append(StringUtils.capitalize(w.getDescription())); } else { condition.append(", ").append(w.getDescription()); } @@ -174,11 +176,13 @@ public class Weather2 extends ThreadedModule { messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(), Colors.GREEN)); } else { - final HttpUrl url = - HttpUrl.parse("https://openweathermap.org/find").newBuilder().addQueryParameter( - "q", city + ',' + country).build(); - messages.add( - new NoticeMessage(url.toString(), Colors.GREEN)); + final HttpUrl url = Objects.requireNonNull(HttpUrl.parse( + "https://openweathermap.org/find")) + .newBuilder() + .addQueryParameter("q", + city + ',' + country) + .build(); + messages.add(new NoticeMessage(url.toString(), Colors.GREEN)); } } } @@ -204,8 +208,9 @@ public class Weather2 extends ThreadedModule { bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); bot.send(sender, "For example:"); bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); - bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") - + ". Zip codes are supported in most countries."); + bot.send(sender, + "The default ISO 3166 country code is " + Utils.bold("US") + + ". Zip codes are supported in most countries."); } /** @@ -213,7 +218,7 @@ public class Weather2 extends ThreadedModule { */ @Override void run(final Mobibot bot, final String sender, final String args) { - if (Utils.isValidString(args)) { + if (StringUtils.isNotBlank(args)) { try { final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); if (messages.get(0).isError()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java index dec97fa..17f70d2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java @@ -36,6 +36,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Commands; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import org.apache.commons.lang3.StringUtils; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -143,7 +144,7 @@ public class Tell { @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.") public void response(final String sender, final String cmds) { final String arrow = " --> "; - if (!Utils.isValidString(cmds)) { + if (StringUtils.isBlank(cmds)) { helpResponse(sender); } else if (cmds.startsWith(Commands.VIEW_CMD)) { if (bot.isOp(sender) && (Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) { @@ -249,7 +250,7 @@ public class Tell { } else { final String[] split = cmds.split(" ", 2); - if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" "))) { + if (split.length == 2 && (StringUtils.isNotBlank(split[1]) && split[1].contains(" "))) { if (messages.size() < maxSize) { final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); From 0c32333c75295ebf39f7e8adbc93925872faa17b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 Feb 2020 22:57:13 -0800 Subject: [PATCH 324/842] Added uptime(). --- .../net/thauvin/erik/mobibot/Mobibot.java | 209 ++++++------------ .../java/net/thauvin/erik/mobibot/Utils.java | 111 ++++++---- .../net/thauvin/erik/mobibot/UtilsTest.java | 52 ++--- 3 files changed, 164 insertions(+), 208 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c018f81..8c57234 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -62,6 +62,7 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -78,6 +79,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.lang.management.ManagementFactory; import java.nio.file.Files; import java.nio.file.Paths; import java.time.Clock; @@ -109,10 +111,9 @@ public class Mobibot extends PircBot { private static final String DEFAULT_SERVER = "irc.freenode.net"; // The info strings. - private static final String[] INFO_STRS = { - ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", - "https://www.mobitopia.org/mobibot/" - }; + private static final String[] INFO_STRS = + {ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", + "https://www.mobitopia.org/mobibot/"}; // The link match string. private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; @@ -132,39 +133,18 @@ public class Mobibot extends PircBot { // The modules. private static final List<AbstractModule> MODULES = new ArrayList<>(0); - // The start time. - private static final long START_TIME = System.currentTimeMillis(); - // The tags/categories marker. private static final String TAGS_MARKER = "tags:"; // The version strings. - private static final String[] VERSION_STRS = { - "Version: " - + ReleaseInfo.VERSION - + " (" - + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', - "Platform: " - + System.getProperty("os.name") - + " (" - + System.getProperty("os.version") - + ", " - + System.getProperty("os.arch") - + ", " - + System.getProperty("user.country") + ')', - "Runtime: " - + System.getProperty("java.runtime.name") - + " (build " - + System.getProperty("java.runtime.version") - + ')', - "VM: " - + System.getProperty("java.vm.name") - + " (build " - + System.getProperty("java.vm.version") - + ", " - + System.getProperty("java.vm.info") - + ')' - }; + private static final String[] VERSION_STRS = {"Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate( + ReleaseInfo.BUILDDATE) + ')', "Platform: " + System.getProperty("os.name") + " (" + System.getProperty( + "os.version") + ", " + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + " (build " + + System.getProperty("java.runtime.version") + ')', + "VM: " + System.getProperty("java.vm.name") + " (build " + System + .getProperty("java.vm.version") + ", " + System.getProperty( + "java.vm.info") + ')'}; // The logger. private static final Logger logger = LogManager.getLogger(Mobibot.class); // The commands list. @@ -223,9 +203,9 @@ public class Mobibot extends PircBot { public Mobibot(final String nickname, final String channel, final String logsDirPath, final Properties p) { super(); System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", - String.valueOf(Constants.CONNECT_TIMEOUT)); + String.valueOf(Constants.CONNECT_TIMEOUT)); System.getProperties().setProperty("sun.net.client.defaultReadTimeout", - String.valueOf(Constants.CONNECT_TIMEOUT)); + String.valueOf(Constants.CONNECT_TIMEOUT)); setName(nickname); @@ -274,8 +254,7 @@ public class Mobibot extends PircBot { setLogin(p.getProperty("login", getName())); setVersion(p.getProperty("weblog", "")); setMessageDelay(MESSAGE_DELAY); - setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), - p.getProperty("ident-msg", "")); + setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", "")); // Set the URLs setWeblogUrl(getVersion()); @@ -304,12 +283,11 @@ public class Mobibot extends PircBot { MODULES.add(new WorldTime()); // Load the modules properties - MODULES.stream().filter(AbstractModule::hasProperties).forEach( - module -> { - for (final String s : module.getPropertyKeys()) { - module.setProperty(s, p.getProperty(s, "")); - } - }); + MODULES.stream().filter(AbstractModule::hasProperties).forEach(module -> { + for (final String s : module.getPropertyKeys()) { + module.setProperty(s, p.getProperty(s, "")); + } + }); // Get the tell command settings tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); @@ -340,7 +318,7 @@ public class Mobibot extends PircBot { * @param action The action. */ private void action(final String channel, final String action) { - if (Utils.isValidString(channel) && Utils.isValidString(action)) { + if (StringUtils.isNotBlank(channel) && StringUtils.isNotBlank(action)) { sendAction(channel, action); } } @@ -364,8 +342,7 @@ public class Mobibot extends PircBot { if (retries == MAX_RECONNECT) { if (logger.isDebugEnabled()) { logger.debug( - "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", - ex); + "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", ex); } e.printStackTrace(System.err); @@ -388,7 +365,7 @@ public class Mobibot extends PircBot { * @param sender The nick of the person who sent the private message. */ private void feedResponse(final String sender) { - if (Utils.isValidString(feedUrl)) { + if (StringUtils.isNotBlank(feedUrl)) { new Thread(new FeedReader(this, sender, feedUrl)).start(); } else { send(sender, "There is no weblog setup for this channel."); @@ -478,10 +455,8 @@ public class Mobibot extends PircBot { for (final char c : getNick().toCharArray()) { if (Character.isLetter(c)) { - buff.append('[') - .append(String.valueOf(c).toLowerCase(Constants.LOCALE)) - .append(String.valueOf(c).toUpperCase(Constants.LOCALE)) - .append(']'); + buff.append('[').append(String.valueOf(c).toLowerCase(Constants.LOCALE)).append( + String.valueOf(c).toUpperCase(Constants.LOCALE)).append(']'); } else { buff.append(c); } @@ -651,11 +626,8 @@ public class Mobibot extends PircBot { if (isOp(sender)) { send(sender, "The op commands are:"); send(sender, helpIndent( - Commands.CYCLE_CMD + " " - + Commands.ME_CMD + " " - + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " - + Commands.VERSION_CMD)); + Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " + Commands.SAY_CMD + + " " + Commands.VERSION_CMD)); } } } @@ -666,12 +638,12 @@ public class Mobibot extends PircBot { */ private void identify() { // Identify with NickServ - if (Utils.isValidString(identPwd)) { + if (StringUtils.isNotBlank(identPwd)) { identify(identPwd); } // Identify with a specified nick - if (Utils.isValidString(identNick) && Utils.isValidString(identMsg)) { + if (StringUtils.isNotBlank(identNick) && StringUtils.isNotBlank(identMsg)) { sendMessage(identNick, identMsg); } } @@ -740,40 +712,9 @@ public class Mobibot extends PircBot { final StringBuilder info = new StringBuilder(28).append("Uptime: "); - long timeInSeconds = (System.currentTimeMillis() - START_TIME) / 1000L; + info.append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())); - final long years = timeInSeconds / 31540000L; - - if (years > 0) { - info.append(years).append(Utils.plural(years, " year ", " years ")); - timeInSeconds -= (years * 31540000L); - } - - final long weeks = timeInSeconds / 604800L; - - if (weeks > 0) { - info.append(weeks).append(Utils.plural(weeks, " week ", " weeks ")); - timeInSeconds -= (weeks * 604800L); - } - - final long days = timeInSeconds / 86400L; - - if (days > 0) { - info.append(days).append(Utils.plural(days, " day ", " days ")); - timeInSeconds -= (days * 86400L); - } - - final long hours = timeInSeconds / 3600L; - - if (hours > 0) { - info.append(hours).append(Utils.plural(hours, " hour ", " hours ")); - timeInSeconds -= (hours * 3600L); - } - - final long minutes = timeInSeconds / 60L; - - info.append(minutes).append(Utils.plural(minutes, " minute ", " minutes ")) - .append("[Entries: ").append(entries.size()); + info.append("[Entries: ").append(entries.size()); if (tell.isEnabled() && isOp(sender)) { info.append(", Messages: ").append(tell.size()); @@ -791,7 +732,7 @@ public class Mobibot extends PircBot { * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. */ private boolean isIgnoredNick(final String nick) { - return Utils.isValidString(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE)); + return StringUtils.isNotBlank(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE)); } /** @@ -826,18 +767,17 @@ public class Mobibot extends PircBot { * * @param args The command line arguments. */ - @SuppressFBWarnings({"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING", - "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS"}) + @SuppressFBWarnings( + {"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING", "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS"}) @SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidFileStream"}) public static void main(final String[] args) { // Setup the command line options final Options options = new Options(); options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, - "print debug & logging data directly to the console"); - options.addOption(Option.builder( - Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc("use " + "alternate properties file") - .longOpt(Commands.PROPS_ARG).build()); + "print debug & logging data directly to the console"); + options.addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc( + "use " + "alternate properties file").longOpt(Commands.PROPS_ARG).build()); options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); // Parse the command line @@ -862,8 +802,8 @@ public class Mobibot extends PircBot { } else { final Properties p = new Properties(); - try (final InputStream fis = Files.newInputStream(Paths.get( - line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + try (final InputStream fis = Files.newInputStream( + Paths.get(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { // Load the properties files p.load(fis); } catch (FileNotFoundException e) { @@ -883,8 +823,8 @@ public class Mobibot extends PircBot { // Redirect the stdout and stderr if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { try { - final PrintStream stdout = new PrintStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + final PrintStream stdout = new PrintStream( + new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); System.setOut(stdout); } catch (IOException e) { System.err.println("Unable to open output (stdout) log file."); @@ -893,8 +833,7 @@ public class Mobibot extends PircBot { } try { - final PrintStream stderr = new PrintStream( - new FileOutputStream(logsDir + nickname + ".err", true)); + final PrintStream stderr = new PrintStream(new FileOutputStream(logsDir + nickname + ".err", true)); System.setErr(stderr); } catch (IOException e) { System.err.println("Unable to open error (stderr) log file."); @@ -916,7 +855,7 @@ public class Mobibot extends PircBot { */ @Override protected final void onDisconnect() { - if (Utils.isValidString(weblogUrl)) { + if (StringUtils.isNotBlank(weblogUrl)) { setVersion(weblogUrl); } @@ -930,10 +869,7 @@ public class Mobibot extends PircBot { */ @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.") @Override - protected final void onMessage(final String channel, - final String sender, - final String login, - final String hostname, + protected final void onMessage(final String channel, final String sender, final String login, final String hostname, final String message) { if (logger.isDebugEnabled()) { logger.debug(">>> {} : {}", sender, message); @@ -971,7 +907,7 @@ public class Mobibot extends PircBot { if (data.length == 1) { title = data[0].trim(); } else { - if (Utils.isValidString(data[0])) { + if (StringUtils.isNotBlank(data[0])) { title = data[0].trim(); } @@ -984,7 +920,7 @@ public class Mobibot extends PircBot { final Document html = Jsoup.connect(link).userAgent("Mozilla").get(); final String htmlTitle = html.title(); - if (Utils.isValidString(htmlTitle)) { + if (StringUtils.isNotBlank(htmlTitle)) { title = htmlTitle; } } catch (IOException ignore) { @@ -1010,8 +946,7 @@ public class Mobibot extends PircBot { } } else { final EntryLink entry = entries.get(dupIndex); - send(sender, Utils.bold("Duplicate") + " >> " - + EntriesUtils.buildLink(dupIndex, entry)); + send(sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry)); } } } else if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> @@ -1236,9 +1171,7 @@ public class Mobibot extends PircBot { */ @SuppressFBWarnings(value = {"DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, we want to bail out.") @Override - protected final void onPrivateMessage(final String sender, - final String login, - final String hostname, + protected final void onPrivateMessage(final String sender, final String login, final String hostname, final String message) { if (logger.isDebugEnabled()) { logger.debug(">>> {} : {}", sender, message); @@ -1346,12 +1279,9 @@ public class Mobibot extends PircBot { * {@inheritDoc} */ @Override - protected final void onAction(final String sender, - final String login, - final String hostname, - final String target, + protected final void onAction(final String sender, final String login, final String hostname, final String target, final String action) { - if (target.equals(ircChannel)) { + if (target != null && target.equals(ircChannel)) { storeRecap(sender, action, true); } } @@ -1406,7 +1336,7 @@ public class Mobibot extends PircBot { * sent. */ public final void send(final String sender, final String message, final boolean isPrivate) { - if (Utils.isValidString(message) && Utils.isValidString(sender)) { + if (StringUtils.isNotBlank(message) && StringUtils.isNotBlank(sender)) { if (isPrivate) { if (logger.isDebugEnabled()) { logger.debug("Sending message to {} : {}", sender, message); @@ -1472,6 +1402,7 @@ public class Mobibot extends PircBot { * @param message The actual message. * @param color The message's color. */ + @SuppressWarnings("unused") public final void send(final String who, final String message, final String color) { send(who, Utils.colorize(message, color), false); } @@ -1513,7 +1444,7 @@ public class Mobibot extends PircBot { * @param nicks The nicks to ignore */ final void setIgnoredNicks(final String nicks) { - if (Utils.isValidString(nicks)) { + if (StringUtils.isNotBlank(nicks)) { final StringTokenizer st = new StringTokenizer(nicks, ","); while (st.hasMoreTokens()) { @@ -1528,7 +1459,7 @@ public class Mobibot extends PircBot { * @param apiToken The API token */ final void setPinboardAuth(final String apiToken) { - if (Utils.isValidString(apiToken)) { + if (StringUtils.isNotBlank(apiToken)) { pinboard = new Pinboard(this, apiToken, ircServer); } } @@ -1558,7 +1489,7 @@ public class Mobibot extends PircBot { */ private static void sleep(final int secs) { try { - Thread.sleep((long) (secs * 1000)); + Thread.sleep(secs * 1000L); } catch (InterruptedException ignore) { // Do nothing. } @@ -1573,7 +1504,7 @@ public class Mobibot extends PircBot { */ private void storeRecap(final String sender, final String message, final boolean isAction) { recap.add(Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + " -> " + sender + (isAction ? " " : ": ") - + message); + + message); if (recap.size() > MAX_RECAP) { recap.remove(0); @@ -1586,16 +1517,13 @@ public class Mobibot extends PircBot { * @param msg The twitter message. */ final void twitterNotification(final String msg) { - if (twitterModule.isEnabled() && Utils.isValidString(twitterHandle)) { + if (twitterModule.isEnabled() && StringUtils.isNotBlank(twitterHandle)) { new Thread(() -> { try { - twitterModule.post( - twitterHandle, - getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true); + twitterModule.post(twitterHandle, getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true); } catch (ModuleException e) { if (logger.isWarnEnabled()) { - logger.warn( - "Failed to notify @" + twitterHandle + ": " + msg, e); + logger.warn("Failed to notify @" + twitterHandle + ": " + msg, e); } } }).start(); @@ -1693,14 +1621,14 @@ public class Mobibot extends PircBot { entry = entries.get(i); if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase(Constants.LOCALE).contains(lcArgs)) - || (entry.getTitle().toLowerCase(Constants.LOCALE).contains(lcArgs)) + if ((entry.getLink().toLowerCase(Constants.LOCALE).contains(lcArgs)) || (entry.getTitle() + .toLowerCase( + Constants.LOCALE) + .contains(lcArgs)) || (entry.getNick().toLowerCase(Constants.LOCALE).contains(lcArgs))) { if (sent > MAX_ENTRIES) { - send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), - isPrivate); + send(sender, "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), isPrivate); break; } @@ -1711,9 +1639,8 @@ public class Mobibot extends PircBot { } else { if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), - isPrivate); + "To view more, try: " + Utils.bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), + isPrivate); break; } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index b32f261..ad7346d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot; +import org.apache.commons.lang3.StringUtils; import org.jibble.pircbot.Colors; import java.io.File; @@ -39,6 +40,7 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; +import java.util.concurrent.TimeUnit; /** * Miscellaneous utilities class. @@ -77,16 +79,6 @@ public final class Utils { return colorize(s, Colors.BOLD); } - /** - * Capitalize a string. - * - * @param s The string. - * @return The capitalized string. - */ - public static String capitalize(final String s) { - return s.substring(0, 1).toUpperCase(Constants.LOCALE) + s.substring(1); - } - /** * Colorize a string. * @@ -95,8 +87,8 @@ public final class Utils { * @return The colorized string. */ static String colorize(final String s, final String color) { - if (!Utils.isValidString(color) || Colors.NORMAL.equals(color)) { - return s; + if (s == null) { + return Colors.NORMAL; } else if (Colors.BOLD.equals(color) || Colors.REVERSE.equals(color)) { return color + s + color; } @@ -105,7 +97,7 @@ public final class Utils { } /** - * Meks the given string cyan. + * Makes the given string cyan. * * @param s The string. * @return The cyan string. @@ -166,25 +158,6 @@ public final class Utils { return colorize(s, Colors.DARK_GREEN); } - /** - * Returns <code>true</code> if the given string is <em>not</em> blank or null. - * - * @param s The string to check. - * @return <code>true</code> if the string is valid, <code>false</code> otherwise. - */ - public static boolean isValidString(final CharSequence s) { - if (s == null || s.length() == 0) { - return false; - } - final int len = s.length(); - for (int i = 0; i < len; i++) { - if (!Character.isWhitespace(s.charAt(i))) { - return true; - } - } - return false; - } - /** * Returns the specified date as an ISO local date string. * @@ -205,6 +178,19 @@ public final class Utils { return date.format(DateTimeFormatter.ISO_LOCAL_DATE); } + /** + * Obfuscates the given string. + * + * @param s The string. + * @return The obfuscated string. + */ + public static String obfuscate(final String s) { + if (StringUtils.isNotBlank(s)) { + return StringUtils.repeat('x', s.length()); + } + return s; + } + /** * Returns the plural form of a word, if count > 1. * @@ -247,19 +233,66 @@ public final class Utils { * @return The unescaped string. */ public static String unescapeXml(final String str) { - return str.replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace(""", "\"") - .replace("'", "'") - .replace("'", "'"); + return str.replace("&", "&").replace("<", "<").replace(">", ">").replace(""", "\"").replace( + "'", "'").replace("'", "'"); + } + + /** + * Converts milliseconds to year month week day hour and minutes. + * + * @param uptime The uptime in milliseconds. + * @return The uptime in year month week day hours and minutes. + */ + public static String uptime(final long uptime) { + final StringBuilder info = new StringBuilder(); + + long days = TimeUnit.MILLISECONDS.toDays(uptime); + final long years = days / 365; + days %= 365; + final long months = days / 30; + days %= 30; + final long weeks = days / 7; + days %= 7; + final long hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours( + TimeUnit.MILLISECONDS.toDays(uptime)); + final long minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( + TimeUnit.MILLISECONDS.toHours(uptime)); + + if (years > 0) { + info.append(years).append(plural(years, " year ", " years ")); + } + + if (months > 0) { + info.append(weeks).append(plural(months, " month ", " months ")); + } + + if (weeks > 0) { + info.append(weeks).append(plural(weeks, " week ", " weeks ")); + } + + + if (days > 0) { + info.append(days).append(plural(days, " day ", " days ")); + + } + + if (hours > 0) { + info.append(hours).append(plural(hours, " hour ", " hours ")); + + } + + if (minutes > 0) { + info.append(minutes).append(plural(minutes, " minute", " minutes")); + } + + return info.toString(); } /** * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>. * * @param date The date. - * @return The fromatted date. + * @return The formatted date. */ public static String utcDateTime(final Date date) { return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index 3727f01..df2ff57 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot; +import org.apache.commons.lang3.StringUtils; import org.jibble.pircbot.Colors; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -54,8 +55,7 @@ public class UtilsTest { " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; private final Calendar cal = Calendar.getInstance(); - private final LocalDateTime localDateTime = - LocalDateTime.of(1952, 2, 17, 12, 30, 0); + private final LocalDateTime localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0); @BeforeClass public void setUp() { @@ -68,17 +68,12 @@ public class UtilsTest { assertThat(Utils.bold(ASCII)).as("bold(ascii").isEqualTo(Colors.BOLD + ASCII + Colors.BOLD); } - @Test - public void testCapitalize() { - assertThat(Utils.capitalize("this is a test.")).isEqualTo("This is a test."); - } - @Test public void testColorize() { - assertThat(Utils.colorize(ASCII, Colors.REVERSE)).as("reverse") - .isEqualTo(Colors.REVERSE + ASCII + Colors.REVERSE); - assertThat(Utils.colorize(ASCII, Colors.RED)).as("red") - .isEqualTo(Colors.RED + ASCII + Colors.NORMAL); + assertThat(Utils.colorize(ASCII, Colors.REVERSE)).as("colorize(reverse)").isEqualTo( + Colors.REVERSE + ASCII + Colors.REVERSE); + assertThat(Utils.colorize(ASCII, Colors.RED)).as("colorize(red)").isEqualTo(Colors.RED + ASCII + Colors.NORMAL); + assertThat(Utils.colorize(null, Colors.RED)).as("colorize(null)").isEqualTo(Colors.NORMAL); } @Test @@ -88,10 +83,9 @@ public class UtilsTest { @Test public void testEnsureDir() { - assertThat(Utils.ensureDir("dir", false)).as("ensureDir(dir, false)") - .isEqualTo("dir" + File.separatorChar); - assertThat(Utils.ensureDir("https://erik.thauvin.net", true)) - .as("ensureDir(erik.thauvin.net, true)").isEqualTo("https://erik.thauvin.net/"); + assertThat(Utils.ensureDir("dir", false)).as("ensureDir(dir, false)").isEqualTo("dir" + File.separatorChar); + assertThat(Utils.ensureDir("https://erik.thauvin.net", true)).as("ensureDir(erik.thauvin.net, true)").isEqualTo( + "https://erik.thauvin.net/"); } @Test @@ -105,21 +99,19 @@ public class UtilsTest { assertThat(Utils.green(ASCII)).isEqualTo(Colors.DARK_GREEN + ASCII + Colors.NORMAL); } - @Test - public void testIsValidString() { - assertThat(Utils.isValidString(ASCII)).as("isValidString(ascii)").isTrue(); - assertThat(Utils.isValidString("")).as("isValidString(empty)").isFalse(); - assertThat(Utils.isValidString(" ")).as("isValidString( )").isFalse(); - assertThat(Utils.isValidString(" \t ")).as("isValidString(tab)").isFalse(); - assertThat(Utils.isValidString(null)).as("isValidString(null)").isFalse(); - } - @Test public void testIsoLocalDate() { assertThat(Utils.isoLocalDate(cal.getTime())).as("isoLocalDate(date)").isEqualTo("1952-02-17"); assertThat(Utils.isoLocalDate(localDateTime)).as("isoLocalDate(localDate)").isEqualTo("1952-02-17"); } + @Test + public void testObfuscate() { + assertThat(Utils.obfuscate(ASCII).length()).as("obfuscate is right length").isEqualTo(ASCII.length()); + assertThat(Utils.obfuscate(ASCII)).as("obfuscate()").isEqualTo(StringUtils.repeat("x", ASCII.length())); + assertThat(Utils.obfuscate(" ")).as("obfuscate(blank)").isEqualTo(" "); + } + @Test public void testPlural() { final String week = "week"; @@ -143,14 +135,18 @@ public class UtilsTest { @Test public void testUnescapeXml() { - assertThat(Utils.unescapeXml("<a name="test & ''">")) - .isEqualTo("<a name=\"test & ''\">"); + assertThat(Utils.unescapeXml("<a name="test & ''">")).isEqualTo( + "<a name=\"test & ''\">"); + } + + @Test + public void testUptime() { + assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(Utils.uptime(547800300076L)); } @Test public void testUtcDateTime() { assertThat(Utils.utcDateTime(cal.getTime())).as("utcDateTime(date)").isEqualTo("1952-02-17 12:30"); - assertThat(Utils.utcDateTime(localDateTime)).as("utcDateTime(localDate)") - .isEqualTo("1952-02-17 12:30"); + assertThat(Utils.utcDateTime(localDateTime)).as("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30"); } } From 78808c0f15c4e6fab7f2b5fb8865a6b70b64b7bf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 Feb 2020 22:58:12 -0800 Subject: [PATCH 325/842] Updated wrapper. --- .idea/modules/mobibot.iml | 11 ++++---- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 58695 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 29 +++++++++----------- mobibot.ipr | 33 ++++++++++++++--------- 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml index f0bdc97..9de0d06 100644 --- a/.idea/modules/mobibot.iml +++ b/.idea/modules/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+500" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+547" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../out/production/classes" /> <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> @@ -22,14 +22,15 @@ <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.12.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.12.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.1.1" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.2.0" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> @@ -37,8 +38,8 @@ <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta3" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta4" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.2.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.50" level="project" /> diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..f3d88b1c2faf2fc91d853cd5d4242b5547257070 100644 GIT binary patch delta 22808 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 delta 20005 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7c4388a..1b16c34 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acf..2fe81a7 100755 --- a/gradlew +++ b/gradlew @@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/mobibot.ipr b/mobibot.ipr index 49557ed..763dc54 100644 --- a/mobibot.ipr +++ b/mobibot.ipr @@ -1437,13 +1437,13 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta3"> + <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta4"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta3/65ce8bfb2bcc1abd1adc5816fc19c687cdad77b5/spotbugs-annotations-4.0.0-beta3.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta4/4c2237f748f5af8a1655a20945ddec334708b0cc/spotbugs-annotations-4.0.0-beta4.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta3/672866c39247ef05484380d72024dec31ca0cf82/spotbugs-annotations-4.0.0-beta3-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta4/d5e1583e2019b1ee5f9823b6d96156f92dc1e666/spotbugs-annotations-4.0.0-beta4-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6"> @@ -1473,31 +1473,31 @@ <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/c5b4c491aecb72e7c32a78da0b5c6b9cda8dee0f/gson-2.8.5-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome-utils:1.12.1"> + <library name="Gradle: com.rometools:rome-utils:1.12.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.1/e14b9757402f0971fabe245f8a3ee7c889151f26/rome-utils-1.12.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.1/4823407506599821643d09519ba2193f520c259a/rome-utils-1.12.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/d27c01295ababef9a2d6266dec6d22b0ad137dce/rome-utils-1.12.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.rometools:rome:1.12.1"> + <library name="Gradle: com.rometools:rome:1.12.2"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.1/e9038b34b001007b2a1f3823c532f3524222075f/rome-1.12.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.1/46920b2dae8ec033a2948a07f58cfc133276070b/rome-1.12.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/f87f5f50b5a7f2c7e4b8e593ec644e7803660daf/rome-1.12.2-sources.jar!/" /> </SOURCES> </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:4.1.1"> + <library name="Gradle: com.squareup.okhttp3:okhttp:4.2.0"> <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.1.1/4c8c6d55ec16c575131f293c9b2cb7b705809a83/okhttp-4.1.1.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.2.0/446acd5a6fc0cf117c5df424af09b38f461d29e6/okhttp-4.2.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.1.1/8b14260502684c226128e726b27a52a3a65ee721/okhttp-4.1.1-sources.jar!/" /> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.2.0/d89a60517bd1e90121b22510494e22497d8be28b/okhttp-4.2.0-sources.jar!/" /> </SOURCES> </library> <library name="Gradle: com.squareup.okio:okio:2.2.2"> @@ -1581,6 +1581,15 @@ <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0-sources.jar!/" /> </SOURCES> </library> + <library name="Gradle: org.apache.commons:commons-lang3:3.9"> + <CLASSES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/8f1cb192e229bc4cd1c900c51171d96706e6d195/commons-lang3-3.9-sources.jar!/" /> + </SOURCES> + </library> <library name="Gradle: org.apache.logging.log4j:log4j-api:2.12.1"> <CLASSES> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.1/a55e6d987f50a515c9260b0451b4fa217dc539cb/log4j-api-2.12.1.jar!/" /> From 5a39d3ee236b64678c59c2c070351c6c1807c379 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 Feb 2020 23:00:23 -0800 Subject: [PATCH 326/842] Update dependencies. --- .circleci/config.yml | 5 ++-- build.gradle | 39 +++++++++++++++++++------------- config/checkstyle/checkstyle.xml | 26 ++++++++++++++------- settings.gradle | 2 -- 4 files changed, 43 insertions(+), 29 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b6d9624..a20eb37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,3 +1,4 @@ +version: 2 defaults: &defaults working_directory: ~/repo environment: @@ -28,14 +29,12 @@ defaults_gradle: &defaults_gradle - store_test_results: path: build/reports/ -version: 2.0 - jobs: build_gradle_jdk12: <<: *defaults docker: - - image: circleci/openjdk:11-jdk + - image: openjdk:12-jdk <<: *defaults_gradle diff --git a/build.gradle b/build.gradle index cf21967..6598a14 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,10 @@ plugins { id 'jacoco' id 'java' id 'pmd' - id "com.github.ben-manes.versions" version "0.25.0" - id "com.github.spotbugs" version "2.0.0" + id "com.github.ben-manes.versions" version "0.27.0" + id "com.github.spotbugs" version "3.0.0" id "net.thauvin.erik.gradle.semver" version "1.0.4" - id "org.sonarqube" version "2.7.1" + id "org.sonarqube" version "2.8" } @@ -24,9 +24,9 @@ mainClassName = packageName + '.Mobibot' ext { versions = [ - kotlin : '1.3.50', - log4j : '2.12.1', - spotbugs_annotations : '4.0.0-beta3' + kotlin : '1.3.61', + log4j : '2.13.0', + spotbugs : '4.0.0-RC3' ] } @@ -47,15 +47,16 @@ dependencies { compile "org.apache.logging.log4j:log4j-core:$versions.log4j" compile "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" + compile 'org.apache.commons:commons-lang3:3.9' compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:4.2.0' + compile 'com.squareup.okhttp3:okhttp:4.3.1' - compile 'com.rometools:rome:1.12.1' + compile 'com.rometools:rome:1.12.2' compile 'org.json:json:20190722' - compile 'org.jsoup:jsoup:1.12.1' + compile 'org.jsoup:jsoup:1.12.2' compile 'net.objecthunter:exp4j:0.4.8' compile 'org.twitter4j:twitter4j-core:4.0.7' @@ -66,14 +67,14 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" - testImplementation 'org.testng:testng:7.0.0' - testImplementation 'org.assertj:assertj-core:3.13.2' + testImplementation 'org.testng:testng:7.1.1' + testImplementation 'org.assertj:assertj-core:3.15.0' - spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' - spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.6' + spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' + spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' - compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs_annotations" - testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs_annotations" + compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" + testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" } test { @@ -82,9 +83,15 @@ test { } } +spotbugs { + toolVersion = versions.spotbugs +} + pmd { - ruleSetFiles = files("config/pmd.xml") + ignoreFailures = true ruleSets = [] + ruleSetFiles = files("${projectDir}/config/pmd.xml") + consoleOutput = true } tasks.withType(JavaCompile) { diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index c426276..c5f6465 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -32,10 +32,27 @@ <property name="eachLine" value="true"/> </module> + <module name="LineLength"> + <property name="max" value="120"/> + <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> + </module> + <module name="SuppressWarningsFilter"/> + <module name="SuppressWithPlainTextCommentFilter"> + <property name="offCommentFormat" value="CHECKSTYLE_OFF"/> + <property name="onCommentFormat" value="CHECKSTYLE_ON"/> + <property name="checkFormat" + value="^((?!(FileTabCharacterCheck)).)*$"/> + </module> + <module name="TreeWalker"> <module name="SuppressWarningsHolder"/> + <module name="SuppressionCommentFilter"> + <property name="offCommentFormat" value="CHECKSTYLE_OFF: ALMOST_ALL"/> + <property name="onCommentFormat" value="CHECKSTYLE_ON: ALMOST_ALL"/> + <property name="checkFormat" value="^((?!(EqualsHashCode)).)*$"/> + </module> <module name="OuterTypeFilename"/> <module name="IllegalTokenText"> <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/> @@ -49,10 +66,6 @@ <property name="allowByTailComment" value="true"/> <property name="allowNonPrintableEscapes" value="true"/> </module> - <module name="LineLength"> - <property name="max" value="120"/> - <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> - </module> <module name="AvoidStarImport"/> <module name="OneTopLevelClass"/> <module name="NoLineWrap"/> @@ -245,11 +258,8 @@ <module name="JavadocMethod"> <property name="scope" value="public"/> <property name="allowMissingParamTags" value="true"/> - <property name="allowMissingThrowsTags" value="true"/> <property name="allowMissingReturnTag" value="true"/> - <property name="minLineCount" value="2"/> <property name="allowedAnnotations" value="Override, Test"/> - <property name="allowThrowsTagsForSubclasses" value="true"/> </module> <module name="MethodName"> <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/> @@ -262,6 +272,6 @@ <module name="EmptyCatchBlock"> <property name="exceptionVariableName" value="expected"/> </module> - <module name="CommentsIndentation"/> + <!-- <module name="CommentsIndentation"/> --> </module> </module> diff --git a/settings.gradle b/settings.gradle index 21faf72..48e3810 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1 @@ -enableFeaturePreview('STABLE_PUBLISHING') - rootProject.name = 'mobibot' From fffc51ca15e35fabcaad01bdddc2fa8e37738c84 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 Feb 2020 23:01:50 -0800 Subject: [PATCH 327/842] Sanitized error messages. --- .../mobibot/modules/GoogleSearchTest.java | 2 +- .../mobibot/modules/ModuleExceptionTest.java | 34 ++++++++----------- .../erik/mobibot/modules/StockQuoteTest.java | 2 +- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index ad25467..0ba872f 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -80,7 +80,7 @@ public class GoogleSearchTest extends LocalProperties { } catch (ModuleException e) { // Avoid displaying api keys in CI logs. if ("true".equals(System.getenv("CI"))) { - throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage()); + throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey, cseKey)); } else { throw e; } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java index a2faff9..46199c9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java @@ -53,14 +53,13 @@ public class ModuleExceptionTest { @DataProvider(name = "dp") Object[][] createData(final Method m) { - return new Object[][]{new Object[]{new ModuleException(debugMessage, message, - new IOException("Secret URL http://foo.com?apiKey=sec&userID=me"))}, - new Object[]{new ModuleException(debugMessage, message, - new IOException("URL http://foobar.com"))}, - new Object[]{new ModuleException(debugMessage, message, - new IOException("URL http://foobar.com?"))}, - new Object[]{new ModuleException(debugMessage, message)} - }; + return new Object[][]{new Object[]{new ModuleException(debugMessage, + message, + new IOException("URL http://foobar.com"))}, + new Object[]{new ModuleException(debugMessage, + message, + new IOException("URL http://foobar.com?"))}, + new Object[]{new ModuleException(debugMessage, message)}}; } @Test(dataProvider = "dp") @@ -73,16 +72,13 @@ public class ModuleExceptionTest { assertThat(e.getMessage()).as("get message").isEqualTo(message); } - @Test(dataProvider = "dp") - final void testGetSanitizedMessage(final ModuleException e) { - if (e.hasCause()) { - if (e.getSanitizedMessage().contains("Secret")) { - assertThat(e.getSanitizedMessage()).as("get sanitized url") - .contains("http://foo.com?apiKey=[3]&userID=[2]"); - } else { - assertThat(e.getSanitizedMessage()).as("get sanitized url") - .contains("http://foobar.com"); - } - } + @Test + final void testGetSanitizedMessage() { + final String apiKey = "1234567890"; + final ModuleException e = new ModuleException(debugMessage, + message, + new IOException( + "URL http://foo.com?apiKey=" + apiKey + "&userID=me")); + assertThat(e.getSanitizedMessage(apiKey)).as("sanitized url").contains("xxxxxxxxxx").doesNotContain(apiKey); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 77de00b..f1e60c0 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -75,7 +75,7 @@ public class StockQuoteTest extends LocalProperties { } catch (ModuleException e) { // Avoid displaying api keys in CI logs. if ("true".equals(System.getenv("CI"))) { - throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage()); + throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey)); } else { throw e; } From 12a1655d3fa91aaed0f58426e7df68096715379c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 Feb 2020 23:45:08 -0800 Subject: [PATCH 328/842] Always output minutes in uptime(). --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index ad7346d..5228e93 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -273,17 +273,13 @@ public final class Utils { if (days > 0) { info.append(days).append(plural(days, " day ", " days ")); - } if (hours > 0) { info.append(hours).append(plural(hours, " hour ", " hours ")); - } - if (minutes > 0) { - info.append(minutes).append(plural(minutes, " minute", " minutes")); - } + info.append(minutes).append(plural(minutes, " minute", " minutes")); return info.toString(); } From 4877c7ab12c694006b2c463f50119a6d89a64405 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 Feb 2020 23:46:37 -0800 Subject: [PATCH 329/842] PMD fixes. --- config/pmd.xml | 33 +++++++++----- .../net/thauvin/erik/mobibot/Mobibot.java | 9 ++-- .../net/thauvin/erik/mobibot/Pinboard.java | 36 +++++++-------- .../thauvin/erik/mobibot/TwitterOAuth.java | 44 ++++++++++--------- .../erik/mobibot/entries/EntryComment.java | 1 + .../thauvin/erik/mobibot/modules/Joke.java | 1 - .../erik/mobibot/modules/StockQuote.java | 1 + .../net/thauvin/erik/mobibot/msg/Message.java | 3 ++ .../erik/mobibot/modules/Weather2Test.java | 2 +- 9 files changed, 73 insertions(+), 57 deletions(-) diff --git a/config/pmd.xml b/config/pmd.xml index 5cfeaf2..65fe3ec 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -5,22 +5,27 @@ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>Erik's Ruleset</description> <!-- BEST PRACTICES --> - <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/> + <!-- <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/>--> <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/> <rule ref="category/java/bestpractices.xml/CheckResultSet"/> <rule ref="category/java/bestpractices.xml/ConstantsInInterface"/> <rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitchStmt"/> <rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/> <rule ref="category/java/bestpractices.xml/GuardLogStatement"/> - <rule ref="category/java/bestpractices.xml/LooseCoupling"/> + <!-- <rule ref="category/java/bestpractices.xml/LooseCoupling"/> --> <rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray"/> - <rule ref="category/java/bestpractices.xml/MissingOverride"/> + <rule ref="category/java/bestpractices.xml/MissingOverride"> + <properties> + <property name="violationSuppressXPath" + value="./MethodDeclarator[@Image='hashCode' or @Image='equals' or @Image='toString']"/> + </properties> + </rule> <rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInCaseInsensitiveComparisons"/> <rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInComparisons"/> <rule ref="category/java/bestpractices.xml/PreserveStackTrace"/> <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/> - <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> - <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/> + <!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> + <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/> --> <rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/> <rule ref="category/java/bestpractices.xml/SystemPrintln"/> <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/> @@ -41,7 +46,7 @@ <rule ref="category/java/codestyle.xml/GenericsNaming"/> <rule ref="category/java/codestyle.xml/LocalVariableNamingConventions"/> <rule ref="category/java/codestyle.xml/MethodNamingConventions"/> - <rule ref="category/java/codestyle.xml/PackageCase"/> + <!-- <rule ref="category/java/codestyle.xml/PackageCase"/>--> <!-- OTHER --> @@ -84,7 +89,7 @@ <rule ref="category/java/design.xml/DataClass"/> <rule ref="category/java/design.xml/DoNotExtendJavaLangError"/> <rule ref="category/java/design.xml/ExceptionAsFlowControl"/> - <rule ref="category/java/design.xml/ExcessivePublicCount"/> + <!-- <rule ref="category/java/design.xml/ExcessivePublicCount"/>--> <rule ref="category/java/design.xml/FinalFieldCouldBeStatic"/> <rule ref="category/java/design.xml/ImmutableField"/> <rule ref="category/java/design.xml/LogicInversion"/> @@ -108,6 +113,8 @@ <rule ref="category/java/errorprone.xml/AssignmentInOperand"> <properties> <property name="allowWhile" value="true"/> + <property name="allowFor" value="true"/> + <property name="allowIf" value="true"/> </properties> </rule> <rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"/> @@ -117,7 +124,11 @@ <rule ref="category/java/errorprone.xml/AvoidCallingFinalize"/> <rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"/> <rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/> - <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals"/> + <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals"> + <properties> + <property name="skipAnnotations" value="true"/> + </properties> + </rule> <rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier"/> <rule ref="category/java/errorprone.xml/AvoidInstanceofChecksInCatchClause"/> <rule ref="category/java/errorprone.xml/AvoidLosingExceptionInformation"/> @@ -150,7 +161,7 @@ <rule ref="category/java/errorprone.xml/IdempotentOperations"/> <rule ref="category/java/errorprone.xml/ImportFromSamePackage"/> <rule ref="category/java/errorprone.xml/InstantiationToGetClass"/> - <rule ref="category/java/errorprone.xml/InvalidSlf4jMessageFormat"/> + <rule ref="category/java/errorprone.xml/InvalidLogMessageFormat"/> <rule ref="category/java/errorprone.xml/JumbledIncrementer"/> <rule ref="category/java/errorprone.xml/JUnitSpelling"/> <rule ref="category/java/errorprone.xml/JUnitStaticSuite"/> @@ -162,7 +173,7 @@ <rule ref="category/java/errorprone.xml/MoreThanOneLogger"/> <rule ref="category/java/errorprone.xml/NonCaseLabelInSwitchStatement"/> <rule ref="category/java/errorprone.xml/NonStaticInitializer"/> - <rule ref="category/java/errorprone.xml/NullAssignment"/> + <!-- <rule ref="category/java/errorprone.xml/NullAssignment"/>--> <rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"/> <rule ref="category/java/errorprone.xml/ProperCloneImplementation"/> <rule ref="category/java/errorprone.xml/ProperLogger"/> @@ -240,7 +251,7 @@ <rule ref="category/java/performance.xml/StringToString"/> <rule ref="category/java/performance.xml/TooFewBranchesForASwitchStatement"/> <rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation"/> - <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector"/> + <!-- <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector"/> --> <rule ref="category/java/performance.xml/UseArraysAsList"/> <rule ref="category/java/performance.xml/UseIndexOfChar"/> <rule ref="category/java/performance.xml/UselessStringValueOf"/> diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 8c57234..a9a3d7a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -710,11 +710,10 @@ public class Mobibot extends PircBot { } } - final StringBuilder info = new StringBuilder(28).append("Uptime: "); + final StringBuilder info = new StringBuilder(28); - info.append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())); - - info.append("[Entries: ").append(entries.size()); + info.append("Uptime: ").append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())).append( + " [Entries: ").append(entries.size()); if (tell.isEnabled() && isOp(sender)) { info.append(", Messages: ").append(tell.size()); @@ -769,7 +768,7 @@ public class Mobibot extends PircBot { */ @SuppressFBWarnings( {"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING", "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS"}) - @SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidFileStream"}) + @SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource"}) public static void main(final String[] args) { // Setup the command line options final Options options = new Options(); diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 175fdc4..b9f538d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -45,7 +45,7 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * The class to handle posts to pinbard.in. + * The class to handle posts to pinboard.in. * * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> * @created 2017-05-17 @@ -81,14 +81,14 @@ class Pinboard { * @param entry The entry to add. */ final void addPost(final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { @Override protected Boolean doInBackground() { return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate())); + entry.getTitle(), + postedBy(entry), + entry.getPinboardTags(), + formatDate(entry.getDate())); } }; @@ -103,7 +103,7 @@ class Pinboard { final void deletePost(final EntryLink entry) { final String link = entry.getLink(); - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { @Override protected Boolean doInBackground() { return pinboard.deletePin(link); @@ -140,25 +140,25 @@ class Pinboard { * @param entry The entry to add. */ final void updatePost(final String oldUrl, final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { @Override protected Boolean doInBackground() { if (!oldUrl.equals(entry.getLink())) { pinboard.deletePin(oldUrl); return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate())); + entry.getTitle(), + postedBy(entry), + entry.getPinboardTags(), + formatDate(entry.getDate())); } else { return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate()), - true, - true); + entry.getTitle(), + postedBy(entry), + entry.getPinboardTags(), + formatDate(entry.getDate()), + true, + true); } } }; diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 05b64d7..dc2f984 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -34,6 +34,8 @@ public final class TwitterOAuth { * Twitter OAuth Client Registration. * * @param args The consumerKey and consumerSecret should be passed as arguments. + * @throws TwitterException If an error occurs. + * @throws IOException If an IO error occurs. */ @SuppressFBWarnings({"DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE"}) @SuppressWarnings({"PMD.AvoidPrintStackTrace", "PMD.SystemPrintln"}) @@ -43,29 +45,29 @@ public final class TwitterOAuth { twitter.setOAuthConsumer(args[0], args[1]); final RequestToken requestToken = twitter.getOAuthRequestToken(); AccessToken accessToken = null; - final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - while (null == accessToken) { - System.out.println("Open the following URL and grant access to your account:"); - System.out.println(requestToken.getAuthorizationURL()); - System.out.print("Enter the PIN (if available) or just hit enter.[PIN]:"); - final String pin = br.readLine(); - try { - if (pin != null && pin.length() > 0) { - accessToken = twitter.getOAuthAccessToken(requestToken, pin); - } else { - accessToken = twitter.getOAuthAccessToken(); - } + try (final BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { + while (null == accessToken) { + System.out.println("Open the following URL and grant access to your account:"); + System.out.println(requestToken.getAuthorizationURL()); + System.out.print("Enter the PIN (if available) or just hit enter.[PIN]:"); + final String pin = br.readLine(); + try { + if (pin != null && pin.length() > 0) { + accessToken = twitter.getOAuthAccessToken(requestToken, pin); + } else { + accessToken = twitter.getOAuthAccessToken(); + } - System.out.println( - "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" + System.out.println( + "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" - + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken - .getTokenSecret()); - } catch (TwitterException te) { - if (401 == te.getStatusCode()) { - System.out.println("Unable to get the access token."); - } else { - te.printStackTrace(); + + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken.getTokenSecret()); + } catch (TwitterException te) { + if (401 == te.getStatusCode()) { + System.out.println("Unable to get the access token."); + } else { + te.printStackTrace(); + } } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java index 1dcf4eb..cc0b244 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java @@ -42,6 +42,7 @@ import java.time.LocalDateTime; * @created Jan 31, 2004 * @since 1.0 */ +@SuppressWarnings({"PMD.DataClass"}) public class EntryComment implements Serializable { // The serial version UID. static final long serialVersionUID = 1L; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index b1b826a..4c4c1ad 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -87,7 +87,6 @@ public final class Joke extends ThreadedModule { final JSONObject json = new JSONObject(sb.toString()); - //noinspection RegExpRedundantEscape return new PublicMessage( json.getJSONObject("value").get("joke").toString().replace("\\'", "'") .replace("\\\"", "\"")); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 0b0fc79..d9cc1c6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -132,6 +132,7 @@ public final class StockQuote extends ThreadedModule { * @return The {@link Message} array containing the stock quote. * @throws ModuleException If an errors occurs. */ + @SuppressWarnings({"PMD.CloseResource"}) static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { if (StringUtils.isBlank(apiKey)) { throw new ModuleException(StringUtils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 290d3c9..9ee7af2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -63,6 +63,7 @@ public class Message { * @param isError The error flag. * @param isPrivate The Private message */ + @SuppressWarnings("unused") public Message(final String message, final boolean isNotice, final boolean isError, final boolean isPrivate) { msg = message; this.isNotice = isNotice; @@ -80,6 +81,7 @@ public class Message { * @param isPrivate The Private message * @param color The color. */ + @SuppressWarnings("unused") public Message(final String message, final boolean isNotice, final boolean isError, @@ -178,6 +180,7 @@ public class Message { * * @param isPrivate The private flag. */ + @SuppressWarnings("unused") public void setPrivate(final boolean isPrivate) { this.isPrivate = isPrivate; } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index bfc80eb..f39c459 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -59,7 +59,7 @@ public class Weather2Test extends LocalProperties { messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getMessage()).as("is UK").contains("London").contains("UK"); - assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Code").endsWith("4298960"); + assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Code").endsWith("4517009"); assertThatThrownBy( () -> Weather2.getWeather("Montpellier, FR", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP))).as( From f8834501e7fe3f76b65810ec4c8f91ebe2fa0cc3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 Feb 2020 00:35:04 -0800 Subject: [PATCH 330/842] Removed IDEA ipr. --- .gitignore | 9 +- .idea/checkstyle-idea.xml | 16 + .idea/compiler.xml | 20 + .idea/jarRepositories.xml | 30 + .idea/misc.xml | 10 + .idea/mobibot.iml | 12 + .idea/modules.xml | 10 + .idea/modules/mobibot.main.iml | 47 + .idea/modules/mobibot.test.iml | 56 + mobibot.ipr | 1763 -------------------------------- 10 files changed, 207 insertions(+), 1766 deletions(-) create mode 100644 .idea/checkstyle-idea.xml create mode 100644 .idea/compiler.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/mobibot.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/modules/mobibot.main.iml create mode 100644 .idea/modules/mobibot.test.iml delete mode 100644 mobibot.ipr diff --git a/.gitignore b/.gitignore index 72be2b8..4d04f45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +__pycache__ !.vscode/extensions.json !.vscode/launch.json !.vscode/settings.json @@ -25,9 +26,10 @@ .idea/**/uiDesigner.xml .idea/**/usage.statistics.xml .idea/**/workspace.xml -.idea/caches/build_file_checksums.ser -.idea/httpRequests -.idea/replstate.xml +.idea/**/caches/build_file_checksums.ser +.idea/**/httpRequests +.idea/**/replstate.xml +.idea/**/shelf/ .kobalt .mtj.tmp/ .mvn/timing.properties @@ -84,3 +86,4 @@ release.properties target/ test-output Thumbs.db +venv diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml new file mode 100644 index 0000000..21a0f73 --- /dev/null +++ b/.idea/checkstyle-idea.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CheckStyle-IDEA"> + <option name="configuration"> + <map> + <entry key="checkstyle-version" value="8.29" /> + <entry key="copy-libs" value="true" /> + <entry key="location-0" value="BUNDLED:(bundled):Sun Checks" /> + <entry key="location-1" value="BUNDLED:(bundled):Google Checks" /> + <entry key="scan-before-checkin" value="false" /> + <entry key="scanscope" value="JavaOnly" /> + <entry key="suppress-errors" value="false" /> + </map> + </option> + </component> +</project> \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..c370bd1 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <annotationProcessing> + <profile name="Gradle Imported" enabled="true"> + <outputRelativeToContentRoot value="true" /> + <option name="semver.project.dir" value="K:\java\mobibot" /> + <processorPath useClasspath="false"> + <entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" /> + <entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" /> + </processorPath> + <module name="mobibot.main" /> + </profile> + </annotationProcessing> + <bytecodeTargetLevel> + <module name="mobibot.main" target="13" /> + <module name="mobibot.test" target="13" /> + </bytecodeTargetLevel> + </component> +</project> \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..d32dd97 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RemoteRepositoriesConfiguration"> + <remote-repository> + <option name="id" value="central" /> + <option name="name" value="Maven Central repository" /> + <option name="url" value="https://repo1.maven.org/maven2" /> + </remote-repository> + <remote-repository> + <option name="id" value="jboss.community" /> + <option name="name" value="JBoss Community repository" /> + <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> + </remote-repository> + <remote-repository> + <option name="id" value="MavenRepo" /> + <option name="name" value="MavenRepo" /> + <option name="url" value="https://repo.maven.apache.org/maven2/" /> + </remote-repository> + <remote-repository> + <option name="id" value="MavenLocal" /> + <option name="name" value="MavenLocal" /> + <option name="url" value="file:/$MAVEN_REPOSITORY$/" /> + </remote-repository> + <remote-repository> + <option name="id" value="BintrayJCenter" /> + <option name="name" value="BintrayJCenter" /> + <option name="url" value="https://jcenter.bintray.com/" /> + </remote-repository> + </component> +</project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5dd7423 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="FrameworkDetectionExcludesConfiguration"> + <file type="web" url="file://$PROJECT_DIR$" /> + </component> + <component name="JavaScriptSettings"> + <option name="languageLevel" value="ES6" /> + </component> + <component name="ProjectRootManager" version="2" project-jdk-name="13" project-jdk-type="JavaSDK" /> +</project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml new file mode 100644 index 0000000..524d563 --- /dev/null +++ b/.idea/mobibot.iml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+549" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.gradle" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2150ab5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/mobibot.iml" filepath="$PROJECT_DIR$/.idea/mobibot.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.main.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.test.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.test.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml new file mode 100644 index 0000000..cd62ea3 --- /dev/null +++ b/.idea/modules/mobibot.main.iml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+549" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_13"> + <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../../src/generated/java"> + <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" generated="true" /> + </content> + <content url="file://$MODULE_DIR$/../../src/main"> + <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.3.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-RC3" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml new file mode 100644 index 0000000..4976f18 --- /dev/null +++ b/.idea/modules/mobibot.test.iml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+549" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_13"> + <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../../src/test"> + <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="mobibot.main" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.3.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-RC3" level="project" /> + <orderEntry type="library" name="Gradle: org.testng:testng:7.1.1" level="project" /> + <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.15.0" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: com.beust:jcommander:1.72" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> + <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> + <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.1.0" level="project" /> + <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.ant:ant-launcher:1.10.3" level="project" /> + <orderEntry type="library" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" /> + <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> + <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.guava:guava:19.0" level="project" /> + </component> + <component name="TestModuleProperties" production-module="mobibot.main" /> +</module> \ No newline at end of file diff --git a/mobibot.ipr b/mobibot.ipr deleted file mode 100644 index 763dc54..0000000 --- a/mobibot.ipr +++ /dev/null @@ -1,1763 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="AnalysisProjectProfileManager"> - <option name="PROJECT_PROFILE" value="Project Default" /> - <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" /> - <scopes /> - <profiles> - <profile profile_name="Project Default" version="1.0" is_locked="false"> - <coding_rule class="AM_CREATES_EMPTY_JAR_FILE_ENTRY" level="MAJOR" enabled="true" /> - <coding_rule class="AM_CREATES_EMPTY_ZIP_FILE_ENTRY" level="MAJOR" enabled="true" /> - <coding_rule class="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION" level="CRITICAL" enabled="false" /> - <coding_rule class="AbstractClassNameCheck" level="MAJOR" enabled="false"> - <param name="ignoreModifier" value="false" /> - <param name="ignoreName" value="false" /> - </coding_rule> - <coding_rule class="AnnotationUseStyleCheck" level="MAJOR" enabled="false" /> - <coding_rule class="AnonInnerLengthCheck" level="MAJOR" enabled="true" /> - <coding_rule class="ArrayTrailingCommaCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ArrayTypeStyleCheck" level="INFO" enabled="false" /> - <coding_rule class="AvoidInlineConditionalsCheck" level="INFO" enabled="false" /> - <coding_rule class="AvoidNestedBlocksCheck" level="MAJOR" enabled="false" /> - <coding_rule class="AvoidStarImportCheck" level="INFO" enabled="false"> - <param name="allowClassImports" value="false" /> - <param name="allowStaticMemberImports" value="false" /> - </coding_rule> - <coding_rule class="AvoidStaticImportCheck" level="MAJOR" enabled="false" /> - <coding_rule class="BAC_BAD_APPLET_CONSTRUCTOR" level="MAJOR" enabled="false" /> - <coding_rule class="BC_BAD_CAST_TO_ABSTRACT_COLLECTION" level="MAJOR" enabled="true" /> - <coding_rule class="BC_BAD_CAST_TO_CONCRETE_COLLECTION" level="CRITICAL" enabled="true" /> - <coding_rule class="BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS" level="CRITICAL" enabled="true" /> - <coding_rule class="BC_IMPOSSIBLE_CAST" level="BLOCKER" enabled="true" /> - <coding_rule class="BC_IMPOSSIBLE_DOWNCAST" level="MAJOR" enabled="false" /> - <coding_rule class="BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY" level="MAJOR" enabled="false" /> - <coding_rule class="BC_IMPOSSIBLE_INSTANCEOF" level="CRITICAL" enabled="true" /> - <coding_rule class="BC_UNCONFIRMED_CAST" level="CRITICAL" enabled="true" /> - <coding_rule class="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" level="CRITICAL" enabled="false" /> - <coding_rule class="BC_VACUOUS_INSTANCEOF" level="CRITICAL" enabled="true" /> - <coding_rule class="BIT_ADD_OF_SIGNED_BYTE" level="CRITICAL" enabled="true" /> - <coding_rule class="BIT_AND" level="CRITICAL" enabled="true" /> - <coding_rule class="BIT_AND_ZZ" level="CRITICAL" enabled="true" /> - <coding_rule class="BIT_IOR" level="CRITICAL" enabled="true" /> - <coding_rule class="BIT_IOR_OF_SIGNED_BYTE" level="CRITICAL" enabled="true" /> - <coding_rule class="BIT_SIGNED_CHECK" level="CRITICAL" enabled="true" /> - <coding_rule class="BIT_SIGNED_CHECK_HIGH_BIT" level="CRITICAL" enabled="true" /> - <coding_rule class="BOA_BADLY_OVERRIDDEN_ADAPTER" level="CRITICAL" enabled="true" /> - <coding_rule class="BSHIFT_WRONG_ADD_PRIORITY" level="MAJOR" enabled="false" /> - <coding_rule class="BX_BOXING_IMMEDIATELY_UNBOXED" level="MAJOR" enabled="true" /> - <coding_rule class="BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION" level="MAJOR" enabled="true" /> - <coding_rule class="BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR" level="MAJOR" enabled="true" /> - <coding_rule class="BX_UNBOXING_IMMEDIATELY_REBOXED" level="CRITICAL" enabled="false" /> - <coding_rule class="BestPracticesAbstractClassWithoutAbstractMethod" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesAccessorClassGeneration" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesAccessorMethodGeneration" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesArrayIsStoredDirectly" level="CRITICAL" enabled="true" /> - <coding_rule class="BestPracticesAvoidPrintStackTrace" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesAvoidReassigningParameters" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesAvoidStringBufferField" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesAvoidUsingHardCodedIP" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesCheckResultSet" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesConstantsInInterface" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesDefaultLabelNotLastInSwitchStmt" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesDontNestJsfInJstlIteration" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesForLoopCanBeForeach" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesGuardDebugLogging" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesGuardLogStatement" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesGuardLogStatementJavaUtil" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnit4SuitesShouldUseSuiteAnnotation" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnit4TestShouldUseAfterAnnotation" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnit4TestShouldUseBeforeAnnotation" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnit4TestShouldUseTestAnnotation" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnitAssertionsShouldIncludeMessage" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnitTestContainsTooManyAsserts" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnitTestsShouldIncludeAssert" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesJUnitUseExpected" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesLooseCoupling" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesMethodReturnsInternalArray" level="CRITICAL" enabled="false" /> - <coding_rule class="BestPracticesNoClassAttribute" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesNoHtmlComments" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesNoJspForward" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesOneDeclarationPerLine" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesPositionLiteralsFirstInCaseInsensitiveComparisons" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesPositionLiteralsFirstInComparisons" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesPreserveStackTrace" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesReplaceEnumerationWithIterator" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesReplaceHashtableWithMap" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesReplaceVectorWithList" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesSwitchStmtsShouldHaveDefault" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesSystemPrintln" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesUnusedFormalParameter" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesUnusedImports" level="INFO" enabled="false" /> - <coding_rule class="BestPracticesUnusedLocalVariable" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesUnusedPrivateField" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesUnusedPrivateMethod" level="MAJOR" enabled="true" /> - <coding_rule class="BestPracticesUseAssertEqualsInsteadOfAssertTrue" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesUseAssertNullInsteadOfAssertTrue" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesUseAssertSameInsteadOfAssertTrue" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesUseAssertTrueInsteadOfAssertEquals" level="MAJOR" enabled="false" /> - <coding_rule class="BestPracticesUseCollectionIsEmpty" level="MINOR" enabled="false" /> - <coding_rule class="BestPracticesUseVarargs" level="MAJOR" enabled="false" /> - <coding_rule class="BooleanExpressionComplexityCheck" level="MAJOR" enabled="true" /> - <coding_rule class="CAA_COVARIANT_ARRAY_ELEMENT_STORE" level="MAJOR" enabled="false" /> - <coding_rule class="CAA_COVARIANT_ARRAY_FIELD" level="MAJOR" enabled="false" /> - <coding_rule class="CAA_COVARIANT_ARRAY_LOCAL" level="MAJOR" enabled="false" /> - <coding_rule class="CAA_COVARIANT_ARRAY_RETURN" level="MAJOR" enabled="false" /> - <coding_rule class="CD_CIRCULAR_DEPENDENCY" level="MAJOR" enabled="false" /> - <coding_rule class="CI_CONFUSED_INHERITANCE" level="MINOR" enabled="true" /> - <coding_rule class="CNT_ROUGH_CONSTANT_VALUE" level="MAJOR" enabled="false" /> - <coding_rule class="CN_IDIOM" level="MAJOR" enabled="true" /> - <coding_rule class="CN_IDIOM_NO_SUPER_CALL" level="MAJOR" enabled="true" /> - <coding_rule class="CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE" level="MAJOR" enabled="true" /> - <coding_rule class="CO_ABSTRACT_SELF" level="MAJOR" enabled="true" /> - <coding_rule class="CO_COMPARETO_INCORRECT_FLOATING" level="MAJOR" enabled="false" /> - <coding_rule class="CO_COMPARETO_RESULTS_MIN_VALUE" level="CRITICAL" enabled="false" /> - <coding_rule class="CO_SELF_NO_OBJECT" level="MAJOR" enabled="true" /> - <coding_rule class="ClassDataAbstractionCouplingCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ClassFanOutComplexityCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ClassTypeParameterNameCheck" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleAbstractNaming" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleAtLeastOneConstructor" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleAvoidDollarSigns" level="MINOR" enabled="true" /> - <coding_rule class="CodeStyleAvoidFinalLocalVariable" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleAvoidPrefixingMethodParameters" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleAvoidProtectedFieldInFinalClass" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleAvoidProtectedMethodInFinalClassNotExtending" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleAvoidUsingNativeCode" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleBooleanGetMethodName" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleCallSuperInConstructor" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleClassNamingConventions" level="MAJOR" enabled="true" /> - <coding_rule class="CodeStyleCommentDefaultAccessModifier" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleConfusingTernary" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleDefaultPackage" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleDontImportJavaLang" level="MINOR" enabled="true" /> - <coding_rule class="CodeStyleDuplicateImports" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleDuplicateJspImports" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleEmptyMethodInAbstractClassShouldBeAbstract" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleExtendsObject" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleFieldDeclarationsShouldBeAtStartOfClass" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleForLoopShouldBeWhileLoop" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleForLoopsMustUseBraces" level="MAJOR" enabled="true" /> - <coding_rule class="CodeStyleGenericsNaming" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleIfElseStmtsMustUseBraces" level="MAJOR" enabled="true" /> - <coding_rule class="CodeStyleIfStmtsMustUseBraces" level="MAJOR" enabled="true" /> - <coding_rule class="CodeStyleLocalHomeNamingConvention" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleLocalInterfaceSessionNamingConvention" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleLocalVariableCouldBeFinal" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleLongVariable" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleMDBAndSessionBeanNamingConvention" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleMIsLeadingVariableName" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleMethodArgumentCouldBeFinal" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleMethodNamingConventions" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleMisleadingVariableName" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleNoPackage" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleOnlyOneReturn" level="MINOR" enabled="false" /> - <coding_rule class="CodeStylePackageCase" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStylePrematureDeclaration" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleRemoteInterfaceNamingConvention" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleRemoteSessionInterfaceNamingConvention" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleShortClassName" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleShortMethodName" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleShortVariable" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleSuspiciousConstantFieldName" level="MAJOR" enabled="true" /> - <coding_rule class="CodeStyleTooManyStaticImports" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleUnnecessaryConstructor" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleUnnecessaryFinalModifier" level="INFO" enabled="false" /> - <coding_rule class="CodeStyleUnnecessaryFullyQualifiedName" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleUnnecessaryLocalBeforeReturn" level="MAJOR" enabled="true" /> - <coding_rule class="CodeStyleUnnecessaryModifier" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleUnnecessaryParentheses" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleUnnecessaryReturn" level="MINOR" enabled="false" /> - <coding_rule class="CodeStyleUselessParentheses" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleUselessQualifiedThis" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleVariableNamingConventions" level="MAJOR" enabled="false" /> - <coding_rule class="CodeStyleWhileLoopsMustUseBraces" level="MAJOR" enabled="true" /> - <coding_rule class="ConstantNameCheck" level="INFO" enabled="true" /> - <coding_rule class="CovariantEqualsCheck" level="MAJOR" enabled="false" /> - <coding_rule class="CyclomaticComplexityCheck" level="MAJOR" enabled="true" /> - <coding_rule class="DB_DUPLICATE_BRANCHES" level="CRITICAL" enabled="true" /> - <coding_rule class="DB_DUPLICATE_SWITCH_CLAUSES" level="CRITICAL" enabled="true" /> - <coding_rule class="DC_DOUBLECHECK" level="MAJOR" enabled="true" /> - <coding_rule class="DC_PARTIALLY_CONSTRUCTED" level="MAJOR" enabled="false" /> - <coding_rule class="DE_MIGHT_DROP" level="MAJOR" enabled="true" /> - <coding_rule class="DE_MIGHT_IGNORE" level="MAJOR" enabled="true" /> - <coding_rule class="DLS_DEAD_LOCAL_INCREMENT_IN_RETURN" level="MAJOR" enabled="false" /> - <coding_rule class="DLS_DEAD_LOCAL_STORE" level="CRITICAL" enabled="true" /> - <coding_rule class="DLS_DEAD_LOCAL_STORE_IN_RETURN" level="CRITICAL" enabled="true" /> - <coding_rule class="DLS_DEAD_LOCAL_STORE_OF_NULL" level="CRITICAL" enabled="true" /> - <coding_rule class="DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD" level="CRITICAL" enabled="false" /> - <coding_rule class="DLS_DEAD_STORE_OF_CLASS_LITERAL" level="CRITICAL" enabled="true" /> - <coding_rule class="DLS_OVERWRITTEN_INCREMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="DL_SYNCHRONIZATION_ON_BOOLEAN" level="CRITICAL" enabled="true" /> - <coding_rule class="DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE" level="CRITICAL" enabled="true" /> - <coding_rule class="DL_SYNCHRONIZATION_ON_SHARED_CONSTANT" level="CRITICAL" enabled="true" /> - <coding_rule class="DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION" level="MAJOR" enabled="true" /> - <coding_rule class="DMI_ARGUMENTS_WRONG_ORDER" level="CRITICAL" enabled="false" /> - <coding_rule class="DMI_BAD_MONTH" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE" level="CRITICAL" enabled="false" /> - <coding_rule class="DMI_BLOCKING_METHODS_ON_URL" level="BLOCKER" enabled="true" /> - <coding_rule class="DMI_CALLING_NEXT_FROM_HASNEXT" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_COLLECTION_OF_URLS" level="BLOCKER" enabled="true" /> - <coding_rule class="DMI_CONSTANT_DB_PASSWORD" level="BLOCKER" enabled="true" /> - <coding_rule class="DMI_DOH" level="CRITICAL" enabled="false" /> - <coding_rule class="DMI_EMPTY_DB_PASSWORD" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS" level="CRITICAL" enabled="false" /> - <coding_rule class="DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR" level="MINOR" enabled="true" /> - <coding_rule class="DMI_HARDCODED_ABSOLUTE_FILENAME" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_INVOKING_HASHCODE_ON_ARRAY" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_INVOKING_TOSTRING_ON_ARRAY" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_NONSERIALIZABLE_OBJECT_WRITTEN" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_RANDOM_USED_ONLY_ONCE" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS" level="MINOR" enabled="true" /> - <coding_rule class="DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED" level="MAJOR" enabled="true" /> - <coding_rule class="DMI_UNSUPPORTED_METHOD" level="MAJOR" enabled="true" /> - <coding_rule class="DMI_USELESS_SUBSTRING" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION" level="CRITICAL" enabled="true" /> - <coding_rule class="DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD" level="MINOR" enabled="true" /> - <coding_rule class="DMI_VACUOUS_SELF_COLLECTION_CALL" level="CRITICAL" enabled="true" /> - <coding_rule class="DM_BOOLEAN_CTOR" level="MAJOR" enabled="true" /> - <coding_rule class="DM_BOXED_PRIMITIVE_FOR_COMPARE" level="MAJOR" enabled="false" /> - <coding_rule class="DM_BOXED_PRIMITIVE_FOR_PARSING" level="MAJOR" enabled="false" /> - <coding_rule class="DM_BOXED_PRIMITIVE_TOSTRING" level="MAJOR" enabled="true" /> - <coding_rule class="DM_CONVERT_CASE" level="INFO" enabled="true" /> - <coding_rule class="DM_DEFAULT_ENCODING" level="CRITICAL" enabled="false" /> - <coding_rule class="DM_EXIT" level="MAJOR" enabled="true" /> - <coding_rule class="DM_FP_NUMBER_CTOR" level="MAJOR" enabled="true" /> - <coding_rule class="DM_GC" level="MAJOR" enabled="true" /> - <coding_rule class="DM_INVALID_MIN_MAX" level="MAJOR" enabled="false" /> - <coding_rule class="DM_MONITOR_WAIT_ON_CONDITION" level="MAJOR" enabled="true" /> - <coding_rule class="DM_NEW_FOR_GETCLASS" level="MAJOR" enabled="true" /> - <coding_rule class="DM_NEXTINT_VIA_NEXTDOUBLE" level="MAJOR" enabled="true" /> - <coding_rule class="DM_NUMBER_CTOR" level="CRITICAL" enabled="true" /> - <coding_rule class="DM_RUN_FINALIZERS_ON_EXIT" level="MAJOR" enabled="true" /> - <coding_rule class="DM_STRING_CTOR" level="MAJOR" enabled="true" /> - <coding_rule class="DM_STRING_TOSTRING" level="INFO" enabled="true" /> - <coding_rule class="DM_STRING_VOID_CTOR" level="MAJOR" enabled="true" /> - <coding_rule class="DM_USELESS_THREAD" level="MAJOR" enabled="true" /> - <coding_rule class="DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED" level="MAJOR" enabled="true" /> - <coding_rule class="DP_DO_INSIDE_DO_PRIVILEGED" level="MAJOR" enabled="true" /> - <coding_rule class="DeclarationOrderCheck" level="INFO" enabled="false"> - <param name="ignoreConstructors" value="false" /> - <param name="ignoreMethods" value="false" /> - <param name="ignoreModifiers" value="false" /> - </coding_rule> - <coding_rule class="DefaultComesLastCheck" level="MAJOR" enabled="true" /> - <coding_rule class="DescendantTokenCheck" level="MAJOR" enabled="false" /> - <coding_rule class="DesignAbstractClassWithoutAnyMethod" level="MAJOR" enabled="false" /> - <coding_rule class="DesignAvoidCatchingGenericException" level="MAJOR" enabled="false" /> - <coding_rule class="DesignAvoidDeeplyNestedIfStmts" level="MAJOR" enabled="false" /> - <coding_rule class="DesignAvoidRethrowingException" level="MAJOR" enabled="true" /> - <coding_rule class="DesignAvoidThrowingNewInstanceOfSameException" level="MAJOR" enabled="false" /> - <coding_rule class="DesignAvoidThrowingNullPointerException" level="MAJOR" enabled="true" /> - <coding_rule class="DesignAvoidThrowingRawExceptionTypes" level="MAJOR" enabled="true" /> - <coding_rule class="DesignClassWithOnlyPrivateConstructorsShouldBeFinal" level="MAJOR" enabled="false" /> - <coding_rule class="DesignCollapsibleIfStatements" level="MINOR" enabled="true" /> - <coding_rule class="DesignCouplingBetweenObjects" level="MAJOR" enabled="false" /> - <coding_rule class="DesignCyclomaticComplexity" level="MAJOR" enabled="false" /> - <coding_rule class="DesignDataClass" level="MAJOR" enabled="false" /> - <coding_rule class="DesignDoNotExtendJavaLangError" level="MAJOR" enabled="false" /> - <coding_rule class="DesignExceptionAsFlowControl" level="MAJOR" enabled="true" /> - <coding_rule class="DesignExcessiveClassLength" level="MAJOR" enabled="false" /> - <coding_rule class="DesignExcessiveImports" level="MAJOR" enabled="false" /> - <coding_rule class="DesignExcessiveMethodLength" level="MAJOR" enabled="false" /> - <coding_rule class="DesignExcessiveParameterList" level="MAJOR" enabled="false" /> - <coding_rule class="DesignExcessivePublicCount" level="MAJOR" enabled="false" /> - <coding_rule class="DesignFinalFieldCouldBeStatic" level="MINOR" enabled="true" /> - <coding_rule class="DesignForExtensionCheck" level="INFO" enabled="true" /> - <coding_rule class="DesignGodClass" level="MAJOR" enabled="false" /> - <coding_rule class="DesignImmutableField" level="MAJOR" enabled="false" /> - <coding_rule class="DesignLawOfDemeter" level="MAJOR" enabled="false" /> - <coding_rule class="DesignLogicInversion" level="MAJOR" enabled="false" /> - <coding_rule class="DesignLoosePackageCoupling" level="MAJOR" enabled="false" /> - <coding_rule class="DesignModifiedCyclomaticComplexity" level="MAJOR" enabled="false" /> - <coding_rule class="DesignNPathComplexity" level="MAJOR" enabled="false" /> - <coding_rule class="DesignNcssConstructorCount" level="MAJOR" enabled="false" /> - <coding_rule class="DesignNcssCount" level="MAJOR" enabled="false" /> - <coding_rule class="DesignNcssMethodCount" level="MAJOR" enabled="true" /> - <coding_rule class="DesignNcssTypeCount" level="MAJOR" enabled="true" /> - <coding_rule class="DesignNoInlineScript" level="MAJOR" enabled="false" /> - <coding_rule class="DesignNoInlineStyleInformation" level="MAJOR" enabled="false" /> - <coding_rule class="DesignNoLongScripts" level="MAJOR" enabled="false" /> - <coding_rule class="DesignNoScriptlets" level="MAJOR" enabled="false" /> - <coding_rule class="DesignSignatureDeclareThrowsException" level="MAJOR" enabled="true" /> - <coding_rule class="DesignSimplifiedTernary" level="MAJOR" enabled="false" /> - <coding_rule class="DesignSimplifyBooleanAssertion" level="MAJOR" enabled="false" /> - <coding_rule class="DesignSimplifyBooleanExpressions" level="MINOR" enabled="false" /> - <coding_rule class="DesignSimplifyBooleanReturns" level="MINOR" enabled="false" /> - <coding_rule class="DesignSimplifyConditional" level="MAJOR" enabled="true" /> - <coding_rule class="DesignSingularField" level="MINOR" enabled="true" /> - <coding_rule class="DesignStdCyclomaticComplexity" level="MAJOR" enabled="false" /> - <coding_rule class="DesignSwitchDensity" level="MAJOR" enabled="false" /> - <coding_rule class="DesignTooManyFields" level="MAJOR" enabled="false" /> - <coding_rule class="DesignTooManyMethods" level="MAJOR" enabled="false" /> - <coding_rule class="DesignUseObjectForClearerAPI" level="MAJOR" enabled="false" /> - <coding_rule class="DesignUseUtilityClass" level="MAJOR" enabled="false" /> - <coding_rule class="DesignUselessOverridingMethod" level="MAJOR" enabled="true" /> - <coding_rule class="DocumentationCommentContent" level="MAJOR" enabled="false" /> - <coding_rule class="DocumentationCommentRequired" level="MAJOR" enabled="false" /> - <coding_rule class="DocumentationCommentSize" level="MAJOR" enabled="false" /> - <coding_rule class="DocumentationUncommentedEmptyConstructor" level="MAJOR" enabled="false" /> - <coding_rule class="DocumentationUncommentedEmptyMethodBody" level="MAJOR" enabled="false" /> - <coding_rule class="EC_ARRAY_AND_NONARRAY" level="CRITICAL" enabled="true" /> - <coding_rule class="EC_BAD_ARRAY_COMPARE" level="CRITICAL" enabled="true" /> - <coding_rule class="EC_INCOMPATIBLE_ARRAY_COMPARE" level="MAJOR" enabled="false" /> - <coding_rule class="EC_NULL_ARG" level="CRITICAL" enabled="true" /> - <coding_rule class="EC_UNRELATED_CLASS_AND_INTERFACE" level="CRITICAL" enabled="true" /> - <coding_rule class="EC_UNRELATED_INTERFACES" level="CRITICAL" enabled="true" /> - <coding_rule class="EC_UNRELATED_TYPES" level="CRITICAL" enabled="true" /> - <coding_rule class="EC_UNRELATED_TYPES_USING_POINTER_EQUALITY" level="CRITICAL" enabled="true" /> - <coding_rule class="EI_EXPOSE_REP" level="MAJOR" enabled="true" /> - <coding_rule class="EI_EXPOSE_REP2" level="MAJOR" enabled="true" /> - <coding_rule class="EI_EXPOSE_STATIC_REP2" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_ABSTRACT_SELF" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_ALWAYS_FALSE" level="BLOCKER" enabled="true" /> - <coding_rule class="EQ_ALWAYS_TRUE" level="BLOCKER" enabled="true" /> - <coding_rule class="EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_COMPARETO_USE_OBJECT_EQUALS" level="CRITICAL" enabled="true" /> - <coding_rule class="EQ_COMPARING_CLASS_NAMES" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_DOESNT_OVERRIDE_EQUALS" level="MAJOR" enabled="false" /> - <coding_rule class="EQ_DONT_DEFINE_EQUALS_FOR_ENUM" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_GETCLASS_AND_CLASS_CONSTANT" level="CRITICAL" enabled="true" /> - <coding_rule class="EQ_OTHER_NO_OBJECT" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_OTHER_USE_OBJECT" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_SELF_NO_OBJECT" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_SELF_USE_OBJECT" level="MAJOR" enabled="true" /> - <coding_rule class="EQ_UNUSUAL" level="MINOR" enabled="true" /> - <coding_rule class="ES_COMPARING_PARAMETER_STRING_WITH_EQ" level="MAJOR" enabled="true" /> - <coding_rule class="ES_COMPARING_STRINGS_WITH_EQ" level="MAJOR" enabled="true" /> - <coding_rule class="ESync_EMPTY_SYNC" level="MAJOR" enabled="true" /> - <coding_rule class="EmptyBlockCheck" level="MAJOR" enabled="false" /> - <coding_rule class="EmptyForInitializerPadCheck" level="INFO" enabled="false" /> - <coding_rule class="EmptyForIteratorPadCheck" level="INFO" enabled="false" /> - <coding_rule class="EmptyStatementCheck" level="INFO" enabled="true" /> - <coding_rule class="EqualsAvoidNullCheck" level="MAJOR" enabled="false"> - <param name="ignoreEqualsIgnoreCase" value="false" /> - </coding_rule> - <coding_rule class="EqualsHashCodeCheck" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneAssignmentInOperand" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAssignmentToNonFinalStatic" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidAccessibilityAlteration" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidAssertAsIdentifier" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneAvoidBranchingStatementAsLastInLoop" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidCallingFinalize" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneAvoidCatchingNPE" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneAvoidCatchingThrowable" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneAvoidDecimalLiteralsInBigDecimalConstructor" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneAvoidDuplicateLiterals" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneAvoidEnumAsIdentifier" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneAvoidFieldNameMatchingMethodName" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidFieldNameMatchingTypeName" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidInstanceofChecksInCatchClause" level="MINOR" enabled="true" /> - <coding_rule class="ErrorProneAvoidLiteralsInIfCondition" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidLosingExceptionInformation" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidMultipleUnaryOperators" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneAvoidUsingOctalValues" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneBadComparison" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneBeanMembersShouldSerialize" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneBrokenNullCheck" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneCallSuperFirst" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneCallSuperLast" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneCheckSkipResult" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneClassCastExceptionWithToArray" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneCloneMethodMustBePublic" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneCloneMethodMustImplementCloneable" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneCloneMethodReturnTypeMustMatchClassName" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneCloneThrowsCloneNotSupportedException" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneCloseResource" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneCompareObjectsWithEquals" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneConstructorCallsOverridableMethod" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneDataflowAnomalyAnalysis" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneDoNotCallGarbageCollectionExplicitly" level="CRITICAL" enabled="false" /> - <coding_rule class="ErrorProneDoNotCallSystemExit" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneDoNotHardCodeSDCard" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneDoNotThrowExceptionInFinally" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneDontImportSun" level="MINOR" enabled="true" /> - <coding_rule class="ErrorProneDontUseFloatTypeForLoopIndices" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneEmptyCatchBlock" level="CRITICAL" enabled="false" /> - <coding_rule class="ErrorProneEmptyFinalizer" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneEmptyFinallyBlock" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneEmptyIfStmt" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneEmptyInitializer" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneEmptyStatementBlock" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneEmptyStatementNotInLoop" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneEmptyStaticInitializer" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneEmptySwitchStatements" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneEmptySynchronizedBlock" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneEmptyTryBlock" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneEmptyWhileStmt" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneEqualsNull" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneFinalizeDoesNotCallSuperFinalize" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneFinalizeOnlyCallsSuperFinalize" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneFinalizeOverloaded" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneFinalizeShouldBeProtected" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneIdempotentOperations" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneImportFromSamePackage" level="MINOR" enabled="false" /> - <coding_rule class="ErrorProneInstantiationToGetClass" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneInvalidSlf4jMessageFormat" level="MINOR" enabled="false" /> - <coding_rule class="ErrorProneJUnitSpelling" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneJUnitStaticSuite" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneJspEncoding" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneJumbledIncrementer" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneLoggerIsNotStaticFinal" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneMethodWithSameNameAsEnclosingClass" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneMisplacedNullCheck" level="CRITICAL" enabled="false" /> - <coding_rule class="ErrorProneMissingBreakInSwitch" level="CRITICAL" enabled="false" /> - <coding_rule class="ErrorProneMissingSerialVersionUID" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneMissingStaticMethodInNonInstantiatableClass" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneMoreThanOneLogger" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneNonCaseLabelInSwitchStatement" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneNonStaticInitializer" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneNullAssignment" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneOverrideBothEqualsAndHashcode" level="CRITICAL" enabled="false" /> - <coding_rule class="ErrorProneProperCloneImplementation" level="CRITICAL" enabled="false" /> - <coding_rule class="ErrorProneProperLogger" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneReturnEmptyArrayRatherThanNull" level="MINOR" enabled="false" /> - <coding_rule class="ErrorProneReturnFromFinallyBlock" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneSimpleDateFormatNeedsLocale" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneSingleMethodSingleton" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneSingletonClassReturningNewInstance" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneStaticEJBFieldShouldBeFinal" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneStringBufferInstantiationWithChar" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneSuspiciousEqualsMethodName" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneSuspiciousHashcodeMethodName" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneSuspiciousOctalEscape" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneTestClassWithoutTestCases" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneUnconditionalIfStatement" level="CRITICAL" enabled="true" /> - <coding_rule class="ErrorProneUnnecessaryBooleanAssertion" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneUnnecessaryCaseChange" level="MINOR" enabled="true" /> - <coding_rule class="ErrorProneUnnecessaryConversionTemporary" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneUnusedNullCheckInEquals" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneUseCorrectExceptionLogging" level="MAJOR" enabled="true" /> - <coding_rule class="ErrorProneUseEqualsToCompareStrings" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneUseLocaleWithCaseConversions" level="MAJOR" enabled="false" /> - <coding_rule class="ErrorProneUseProperClassLoader" level="CRITICAL" enabled="false" /> - <coding_rule class="ErrorProneUselessOperationOnImmutable" level="CRITICAL" enabled="true" /> - <coding_rule class="ExecutableStatementCountCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ExplicitInitializationCheck" level="MAJOR" enabled="false" /> - <coding_rule class="FB_MISSING_EXPECTED_WARNING" level="CRITICAL" enabled="false" /> - <coding_rule class="FB_UNEXPECTED_WARNING" level="CRITICAL" enabled="false" /> - <coding_rule class="FE_FLOATING_POINT_EQUALITY" level="CRITICAL" enabled="true" /> - <coding_rule class="FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER" level="CRITICAL" enabled="true" /> - <coding_rule class="FI_EMPTY" level="MAJOR" enabled="true" /> - <coding_rule class="FI_EXPLICIT_INVOCATION" level="MAJOR" enabled="true" /> - <coding_rule class="FI_FINALIZER_NULLS_FIELDS" level="MAJOR" enabled="true" /> - <coding_rule class="FI_FINALIZER_ONLY_NULLS_FIELDS" level="MAJOR" enabled="true" /> - <coding_rule class="FI_MISSING_SUPER_CALL" level="MAJOR" enabled="true" /> - <coding_rule class="FI_NULLIFY_SUPER" level="CRITICAL" enabled="true" /> - <coding_rule class="FI_PUBLIC_SHOULD_BE_PROTECTED" level="MAJOR" enabled="true" /> - <coding_rule class="FI_USELESS" level="MINOR" enabled="true" /> - <coding_rule class="FL_MATH_USING_FLOAT_PRECISION" level="CRITICAL" enabled="true" /> - <coding_rule class="FallThroughCheck" level="MAJOR" enabled="false" /> - <coding_rule class="FileLengthCheck" level="MAJOR" enabled="false" /> - <coding_rule class="FileTabCharacterCheck" level="MAJOR" enabled="false" /> - <coding_rule class="FinalClassCheck" level="MAJOR" enabled="true" /> - <coding_rule class="FinalLocalVariableCheck" level="INFO" enabled="false" /> - <coding_rule class="FinalParametersCheck" level="INFO" enabled="false" /> - <coding_rule class="GC_UNCHECKED_TYPE_IN_GENERIC_CALL" level="CRITICAL" enabled="true" /> - <coding_rule class="GC_UNRELATED_TYPES" level="CRITICAL" enabled="true" /> - <coding_rule class="GenericWhitespaceCheck" level="MAJOR" enabled="false" /> - <coding_rule class="HE_EQUALS_NO_HASHCODE" level="MAJOR" enabled="true" /> - <coding_rule class="HE_EQUALS_USE_HASHCODE" level="CRITICAL" enabled="true" /> - <coding_rule class="HE_HASHCODE_NO_EQUALS" level="CRITICAL" enabled="true" /> - <coding_rule class="HE_HASHCODE_USE_OBJECT_EQUALS" level="CRITICAL" enabled="true" /> - <coding_rule class="HE_INHERITS_EQUALS_USE_HASHCODE" level="CRITICAL" enabled="true" /> - <coding_rule class="HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS" level="CRITICAL" enabled="true" /> - <coding_rule class="HE_USE_OF_UNHASHABLE_CLASS" level="CRITICAL" enabled="true" /> - <coding_rule class="HRS_REQUEST_PARAMETER_TO_COOKIE" level="MAJOR" enabled="true" /> - <coding_rule class="HRS_REQUEST_PARAMETER_TO_HTTP_HEADER" level="MAJOR" enabled="true" /> - <coding_rule class="HSC_HUGE_SHARED_STRING_CONSTANT" level="CRITICAL" enabled="true" /> - <coding_rule class="HeaderCheck" level="MAJOR" enabled="false" /> - <coding_rule class="HiddenFieldCheck" level="MAJOR" enabled="true" /> - <coding_rule class="HideUtilityClassConstructorCheck" level="MAJOR" enabled="true" /> - <coding_rule class="IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD" level="MAJOR" enabled="true" /> - <coding_rule class="ICAST_BAD_SHIFT_AMOUNT" level="CRITICAL" enabled="true" /> - <coding_rule class="ICAST_IDIV_CAST_TO_DOUBLE" level="CRITICAL" enabled="true" /> - <coding_rule class="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG" level="CRITICAL" enabled="true" /> - <coding_rule class="ICAST_INT_2_LONG_AS_INSTANT" level="CRITICAL" enabled="false" /> - <coding_rule class="ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL" level="CRITICAL" enabled="true" /> - <coding_rule class="ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND" level="CRITICAL" enabled="true" /> - <coding_rule class="ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT" level="CRITICAL" enabled="true" /> - <coding_rule class="IC_INIT_CIRCULARITY" level="CRITICAL" enabled="true" /> - <coding_rule class="IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION" level="MAJOR" enabled="true" /> - <coding_rule class="IIL_ELEMENTS_GET_LENGTH_IN_LOOP" level="MAJOR" enabled="false" /> - <coding_rule class="IIL_PATTERN_COMPILE_IN_LOOP" level="MAJOR" enabled="false" /> - <coding_rule class="IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT" level="MAJOR" enabled="false" /> - <coding_rule class="IIL_PREPARE_STATEMENT_IN_LOOP" level="MAJOR" enabled="false" /> - <coding_rule class="IIO_INEFFICIENT_INDEX_OF" level="MAJOR" enabled="false" /> - <coding_rule class="IIO_INEFFICIENT_LAST_INDEX_OF" level="MAJOR" enabled="false" /> - <coding_rule class="IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD" level="CRITICAL" enabled="true" /> - <coding_rule class="IJU_BAD_SUITE_METHOD" level="CRITICAL" enabled="true" /> - <coding_rule class="IJU_NO_TESTS" level="CRITICAL" enabled="true" /> - <coding_rule class="IJU_SETUP_NO_SUPER" level="CRITICAL" enabled="true" /> - <coding_rule class="IJU_SUITE_NOT_STATIC" level="CRITICAL" enabled="true" /> - <coding_rule class="IJU_TEARDOWN_NO_SUPER" level="CRITICAL" enabled="true" /> - <coding_rule class="IL_CONTAINER_ADDED_TO_ITSELF" level="CRITICAL" enabled="true" /> - <coding_rule class="IL_INFINITE_LOOP" level="CRITICAL" enabled="true" /> - <coding_rule class="IL_INFINITE_RECURSIVE_LOOP" level="CRITICAL" enabled="true" /> - <coding_rule class="IMA_INEFFICIENT_MEMBER_ACCESS" level="MAJOR" enabled="false" /> - <coding_rule class="IMSE_DONT_CATCH_IMSE" level="MAJOR" enabled="true" /> - <coding_rule class="IM_AVERAGE_COMPUTATION_COULD_OVERFLOW" level="CRITICAL" enabled="true" /> - <coding_rule class="IM_BAD_CHECK_FOR_ODD" level="CRITICAL" enabled="true" /> - <coding_rule class="IM_MULTIPLYING_RESULT_OF_IREM" level="CRITICAL" enabled="true" /> - <coding_rule class="INT_BAD_COMPARISON_WITH_INT_VALUE" level="CRITICAL" enabled="false" /> - <coding_rule class="INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE" level="CRITICAL" enabled="true" /> - <coding_rule class="INT_BAD_COMPARISON_WITH_SIGNED_BYTE" level="CRITICAL" enabled="true" /> - <coding_rule class="INT_BAD_REM_BY_1" level="CRITICAL" enabled="true" /> - <coding_rule class="INT_VACUOUS_BIT_OPERATION" level="CRITICAL" enabled="true" /> - <coding_rule class="INT_VACUOUS_COMPARISON" level="CRITICAL" enabled="true" /> - <coding_rule class="IO_APPENDING_TO_OBJECT_OUTPUT_STREAM" level="CRITICAL" enabled="true" /> - <coding_rule class="IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN" level="CRITICAL" enabled="true" /> - <coding_rule class="IS2_INCONSISTENT_SYNC" level="CRITICAL" enabled="true" /> - <coding_rule class="ISC_INSTANTIATE_STATIC_CLASS" level="MAJOR" enabled="true" /> - <coding_rule class="IS_FIELD_NOT_GUARDED" level="CRITICAL" enabled="true" /> - <coding_rule class="ITA_INEFFICIENT_TO_ARRAY" level="CRITICAL" enabled="true" /> - <coding_rule class="IT_NO_SUCH_ELEMENT" level="MINOR" enabled="true" /> - <coding_rule class="IllegalCatchCheck" level="MAJOR" enabled="false" /> - <coding_rule class="IllegalImportCheck" level="MAJOR" enabled="false" /> - <coding_rule class="IllegalInstantiationCheck" level="MAJOR" enabled="false" /> - <coding_rule class="IllegalThrowsCheck" level="MAJOR" enabled="true" /> - <coding_rule class="IllegalTokenCheck" level="MAJOR" enabled="false" /> - <coding_rule class="IllegalTokenTextCheck" level="MAJOR" enabled="false" /> - <coding_rule class="IllegalTypeCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ImportControlCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ImportOrderCheck" level="INFO" enabled="false" /> - <coding_rule class="IndentationCheck" level="INFO" enabled="false" /> - <coding_rule class="InnerAssignmentCheck" level="MAJOR" enabled="true" /> - <coding_rule class="InnerTypeLastCheck" level="MAJOR" enabled="true" /> - <coding_rule class="InterfaceIsTypeCheck" level="MAJOR" enabled="false" /> - <coding_rule class="J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION" level="CRITICAL" enabled="true" /> - <coding_rule class="JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS" level="MINOR" enabled="true" /> - <coding_rule class="JLM_JSR166_LOCK_MONITORENTER" level="CRITICAL" enabled="true" /> - <coding_rule class="JLM_JSR166_UTILCONCURRENT_MONITORENTER" level="CRITICAL" enabled="false" /> - <coding_rule class="JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT" level="CRITICAL" enabled="false" /> - <coding_rule class="JUnitTestCaseCheck" level="MAJOR" enabled="false" /> - <coding_rule class="JavaNCSSCheck" level="MAJOR" enabled="false" /> - <coding_rule class="JavadocMethodCheck" level="MAJOR" enabled="false" /> - <coding_rule class="JavadocPackageCheck" level="MAJOR" enabled="false" /> - <coding_rule class="JavadocStyleCheck" level="MAJOR" enabled="false" /> - <coding_rule class="JavadocTypeCheck" level="MAJOR" enabled="false" /> - <coding_rule class="JavadocVariableCheck" level="MAJOR" enabled="false" /> - <coding_rule class="LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE" level="MAJOR" enabled="false" /> - <coding_rule class="LI_LAZY_INIT_STATIC" level="CRITICAL" enabled="true" /> - <coding_rule class="LI_LAZY_INIT_UPDATE_STATIC" level="CRITICAL" enabled="true" /> - <coding_rule class="LeftCurlyCheck" level="INFO" enabled="false" /> - <coding_rule class="LineLengthCheck" level="MAJOR" enabled="false" /> - <coding_rule class="LocalFinalVariableNameCheck" level="MAJOR" enabled="true" /> - <coding_rule class="LocalVariableNameCheck" level="MAJOR" enabled="true" /> - <coding_rule class="ME_ENUM_FIELD_SETTER" level="MAJOR" enabled="false" /> - <coding_rule class="ME_MUTABLE_ENUM_FIELD" level="MAJOR" enabled="false" /> - <coding_rule class="MF_CLASS_MASKS_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="MF_METHOD_MASKS_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="ML_SYNC_ON_UPDATED_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="MSF_MUTABLE_SERVLET_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="MS_CANNOT_BE_FINAL" level="MAJOR" enabled="true" /> - <coding_rule class="MS_EXPOSE_REP" level="CRITICAL" enabled="true" /> - <coding_rule class="MS_FINAL_PKGPROTECT" level="MAJOR" enabled="true" /> - <coding_rule class="MS_MUTABLE_ARRAY" level="MAJOR" enabled="true" /> - <coding_rule class="MS_MUTABLE_COLLECTION" level="MAJOR" enabled="false" /> - <coding_rule class="MS_MUTABLE_COLLECTION_PKGPROTECT" level="MAJOR" enabled="false" /> - <coding_rule class="MS_MUTABLE_HASHTABLE" level="MAJOR" enabled="true" /> - <coding_rule class="MS_OOI_PKGPROTECT" level="MAJOR" enabled="true" /> - <coding_rule class="MS_PKGPROTECT" level="MAJOR" enabled="true" /> - <coding_rule class="MS_SHOULD_BE_FINAL" level="MAJOR" enabled="true" /> - <coding_rule class="MS_SHOULD_BE_REFACTORED_TO_BE_FINAL" level="CRITICAL" enabled="false" /> - <coding_rule class="MTIA_SUSPECT_SERVLET_INSTANCE_FIELD" level="CRITICAL" enabled="true" /> - <coding_rule class="MTIA_SUSPECT_STRUTS_INSTANCE_FIELD" level="CRITICAL" enabled="true" /> - <coding_rule class="MWN_MISMATCHED_NOTIFY" level="CRITICAL" enabled="true" /> - <coding_rule class="MWN_MISMATCHED_WAIT" level="CRITICAL" enabled="true" /> - <coding_rule class="MagicNumberCheck" level="INFO" enabled="true"> - <param name="ignoreHashCodeMethod" value="false" /> - <param name="ignoreAnnotation" value="false" /> - </coding_rule> - <coding_rule class="MemberNameCheck" level="MAJOR" enabled="true" /> - <coding_rule class="MethodCountCheck" level="MAJOR" enabled="false"> - <param name="maxTotal" value="100" /> - <param name="maxPrivate" value="100" /> - <param name="maxPackage" value="100" /> - <param name="maxProtected" value="100" /> - <param name="maxPublic" value="100" /> - </coding_rule> - <coding_rule class="MethodLengthCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MethodNameCheck" level="MAJOR" enabled="true" /> - <coding_rule class="MethodParamPadCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MethodTypeParameterNameCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MissingCtorCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MissingDeprecatedCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MissingOverrideCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MissingSwitchDefaultCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ModifiedControlVariableCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ModifierOrderCheck" level="INFO" enabled="true" /> - <coding_rule class="MultipleStringLiteralsCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MultipleVariableDeclarationsCheck" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingAvoidSynchronizedAtMethodLevel" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingAvoidThreadGroup" level="CRITICAL" enabled="false" /> - <coding_rule class="MultithreadingAvoidUsingVolatile" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingDoNotUseThreads" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingDontCallThreadRun" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingDoubleCheckedLocking" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingNonThreadSafeSingleton" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingUnsynchronizedStaticDateFormatter" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingUseConcurrentHashMap" level="MAJOR" enabled="false" /> - <coding_rule class="MultithreadingUseNotifyAllInsteadOfNotify" level="MAJOR" enabled="false" /> - <coding_rule class="MutableExceptionCheck" level="MAJOR" enabled="false" /> - <coding_rule class="NM_BAD_EQUAL" level="MAJOR" enabled="false" /> - <coding_rule class="NM_CLASS_NAMING_CONVENTION" level="MAJOR" enabled="false" /> - <coding_rule class="NM_CLASS_NOT_EXCEPTION" level="MAJOR" enabled="true" /> - <coding_rule class="NM_CONFUSING" level="MAJOR" enabled="true" /> - <coding_rule class="NM_FIELD_NAMING_CONVENTION" level="MAJOR" enabled="false" /> - <coding_rule class="NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER" level="MAJOR" enabled="true" /> - <coding_rule class="NM_FUTURE_KEYWORD_USED_AS_MEMBER_IDENTIFIER" level="MAJOR" enabled="true" /> - <coding_rule class="NM_LCASE_HASHCODE" level="MAJOR" enabled="false" /> - <coding_rule class="NM_LCASE_TOSTRING" level="MAJOR" enabled="false" /> - <coding_rule class="NM_METHOD_CONSTRUCTOR_CONFUSION" level="MAJOR" enabled="true" /> - <coding_rule class="NM_METHOD_NAMING_CONVENTION" level="MAJOR" enabled="false" /> - <coding_rule class="NM_SAME_SIMPLE_NAME_AS_INTERFACE" level="MAJOR" enabled="true" /> - <coding_rule class="NM_SAME_SIMPLE_NAME_AS_SUPERCLASS" level="MAJOR" enabled="true" /> - <coding_rule class="NM_VERY_CONFUSING" level="MAJOR" enabled="true" /> - <coding_rule class="NM_VERY_CONFUSING_INTENTIONAL" level="MAJOR" enabled="true" /> - <coding_rule class="NM_WRONG_PACKAGE" level="MAJOR" enabled="true" /> - <coding_rule class="NM_WRONG_PACKAGE_INTENTIONAL" level="MAJOR" enabled="true" /> - <coding_rule class="NN_NAKED_NOTIFY" level="CRITICAL" enabled="true" /> - <coding_rule class="NOISE_FIELD_REFERENCE" level="MAJOR" enabled="false" /> - <coding_rule class="NOISE_METHOD_CALL" level="MAJOR" enabled="false" /> - <coding_rule class="NOISE_NULL_DEREFERENCE" level="MAJOR" enabled="false" /> - <coding_rule class="NOISE_OPERATION" level="MAJOR" enabled="false" /> - <coding_rule class="NO_NOTIFY_NOT_NOTIFYALL" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_ALWAYS_NULL" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_ALWAYS_NULL_EXCEPTION" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_ARGUMENT_MIGHT_BE_NULL" level="MAJOR" enabled="true" /> - <coding_rule class="NP_BOOLEAN_RETURN_NULL" level="MAJOR" enabled="true" /> - <coding_rule class="NP_CLONE_COULD_RETURN_NULL" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_CLOSING_NULL" level="MAJOR" enabled="false" /> - <coding_rule class="NP_DEREFERENCE_OF_READLINE_VALUE" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_GUARANTEED_DEREF" level="BLOCKER" enabled="true" /> - <coding_rule class="NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_IMMEDIATE_DEREFERENCE_OF_READLINE" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_LOAD_OF_KNOWN_NULL_VALUE" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION" level="MAJOR" enabled="false" /> - <coding_rule class="NP_METHOD_RETURN_RELAXING_ANNOTATION" level="MAJOR" enabled="false" /> - <coding_rule class="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" level="CRITICAL" enabled="false" /> - <coding_rule class="NP_NONNULL_PARAM_VIOLATION" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NONNULL_RETURN_VIOLATION" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NULL_INSTANCEOF" level="BLOCKER" enabled="true" /> - <coding_rule class="NP_NULL_ON_SOME_PATH" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NULL_ON_SOME_PATH_EXCEPTION" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NULL_PARAM_DEREF" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_NULL_PARAM_DEREF_NONVIRTUAL" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_OPTIONAL_RETURN_NULL" level="MAJOR" enabled="false" /> - <coding_rule class="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_STORE_INTO_NONNULL_FIELD" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_SYNC_AND_NULL_CHECK_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="NP_TOSTRING_COULD_RETURN_NULL" level="CRITICAL" enabled="true" /> - <coding_rule class="NP_UNWRITTEN_FIELD" level="MAJOR" enabled="false" /> - <coding_rule class="NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> - <coding_rule class="NPathComplexityCheck" level="MAJOR" enabled="false" /> - <coding_rule class="NS_DANGEROUS_NON_SHORT_CIRCUIT" level="CRITICAL" enabled="true" /> - <coding_rule class="NS_NON_SHORT_CIRCUIT" level="MAJOR" enabled="true" /> - <coding_rule class="NeedBracesCheck" level="INFO" enabled="false" /> - <coding_rule class="NestedForDepthCheck" level="MAJOR" enabled="false"> - <param name="max" value="1" /> - </coding_rule> - <coding_rule class="NestedIfDepthCheck" level="MAJOR" enabled="false" /> - <coding_rule class="NestedTryDepthCheck" level="MAJOR" enabled="false" /> - <coding_rule class="NewlineAtEndOfFileCheck" level="INFO" enabled="false" /> - <coding_rule class="NoCloneCheck" level="MAJOR" enabled="false" /> - <coding_rule class="NoFinalizerCheck" level="MAJOR" enabled="false" /> - <coding_rule class="NoWhitespaceAfterCheck" level="INFO" enabled="false" /> - <coding_rule class="NoWhitespaceBeforeCheck" level="INFO" enabled="false" /> - <coding_rule class="OBL_UNSATISFIED_OBLIGATION" level="CRITICAL" enabled="false" /> - <coding_rule class="OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE" level="CRITICAL" enabled="false" /> - <coding_rule class="ODR_OPEN_DATABASE_RESOURCE" level="CRITICAL" enabled="true" /> - <coding_rule class="ODR_OPEN_DATABASE_RESOURCE_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> - <coding_rule class="OS_OPEN_STREAM" level="CRITICAL" enabled="true" /> - <coding_rule class="OS_OPEN_STREAM_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> - <coding_rule class="OneStatementPerLineCheck" level="INFO" enabled="false" /> - <coding_rule class="OperatorWrapCheck" level="INFO" enabled="false" /> - <coding_rule class="OuterTypeFilenameCheck" level="INFO" enabled="false" /> - <coding_rule class="OuterTypeNumberCheck" level="MAJOR" enabled="false" /> - <coding_rule class="PS_PUBLIC_SEMAPHORES" level="CRITICAL" enabled="true" /> - <coding_rule class="PT_ABSOLUTE_PATH_TRAVERSAL" level="CRITICAL" enabled="false" /> - <coding_rule class="PT_RELATIVE_PATH_TRAVERSAL" level="CRITICAL" enabled="false" /> - <coding_rule class="PZLA_PREFER_ZERO_LENGTH_ARRAYS" level="MAJOR" enabled="true" /> - <coding_rule class="PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS" level="CRITICAL" enabled="false" /> - <coding_rule class="PackageAnnotationCheck" level="MAJOR" enabled="false" /> - <coding_rule class="PackageDeclarationCheck" level="MAJOR" enabled="false"> - <param name="ignoreDirectoryName" value="false" /> - </coding_rule> - <coding_rule class="PackageNameCheck" level="MAJOR" enabled="true" /> - <coding_rule class="ParameterAssignmentCheck" level="MAJOR" enabled="true" /> - <coding_rule class="ParameterNameCheck" level="MAJOR" enabled="true" /> - <coding_rule class="ParameterNumberCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ParenPadCheck" level="INFO" enabled="false" /> - <coding_rule class="PerformanceAddEmptyString" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceAppendCharacterWithChar" level="MINOR" enabled="false" /> - <coding_rule class="PerformanceAvoidArrayLoops" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceAvoidInstantiatingObjectsInLoops" level="MINOR" enabled="false" /> - <coding_rule class="PerformanceAvoidUsingShortType" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceBigIntegerInstantiation" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceBooleanInstantiation" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceByteInstantiation" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceConsecutiveAppendsShouldReuse" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceConsecutiveLiteralAppends" level="MINOR" enabled="false" /> - <coding_rule class="PerformanceInefficientEmptyStringCheck" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceInefficientStringBuffering" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceInsufficientStringBufferDeclaration" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceIntegerInstantiation" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceLongInstantiation" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceOptimizableToArrayCall" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceRedundantFieldInitializer" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceShortInstantiation" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceSimplifyStartsWith" level="MINOR" enabled="false" /> - <coding_rule class="PerformanceStringInstantiation" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceStringToString" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceTooFewBranchesForASwitchStatement" level="MINOR" enabled="false" /> - <coding_rule class="PerformanceUnnecessaryWrapperObjectCreation" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceUseArrayListInsteadOfVector" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceUseArraysAsList" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceUseIndexOfChar" level="MAJOR" enabled="true" /> - <coding_rule class="PerformanceUseStringBufferForStringAppends" level="MAJOR" enabled="false" /> - <coding_rule class="PerformanceUseStringBufferLength" level="MINOR" enabled="true" /> - <coding_rule class="PerformanceUselessStringValueOf" level="MINOR" enabled="true" /> - <coding_rule class="QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="QF_QUESTIONABLE_FOR_LOOP" level="CRITICAL" enabled="true" /> - <coding_rule class="RANGE_ARRAY_INDEX" level="MAJOR" enabled="false" /> - <coding_rule class="RANGE_ARRAY_LENGTH" level="MAJOR" enabled="false" /> - <coding_rule class="RANGE_ARRAY_OFFSET" level="MAJOR" enabled="false" /> - <coding_rule class="RANGE_STRING_INDEX" level="MAJOR" enabled="false" /> - <coding_rule class="RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE" level="CRITICAL" enabled="true" /> - <coding_rule class="RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES" level="CRITICAL" enabled="true" /> - <coding_rule class="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" level="CRITICAL" enabled="true" /> - <coding_rule class="RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE" level="CRITICAL" enabled="true" /> - <coding_rule class="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" level="CRITICAL" enabled="true" /> - <coding_rule class="RC_REF_COMPARISON" level="CRITICAL" enabled="true" /> - <coding_rule class="RC_REF_COMPARISON_BAD_PRACTICE" level="MAJOR" enabled="false" /> - <coding_rule class="RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN" level="MAJOR" enabled="false" /> - <coding_rule class="REC_CATCH_EXCEPTION" level="MAJOR" enabled="true" /> - <coding_rule class="RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION" level="CRITICAL" enabled="true" /> - <coding_rule class="RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION" level="CRITICAL" enabled="true" /> - <coding_rule class="RE_POSSIBLE_UNINTENDED_PATTERN" level="CRITICAL" enabled="true" /> - <coding_rule class="RI_REDUNDANT_INTERFACES" level="MAJOR" enabled="true" /> - <coding_rule class="RR_NOT_CHECKED" level="MAJOR" enabled="true" /> - <coding_rule class="RS_READOBJECT_SYNC" level="CRITICAL" enabled="true" /> - <coding_rule class="RU_INVOKE_RUN" level="MAJOR" enabled="true" /> - <coding_rule class="RV_01_TO_INT" level="MAJOR" enabled="true" /> - <coding_rule class="RV_ABSOLUTE_VALUE_OF_HASHCODE" level="CRITICAL" enabled="true" /> - <coding_rule class="RV_ABSOLUTE_VALUE_OF_RANDOM_INT" level="CRITICAL" enabled="true" /> - <coding_rule class="RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE" level="CRITICAL" enabled="false" /> - <coding_rule class="RV_CHECK_FOR_POSITIVE_INDEXOF" level="MINOR" enabled="true" /> - <coding_rule class="RV_DONT_JUST_NULL_CHECK_READLINE" level="MAJOR" enabled="true" /> - <coding_rule class="RV_EXCEPTION_NOT_THROWN" level="CRITICAL" enabled="true" /> - <coding_rule class="RV_NEGATING_RESULT_OF_COMPARETO" level="CRITICAL" enabled="false" /> - <coding_rule class="RV_REM_OF_HASHCODE" level="CRITICAL" enabled="true" /> - <coding_rule class="RV_REM_OF_RANDOM_INT" level="CRITICAL" enabled="true" /> - <coding_rule class="RV_RETURN_VALUE_IGNORED" level="MINOR" enabled="true" /> - <coding_rule class="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" level="MAJOR" enabled="true" /> - <coding_rule class="RV_RETURN_VALUE_IGNORED_INFERRED" level="CRITICAL" enabled="false" /> - <coding_rule class="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" level="MAJOR" enabled="false" /> - <coding_rule class="RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED" level="MAJOR" enabled="false" /> - <coding_rule class="RedundantImportCheck" level="INFO" enabled="false" /> - <coding_rule class="RedundantModifierCheck" level="INFO" enabled="true" /> - <coding_rule class="RedundantThrowsCheck" level="INFO" enabled="true" /> - <coding_rule class="RegexpCheck" level="MAJOR" enabled="false" /> - <coding_rule class="RegexpHeaderCheck" level="MAJOR" enabled="false" /> - <coding_rule class="RegexpMultilineCheck" level="MAJOR" enabled="false"> - <param name="message" value="TODO item found" /> - <param name="minimum" value="0" /> - <param name="maximum" value="0" /> - <param name="format" value="TODO" /> - <param name="ignoreCase" value="true" /> - </coding_rule> - <coding_rule class="RegexpSinglelineCheck" level="MAJOR" enabled="false"> - <param name="message" value="TODO item found" /> - <param name="minimum" value="0" /> - <param name="maximum" value="0" /> - <param name="format" value="TODO" /> - <param name="ignoreCase" value="true" /> - </coding_rule> - <coding_rule class="RegexpSinglelineJavaCheck" level="MAJOR" enabled="false"> - <param name="message" value="TODO item found" /> - <param name="minimum" value="0" /> - <param name="maximum" value="0" /> - <param name="ignoreComments" value="false" /> - <param name="format" value="TODO" /> - <param name="ignoreCase" value="true" /> - </coding_rule> - <coding_rule class="RequireThisCheck" level="MAJOR" enabled="false" /> - <coding_rule class="ReturnCountCheck" level="MAJOR" enabled="false" /> - <coding_rule class="RightCurlyCheck" level="INFO" enabled="false" /> - <coding_rule class="RpC_REPEATED_CONDITIONAL_TEST" level="MAJOR" enabled="true" /> - <coding_rule class="SA_FIELD_DOUBLE_ASSIGNMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="SA_FIELD_SELF_ASSIGNMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="SA_FIELD_SELF_COMPARISON" level="CRITICAL" enabled="true" /> - <coding_rule class="SA_FIELD_SELF_COMPUTATION" level="CRITICAL" enabled="true" /> - <coding_rule class="SA_LOCAL_DOUBLE_ASSIGNMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="SA_LOCAL_SELF_ASSIGNMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD" level="CRITICAL" enabled="false" /> - <coding_rule class="SA_LOCAL_SELF_COMPARISON" level="CRITICAL" enabled="true" /> - <coding_rule class="SA_LOCAL_SELF_COMPUTATION" level="CRITICAL" enabled="true" /> - <coding_rule class="SBSC_USE_STRINGBUFFER_CONCATENATION" level="CRITICAL" enabled="true" /> - <coding_rule class="SC_START_IN_CTOR" level="CRITICAL" enabled="true" /> - <coding_rule class="SE_BAD_FIELD" level="MINOR" enabled="false" /> - <coding_rule class="SE_BAD_FIELD_INNER_CLASS" level="MINOR" enabled="true" /> - <coding_rule class="SE_BAD_FIELD_STORE" level="CRITICAL" enabled="true" /> - <coding_rule class="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE" level="MAJOR" enabled="true" /> - <coding_rule class="SE_INNER_CLASS" level="MAJOR" enabled="true" /> - <coding_rule class="SE_METHOD_MUST_BE_PRIVATE" level="MAJOR" enabled="true" /> - <coding_rule class="SE_NONFINAL_SERIALVERSIONID" level="CRITICAL" enabled="true" /> - <coding_rule class="SE_NONLONG_SERIALVERSIONID" level="MAJOR" enabled="true" /> - <coding_rule class="SE_NONSTATIC_SERIALVERSIONID" level="MAJOR" enabled="true" /> - <coding_rule class="SE_NO_SERIALVERSIONID" level="MAJOR" enabled="true" /> - <coding_rule class="SE_NO_SUITABLE_CONSTRUCTOR" level="MAJOR" enabled="true" /> - <coding_rule class="SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION" level="MAJOR" enabled="true" /> - <coding_rule class="SE_PRIVATE_READ_RESOLVE_NOT_INHERITED" level="MAJOR" enabled="true" /> - <coding_rule class="SE_READ_RESOLVE_IS_STATIC" level="MAJOR" enabled="true" /> - <coding_rule class="SE_READ_RESOLVE_MUST_RETURN_OBJECT" level="MAJOR" enabled="true" /> - <coding_rule class="SE_TRANSIENT_FIELD_NOT_RESTORED" level="MAJOR" enabled="true" /> - <coding_rule class="SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS" level="MAJOR" enabled="true" /> - <coding_rule class="SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH" level="MAJOR" enabled="false" /> - <coding_rule class="SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW" level="MAJOR" enabled="false" /> - <coding_rule class="SF_SWITCH_FALLTHROUGH" level="MAJOR" enabled="false" /> - <coding_rule class="SF_SWITCH_NO_DEFAULT" level="MAJOR" enabled="false" /> - <coding_rule class="SIC_INNER_SHOULD_BE_STATIC" level="MAJOR" enabled="true" /> - <coding_rule class="SIC_INNER_SHOULD_BE_STATIC_ANON" level="MAJOR" enabled="true" /> - <coding_rule class="SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS" level="MAJOR" enabled="true" /> - <coding_rule class="SIC_THREADLOCAL_DEADLY_EMBRACE" level="MAJOR" enabled="false" /> - <coding_rule class="SIO_SUPERFLUOUS_INSTANCEOF" level="CRITICAL" enabled="true" /> - <coding_rule class="SI_INSTANCE_BEFORE_FINALS_ASSIGNED" level="CRITICAL" enabled="true" /> - <coding_rule class="SP_SPIN_ON_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="SQL_BAD_PREPARED_STATEMENT_ACCESS" level="CRITICAL" enabled="true" /> - <coding_rule class="SQL_BAD_RESULTSET_ACCESS" level="CRITICAL" enabled="true" /> - <coding_rule class="SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE" level="CRITICAL" enabled="true" /> - <coding_rule class="SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" level="CRITICAL" enabled="true" /> - <coding_rule class="SR_NOT_CHECKED" level="MAJOR" enabled="true" /> - <coding_rule class="SS_SHOULD_BE_STATIC" level="MAJOR" enabled="true" /> - <coding_rule class="STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE" level="CRITICAL" enabled="true" /> - <coding_rule class="STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE" level="CRITICAL" enabled="true" /> - <coding_rule class="STCAL_STATIC_CALENDAR_INSTANCE" level="CRITICAL" enabled="true" /> - <coding_rule class="STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE" level="CRITICAL" enabled="true" /> - <coding_rule class="STI_INTERRUPTED_ON_CURRENTTHREAD" level="CRITICAL" enabled="true" /> - <coding_rule class="STI_INTERRUPTED_ON_UNKNOWNTHREAD" level="CRITICAL" enabled="true" /> - <coding_rule class="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" level="CRITICAL" enabled="true" /> - <coding_rule class="SWL_SLEEP_WITH_LOCK_HELD" level="CRITICAL" enabled="true" /> - <coding_rule class="SW_SWING_METHODS_INVOKED_IN_SWING_THREAD" level="MAJOR" enabled="true" /> - <coding_rule class="SecurityIframeMissingSrcAttribute" level="MAJOR" enabled="false" /> - <coding_rule class="SecurityNoUnsanitizedJSPExpression" level="MAJOR" enabled="false" /> - <coding_rule class="SimplifyBooleanExpressionCheck" level="MAJOR" enabled="true" /> - <coding_rule class="SimplifyBooleanReturnCheck" level="MAJOR" enabled="true" /> - <coding_rule class="StaticVariableNameCheck" level="MAJOR" enabled="true" /> - <coding_rule class="StrictDuplicateCodeCheck" level="MAJOR" enabled="false" /> - <coding_rule class="StringLiteralEqualityCheck" level="MAJOR" enabled="true" /> - <coding_rule class="SuperCloneCheck" level="MAJOR" enabled="false" /> - <coding_rule class="SuperFinalizeCheck" level="MAJOR" enabled="false" /> - <coding_rule class="SuppressWarningsCheck" level="MAJOR" enabled="false" /> - <coding_rule class="TLW_TWO_LOCK_WAIT" level="MAJOR" enabled="true" /> - <coding_rule class="TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED" level="CRITICAL" enabled="true" /> - <coding_rule class="TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS" level="CRITICAL" enabled="false" /> - <coding_rule class="TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK" level="CRITICAL" enabled="true" /> - <coding_rule class="TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK" level="CRITICAL" enabled="true" /> - <coding_rule class="TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK" level="CRITICAL" enabled="true" /> - <coding_rule class="TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK" level="CRITICAL" enabled="true" /> - <coding_rule class="TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED" level="CRITICAL" enabled="true" /> - <coding_rule class="TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED" level="CRITICAL" enabled="false" /> - <coding_rule class="ThrowsCountCheck" level="MAJOR" enabled="false" /> - <coding_rule class="TodoCommentCheck" level="INFO" enabled="false" /> - <coding_rule class="TrailingCommentCheck" level="INFO" enabled="false" /> - <coding_rule class="TranslationCheck" level="INFO" enabled="false" /> - <coding_rule class="TypeNameCheck" level="MAJOR" enabled="false" /> - <coding_rule class="TypecastParenPadCheck" level="MAJOR" enabled="false" /> - <coding_rule class="UCF_USELESS_CONTROL_FLOW" level="CRITICAL" enabled="true" /> - <coding_rule class="UCF_USELESS_CONTROL_FLOW_NEXT_LINE" level="CRITICAL" enabled="true" /> - <coding_rule class="UC_USELESS_CONDITION" level="MAJOR" enabled="false" /> - <coding_rule class="UC_USELESS_CONDITION_TYPE" level="MAJOR" enabled="false" /> - <coding_rule class="UC_USELESS_OBJECT" level="MAJOR" enabled="false" /> - <coding_rule class="UC_USELESS_OBJECT_STACK" level="MAJOR" enabled="false" /> - <coding_rule class="UC_USELESS_VOID_METHOD" level="MAJOR" enabled="false" /> - <coding_rule class="UG_SYNC_SET_UNSYNC_GET" level="MAJOR" enabled="true" /> - <coding_rule class="UI_INHERITANCE_UNSAFE_GETRESOURCE" level="MAJOR" enabled="true" /> - <coding_rule class="UL_UNRELEASED_LOCK" level="CRITICAL" enabled="true" /> - <coding_rule class="UL_UNRELEASED_LOCK_EXCEPTION_PATH" level="CRITICAL" enabled="true" /> - <coding_rule class="UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS" level="CRITICAL" enabled="true" /> - <coding_rule class="UM_UNNECESSARY_MATH" level="CRITICAL" enabled="true" /> - <coding_rule class="UPM_UNCALLED_PRIVATE_METHOD" level="CRITICAL" enabled="true" /> - <coding_rule class="URF_UNREAD_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> - <coding_rule class="UR_UNINIT_READ" level="MAJOR" enabled="true" /> - <coding_rule class="UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR" level="MAJOR" enabled="false" /> - <coding_rule class="USM_USELESS_ABSTRACT_METHOD" level="MAJOR" enabled="false" /> - <coding_rule class="USM_USELESS_SUBCLASS_METHOD" level="MAJOR" enabled="false" /> - <coding_rule class="UUF_UNUSED_FIELD" level="MAJOR" enabled="true" /> - <coding_rule class="UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> - <coding_rule class="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" level="MAJOR" enabled="false" /> - <coding_rule class="UWF_NULL_FIELD" level="CRITICAL" enabled="true" /> - <coding_rule class="UWF_UNWRITTEN_FIELD" level="MAJOR" enabled="false" /> - <coding_rule class="UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" /> - <coding_rule class="UW_UNCOND_WAIT" level="MAJOR" enabled="true" /> - <coding_rule class="UncommentedMainCheck" level="MAJOR" enabled="false" /> - <coding_rule class="UnnecessaryParenthesesCheck" level="INFO" enabled="false" /> - <coding_rule class="UnusedImportsCheck" level="INFO" enabled="true"> - <param name="processJavadoc" value="false" /> - </coding_rule> - <coding_rule class="UpperEllCheck" level="INFO" enabled="false" /> - <coding_rule class="VA_FORMAT_STRING_BAD_ARGUMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION" level="CRITICAL" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION_FROM_ARRAY" level="MAJOR" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION_TO_BOOLEAN" level="MAJOR" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED" level="MAJOR" enabled="false" /> - <coding_rule class="VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED" level="MAJOR" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_ILLEGAL" level="CRITICAL" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_MISSING_ARGUMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT" level="CRITICAL" enabled="true" /> - <coding_rule class="VA_FORMAT_STRING_USES_NEWLINE" level="CRITICAL" enabled="false" /> - <coding_rule class="VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG" level="CRITICAL" enabled="true" /> - <coding_rule class="VO_VOLATILE_INCREMENT" level="CRITICAL" enabled="false" /> - <coding_rule class="VO_VOLATILE_REFERENCE_TO_ARRAY" level="MAJOR" enabled="true" /> - <coding_rule class="VR_UNRESOLVABLE_REFERENCE" level="MAJOR" enabled="false" /> - <coding_rule class="VisibilityModifierCheck" level="MAJOR" enabled="true" /> - <coding_rule class="WA_AWAIT_NOT_IN_LOOP" level="CRITICAL" enabled="true" /> - <coding_rule class="WA_NOT_IN_LOOP" level="CRITICAL" enabled="true" /> - <coding_rule class="WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL" level="CRITICAL" enabled="true" /> - <coding_rule class="WMI_WRONG_MAP_ITERATOR" level="CRITICAL" enabled="true" /> - <coding_rule class="WS_WRITEOBJECT_SYNC" level="CRITICAL" enabled="true" /> - <coding_rule class="WhitespaceAfterCheck" level="INFO" enabled="false" /> - <coding_rule class="WhitespaceAroundCheck" level="INFO" enabled="false" /> - <coding_rule class="WriteTagCheck" level="INFO" enabled="false" /> - <coding_rule class="XFB_XML_FACTORY_BYPASS" level="CRITICAL" enabled="true" /> - <coding_rule class="XSS_REQUEST_PARAMETER_TO_JSP_WRITER" level="CRITICAL" enabled="true" /> - <coding_rule class="XSS_REQUEST_PARAMETER_TO_SEND_ERROR" level="CRITICAL" enabled="true" /> - <coding_rule class="XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER" level="CRITICAL" enabled="true" /> - </profile> - </profiles> - <list size="0" /> - </component> - <component name="CheckStyle-IDEA"> - <option name="configuration"> - <map> - <entry key="active-configuration" value="LOCAL_FILE:K:/java/mobibot/config/checkstyle/checkstyle.xml:Erik's Checks" /> - <entry key="checkstyle-version" value="8.19" /> - <entry key="copy-libs" value="true" /> - <entry key="location-0" value="BUNDLED:(bundled):Sun Checks" /> - <entry key="location-1" value="BUNDLED:(bundled):Google Checks" /> - <entry key="location-2" value="LOCAL_FILE:K:/java/mobibot/config/checkstyle/checkstyle.xml:Erik's Checks" /> - <entry key="scan-before-checkin" value="false" /> - <entry key="scanscope" value="JavaOnlyWithTests" /> - <entry key="suppress-errors" value="false" /> - </map> - </option> - </component> - <component name="CheckstyleConfigurable"> - <option name="suppFilterFilename" value="" /> - <option name="suppCommentFilter" value="false" /> - <option name="offComment" value="CHECKSTYLE\:OFF" /> - <option name="onComment" value="CHECKSTYLE\:ON" /> - <option name="checkFormat" value=".*" /> - <option name="messageFormat" value="" /> - <option name="checkCPP" value="true" /> - <option name="checkC" value="true" /> - <option name="suppNearbyCommentFilter" value="false" /> - <option name="snCommentFormat" value="SUPPRESS CHECKSTYLE (\w+)" /> - <option name="snCheckFormat" value="$1" /> - <option name="snMessageFormat" value="" /> - <option name="snInfluenceFormat" value="0" /> - <option name="snCheckCPP" value="true" /> - <option name="snCheckC" value="true" /> - <option name="pathToUserRulesConfiguration" value="" /> - <option name="pathToJarWithRules" value="" /> - </component> - <component name="ClientPropertiesManager"> - <properties class="javax.swing.AbstractButton"> - <property name="hideActionText" class="java.lang.Boolean" /> - </properties> - <properties class="javax.swing.JComponent"> - <property name="html.disable" class="java.lang.Boolean" /> - </properties> - <properties class="javax.swing.JEditorPane"> - <property name="JEditorPane.w3cLengthUnits" class="java.lang.Boolean" /> - <property name="JEditorPane.honorDisplayProperties" class="java.lang.Boolean" /> - <property name="charset" class="java.lang.String" /> - </properties> - <properties class="javax.swing.JList"> - <property name="List.isFileList" class="java.lang.Boolean" /> - </properties> - <properties class="javax.swing.JPasswordField"> - <property name="JPasswordField.cutCopyAllowed" class="java.lang.Boolean" /> - </properties> - <properties class="javax.swing.JSlider"> - <property name="Slider.paintThumbArrowShape" class="java.lang.Boolean" /> - <property name="JSlider.isFilled" class="java.lang.Boolean" /> - </properties> - <properties class="javax.swing.JTable"> - <property name="Table.isFileList" class="java.lang.Boolean" /> - <property name="JTable.autoStartsEdit" class="java.lang.Boolean" /> - <property name="terminateEditOnFocusLost" class="java.lang.Boolean" /> - </properties> - <properties class="javax.swing.JToolBar"> - <property name="JToolBar.isRollover" class="java.lang.Boolean" /> - </properties> - <properties class="javax.swing.JTree"> - <property name="JTree.lineStyle" class="java.lang.String" /> - </properties> - <properties class="javax.swing.text.JTextComponent"> - <property name="caretAspectRatio" class="java.lang.Double" /> - <property name="caretWidth" class="java.lang.Integer" /> - </properties> - </component> - <component name="CopyrightManager" default="Copyright"> - <copyright> - <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> - <option name="myName" value="Copyright" /> - </copyright> - <module2copyright> - <element module="Copryight" copyright="Copyright" /> - </module2copyright> - <LanguageOptions name="JAVA"> - <option name="addBlankAfter" value="false" /> - </LanguageOptions> - <LanguageOptions name="__TEMPLATE__"> - <option name="addBlankAfter" value="false" /> - </LanguageOptions> - </component> - <component name="DependencyValidationManager"> - <scope name="Copryight" pattern="(file[mobibot]:src/generated/java//*.java||file[mobibot]:src/main/java//*.java||file[mobibot]:src/test/java//*.java)&&!file[mobibot]:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" /> - </component> - <component name="Encoding"> - <file url="PROJECT" charset="UTF-8" /> - </component> - <component name="FindBugsConfigurable"> - <option name="make" value="true" /> - <option name="effort" value="default" /> - <option name="priority" value="Medium" /> - <option name="excludeFilter" value="" /> - </component> - <component name="GradleLocalSettings"> - <option name="modificationStamps"> - <map> - <entry key="K:/GitHub/CompileOnlyPlugin" value="2933943731758" /> - <entry key="K:/ect/groovy/SshotCleaner" value="2892856534549" /> - <entry key="K:/ect/java/Captcha" value="2894636758390" /> - <entry key="K:/ect/java/FileSplitter" value="2846829504112" /> - <entry key="K:/ect/java/GZIPFilter" value="2894567636586" /> - <entry key="K:/ect/java/Lotto" value="2894619520256" /> - <entry key="K:/ect/java/SshotCleaner" value="1446415498070" /> - <entry key="K:/ect/java/TorrentCleaner" value="2894593616839" /> - <entry key="K:/ect/java/click" value="2894595659515" /> - <entry key="K:/ect/java/iFit2Workout" value="2894645931218" /> - <entry key="$PROJECT_DIR$/../HttpStatus" value="2906561643260" /> - <entry key="$PROJECT_DIR$/../SemanticVersion" value="2905451385982" /> - <entry key="$PROJECT_DIR$" value="2865248599040" /> - <entry key="$PROJECT_DIR$/../semver" value="2905990126118" /> - </map> - </option> - <option name="externalProjectsViewState"> - <projects_view /> - </option> - </component> - <component name="GradleMigrationSettings" migrationVersion="1" /> - <component name="GradleSettings"> - <option name="linkedExternalProjectsSettings"> - <GradleProjectSettings> - <option name="delegatedBuild" value="false" /> - <option name="testRunner" value="PLATFORM" /> - <option name="distributionType" value="LOCAL" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="gradleHome" value="C:/gradle" /> - <option name="gradleJvm" value="#JAVA_HOME" /> - <option name="modules"> - <set> - <option value="$PROJECT_DIR$" /> - </set> - </option> - <option name="resolveModulePerSourceSet" value="false" /> - </GradleProjectSettings> - </option> - </component> - <component name="InspectionProjectProfileManager"> - <profile version="1.0"> - <option name="myName" value="Project Default" /> - <option name="myLocal" value="true" /> - <inspection_tool class="FieldMayBeFinal" enabled="true" level="WARNING" enabled_by_default="true" /> - <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="TOP_LEVEL_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="INNER_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="METHOD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> - </value> - </option> - <option name="FIELD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="IGNORE_DEPRECATED" value="false" /> - <option name="IGNORE_JAVADOC_PERIOD" value="true" /> - <option name="IGNORE_DUPLICATED_THROWS" value="false" /> - <option name="IGNORE_POINT_TO_ITSELF" value="false" /> - <option name="myAdditionalJavadocTags" value="created" /> - </inspection_tool> - <inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="REPORT_VARIABLES" value="true" /> - <option name="REPORT_PARAMETERS" value="false" /> - <option name="REPORT_CATCH_PARAMETERS" value="false" /> - </inspection_tool> - <inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false"> - <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" /> - <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" /> - </inspection_tool> - <inspection_tool class="UnnecessarySemicolon" enabled="false" level="WARNING" enabled_by_default="false" /> - <inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" /> - <option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="false" /> - <option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" /> - </inspection_tool> - </profile> - <option name="PROJECT_PROFILE" value="Erik's Default" /> - <option name="USE_PROJECT_PROFILE" value="false" /> - <version value="1.0" /> - </component> - <component name="MavenImportPreferences"> - <option name="generalSettings"> - <MavenGeneralSettings> - <option name="mavenHome" value="Bundled (Maven 3)" /> - </MavenGeneralSettings> - </option> - </component> - <component name="PDMPlugin"> - <option name="customRuleSets"> - <list> - <option value="K:\java\mobibot\config\pmd.xml" /> - </list> - </option> - <option name="options"> - <map> - <entry key="Encoding" value="" /> - <entry key="Target JDK" value="1.8" /> - </map> - </option> - </component> - <component name="Palette2"> - <group name="Swing"> - <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> - </item> - <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> - </item> - <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> - </item> - <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> - <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> - </item> - <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> - <initial-values> - <property name="text" value="Button" /> - </initial-values> - </item> - <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> - <initial-values> - <property name="text" value="RadioButton" /> - </initial-values> - </item> - <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> - <initial-values> - <property name="text" value="CheckBox" /> - </initial-values> - </item> - <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> - <initial-values> - <property name="text" value="Label" /> - </initial-values> - </item> - <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> - <preferred-size width="150" height="-1" /> - </default-constraints> - </item> - <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> - <preferred-size width="150" height="-1" /> - </default-constraints> - </item> - <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> - <preferred-size width="150" height="-1" /> - </default-constraints> - </item> - <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> - </item> - <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> - <preferred-size width="150" height="50" /> - </default-constraints> - </item> - <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> - <preferred-size width="200" height="200" /> - </default-constraints> - </item> - <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> - <preferred-size width="200" height="200" /> - </default-constraints> - </item> - <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> - </item> - <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> - </item> - <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> - </item> - <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> - </item> - <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> - <preferred-size width="-1" height="20" /> - </default-constraints> - </item> - <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> - <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> - </item> - <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> - <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> - </item> - </group> - </component> - <component name="ProjectCodeStyleConfiguration"> - <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> - </component> - <component name="ProjectCodeStyleSettingsManager"> - <option name="PER_PROJECT_SETTINGS"> - <value /> - </option> - <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> - </component> - <component name="ProjectInspectionProfilesVisibleTreeState"> - <entry key="Project Default"> - <profile-state> - <expanded-state> - <State> - <id /> - </State> - <State> - <id>Android Lint</id> - </State> - <State> - <id>Java</id> - </State> - <State> - <id>Portability issuesJava</id> - </State> - </expanded-state> - </profile-state> - </entry> - </component> - <component name="ProjectLevelVcsManager" settingsEditedManually="false"> - <OptionsSetting value="true" id="Add" /> - <OptionsSetting value="true" id="Remove" /> - <OptionsSetting value="true" id="Checkout" /> - <OptionsSetting value="true" id="Update" /> - <OptionsSetting value="true" id="Status" /> - <OptionsSetting value="true" id="Edit" /> - <ConfirmationsSetting value="0" id="Add" /> - <ConfirmationsSetting value="0" id="Remove" /> - </component> - <component name="ProjectModuleManager"> - <modules> - <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.iml" group="mobibot" /> - </modules> - </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_12" default="false" project-jdk-name="12" project-jdk-type="JavaSDK"> - <output url="file://$PROJECT_DIR$/build/classes" /> - </component> - <component name="PropertiesComponent"> - <property name="GoToClass.includeLibraries" value="false" /> - <property name="GoToClass.toSaveIncludeLibraries" value="false" /> - <property name="GoToFile.includeJavaFiles" value="false" /> - <property name="MemberChooser.sorted" value="false" /> - <property name="MemberChooser.showClasses" value="true" /> - <property name="MemberChooser.copyJavadoc" value="false" /> - <property name="settings.editor.selected.configurable" value="Errors" /> - <property name="settings.editor.splitter.proportion" value="0.2" /> - </component> - <component name="RunManager"> - <configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin"> - <module name="" /> - <option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" /> - <option name="PROGRAM_PARAMETERS" /> - <method /> - </configuration> - <configuration default="true" type="Applet" factoryName="Applet"> - <option name="WIDTH" value="400" /> - <option name="HEIGHT" value="300" /> - <option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" /> - <module /> - <method /> - </configuration> - <configuration default="true" type="Application" factoryName="Application"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <option name="MAIN_CLASS_NAME" /> - <option name="VM_PARAMETERS" /> - <option name="PROGRAM_PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="ENABLE_SWING_INSPECTOR" value="false" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <module name="" /> - <envs /> - <method /> - </configuration> - <configuration default="true" type="JUnit" factoryName="JUnit"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <module name="" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="PACKAGE_NAME" /> - <option name="MAIN_CLASS_NAME" /> - <option name="METHOD_NAME" /> - <option name="TEST_OBJECT" value="class" /> - <option name="VM_PARAMETERS" value="-ea" /> - <option name="PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$MODULE_DIR$" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <option name="TEST_SEARCH_SCOPE"> - <value defaultName="singleModule" /> - </option> - <envs /> - <patterns /> - <method /> - </configuration> - <configuration default="true" type="Remote" factoryName="Remote"> - <option name="USE_SOCKET_TRANSPORT" value="true" /> - <option name="SERVER_MODE" value="false" /> - <option name="SHMEM_ADDRESS" value="javadebug" /> - <option name="HOST" value="localhost" /> - <option name="PORT" value="5005" /> - <method /> - </configuration> - <configuration default="true" type="TestNG" factoryName="TestNG"> - <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> - <module name="" /> - <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> - <option name="ALTERNATIVE_JRE_PATH" /> - <option name="SUITE_NAME" /> - <option name="PACKAGE_NAME" /> - <option name="MAIN_CLASS_NAME" /> - <option name="METHOD_NAME" /> - <option name="GROUP_NAME" /> - <option name="TEST_OBJECT" value="CLASS" /> - <option name="VM_PARAMETERS" value="-ea" /> - <option name="PARAMETERS" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> - <option name="OUTPUT_DIRECTORY" /> - <option name="ANNOTATION_TYPE" /> - <option name="ENV_VARIABLES" /> - <option name="PASS_PARENT_ENVS" value="true" /> - <option name="TEST_SEARCH_SCOPE"> - <value defaultName="singleModule" /> - </option> - <option name="USE_DEFAULT_REPORTERS" value="false" /> - <option name="PROPERTIES_FILE" /> - <envs /> - <properties /> - <listeners /> - <method /> - </configuration> - </component> - <component name="SuppressionsComponent"> - <option name="suppComments" value="[]" /> - </component> - <component name="VcsDirectoryMappings"> - <mapping directory="" vcs="Git" /> - <mapping directory="$PROJECT_DIR$" vcs="Git" /> - </component> - <component name="libraryTable"> - <library name="Gradle: com.beust:jcommander:1.72"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta4"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta4/4c2237f748f5af8a1655a20945ddec334708b0cc/spotbugs-annotations-4.0.0-beta4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.0-beta4/d5e1583e2019b1ee5f9823b6d96156f92dc1e666/spotbugs-annotations-4.0.0-beta4-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6"> - <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.6/a23ce3f24ae450f13f52aaa336bf836759f22331/compiler-0.9.6-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.google.code.findbugs:jsr305:3.0.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/b19b5927c2c25b6c70f093767041e641ae0b1b35/jsr305-3.0.2-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.google.code.gson:gson:2.8.5"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/c5b4c491aecb72e7c32a78da0b5c6b9cda8dee0f/gson-2.8.5-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.rometools:rome-utils:1.12.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/d27c01295ababef9a2d6266dec6d22b0ad137dce/rome-utils-1.12.2-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.rometools:rome:1.12.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/f87f5f50b5a7f2c7e4b8e593ec644e7803660daf/rome-1.12.2-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.squareup.okhttp3:okhttp:4.2.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.2.0/446acd5a6fc0cf117c5df424af09b38f461d29e6/okhttp-4.2.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.2.0/d89a60517bd1e90121b22510494e22497d8be28b/okhttp-4.2.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.squareup.okio:okio:2.2.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.2.2/36f483536153f15339a8b48d508e22be7c9c531a/okio-2.2.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.2.2/6904b2c4ae9d612465f6fdd45ddda28aebb45921/okio-2.2.2-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/e33b9ee015798ca86bc1ce78b4a17aa9f9a53e63/converter-gson-2.5.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: com.squareup.retrofit2:retrofit:2.5.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/5c17035548a41ec60f00291163d36596a23ddd1a/retrofit-2.5.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-cli:commons-cli:1.4"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/40dfd9fdef125e19136135e68d54af6d9b0cfbb8/commons-cli-1.4-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: commons-net:commons-net:3.6"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/961dc27eabbe71bf32478baffe0e1be915ce7689/commons-net-3.6-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.aksingh:owm-japis:2.5.3.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/bddd258f8737de9f96014e44e19168629ccb113a/owm-japis-2.5.3.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.objecthunter:exp4j:0.4.8"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/8d86f148ff1f0d5b624eae9bb0882198ab5cd07/exp4j-0.4.8-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1"> - <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: net.thauvin.erik:semver:1.2.0"> - <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.apache.commons:commons-lang3:3.9"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/8f1cb192e229bc4cd1c900c51171d96706e6d195/commons-lang3-3.9-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.apache.logging.log4j:log4j-api:2.12.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.1/a55e6d987f50a515c9260b0451b4fa217dc539cb/log4j-api-2.12.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.12.1/72dbe5460db61664f6bbfffb36665fa0bbe8e3ad/log4j-api-2.12.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.apache.logging.log4j:log4j-core:2.12.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.1/4382e93136c06bfb34ddfa0bb8a9fb4ea2f3df59/log4j-core-2.12.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.12.1/1810a578053de4b4f8dd6d5c8b6b853db7ac4176/log4j-core-2.12.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.1/14973e22497adaf0196d481fb99c5dc2a0b58d41/log4j-slf4j-impl-2.12.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.1/49c6c7bed2c0074cedc53edca9681fa442f09b81/log4j-slf4j-impl-2.12.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.assertj:assertj-core:3.13.2"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.13.2/bb7b963fe752f69f055df0025691eceb83ce0c5d/assertj-core-3.13.2.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.13.2/616a8ca81b9a1a58e3c8cd8c5d7395eb548ce57/assertj-core-3.13.2-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jdom:jdom2:2.0.6"> - <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/3dcf8ba7582eeac3b67ed5155ee3659e16c8dadc/jdom2-2.0.6-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50" type="kotlin.common"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.50/3d9cd3e1bc7b92e95f43d45be3bfbcf38e36ab87/kotlin-stdlib-common-1.3.50.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.50/8ac406cf33e942265c4abb33ee0acdee79292dd6/kotlin-stdlib-common-1.3.50-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.50/50ad05ea1c2595fb31b800e76db464d08d599af3/kotlin-stdlib-jdk7-1.3.50.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.50/854725f0e8cdf688647bdc9022d1b32228fefc14/kotlin-stdlib-jdk7-1.3.50-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.50/bf65725d4ae2cf00010d84e945fcbc201f590e11/kotlin-stdlib-jdk8-1.3.50.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.50/e1eaa82220f14bd12ae0bf211a08b2bad5700e20/kotlin-stdlib-jdk8-1.3.50-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.50"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.50/b529d1738c7e98bbfa36a4134039528f2ce78ebf/kotlin-stdlib-1.3.50.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.50/bc0c767786c4a3c042a364e69d3f3dd5ff5253a5/kotlin-stdlib-1.3.50-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jetbrains:annotations:13.0"> - <CLASSES> - <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.json:json:20190722"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/6c40df0d93d36cdfc0fbf08e4e3b27eaa1208199/json-20190722-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.jsoup:jsoup:1.12.1"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.12.1/55819a28fc834c2f2bcf4dcdb278524dc3cf088f/jsoup-1.12.1.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.12.1/1ec738903bf781d7218455e314c6dc9831a2f9a6/jsoup-1.12.1-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.slf4j:slf4j-api:1.7.25"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/962153db4a9ea71b79d047dfd1b2a0d80d8f4739/slf4j-api-1.7.25-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.testng:testng:7.0.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.0.0/14b73f64988eda81a42b4584e9647d48633ef857/testng-7.0.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.0.0/5a0241ace5a7b8eb3d31556a7d5dd2b3aae9baf/testng-7.0.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: org.twitter4j:twitter4j-core:4.0.7"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/4fa8cfcfa162d2b2a76c3be71563ccbfb234a5d9/twitter4j-core-4.0.7-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: pircbot:pircbot:1.5.0"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> - </SOURCES> - </library> - <library name="Gradle: pircbot:pircbot:1.5.0:sources"> - <CLASSES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar!/" /> - </SOURCES> - </library> - </component> - <component name="masterDetails"> - <states> - <state key="ProjectJDKs.UI"> - <settings> - <last-edited>1.8.x</last-edited> - <splitter-proportions> - <option name="proportions"> - <list> - <option value="0.2" /> - </list> - </option> - </splitter-proportions> - </settings> - </state> - </states> - </component> -</project> \ No newline at end of file From 93b40fae55c8133c2d768785b9c5ed77ec61e7ea Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 17 Mar 2020 18:33:28 -0700 Subject: [PATCH 331/842] Keep Java 8 compatibility. --- src/main/java/net/thauvin/erik/mobibot/Pinboard.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index b9f538d..a0f3d95 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -80,8 +80,9 @@ class Pinboard { * * @param entry The entry to add. */ + @SuppressWarnings("Convert2Diamond") final void addPost(final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() { return pinboard.addPin(entry.getLink(), @@ -100,10 +101,11 @@ class Pinboard { * * @param entry The entry to delete. */ + @SuppressWarnings("Convert2Diamond") final void deletePost(final EntryLink entry) { final String link = entry.getLink(); - final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() { return pinboard.deletePin(link); @@ -139,8 +141,9 @@ class Pinboard { * @param oldUrl The old post URL. * @param entry The entry to add. */ + @SuppressWarnings("Convert2Diamond") final void updatePost(final String oldUrl, final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() { if (!oldUrl.equals(entry.getLink())) { From 17aa4f4bd65efb8048ed05c208a56919c0394acf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 17 Mar 2020 18:33:42 -0700 Subject: [PATCH 332/842] Cleanup. --- .idea/compiler.xml | 5 +---- .idea/misc.xml | 2 +- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 4 ++-- .idea/modules/mobibot.test.iml | 4 ++-- build.gradle | 1 - .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 2 +- version.properties | 6 +++--- 9 files changed, 14 insertions(+), 18 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index c370bd1..d0652e5 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -12,9 +12,6 @@ <module name="mobibot.main" /> </profile> </annotationProcessing> - <bytecodeTargetLevel> - <module name="mobibot.main" target="13" /> - <module name="mobibot.test" target="13" /> - </bytecodeTargetLevel> + <bytecodeTargetLevel target="13" /> </component> </project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 5dd7423..4f4003c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,5 +6,5 @@ <component name="JavaScriptSettings"> <option name="languageLevel" value="ES6" /> </component> - <component name="ProjectRootManager" version="2" project-jdk-name="13" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="true" project-jdk-name="13" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 524d563..204a365 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+549" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+564" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index cd62ea3..2746ab0 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+549" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_13"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+564" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> <exclude-output /> <content url="file://$MODULE_DIR$/../../src/generated/java"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 4976f18..c6b402b 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+549" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_13"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+564" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> <exclude-output /> <content url="file://$MODULE_DIR$/../../src/test"> diff --git a/build.gradle b/build.gradle index 6598a14..04db9d4 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,6 @@ plugins { id "org.sonarqube" version "2.8" } - import com.github.spotbugs.SpotBugsTask defaultTasks 'deploy' diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 769bb12..25ac76b 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1568402741804L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1584495033449L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "526"; - public static final String VERSION = "0.7.3-beta+526"; + public static final String BUILDMETA = "566"; + public static final String VERSION = "0.7.3-beta+566"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index a9a3d7a..c8875f2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -710,7 +710,7 @@ public class Mobibot extends PircBot { } } - final StringBuilder info = new StringBuilder(28); + final StringBuilder info = new StringBuilder(29); info.append("Uptime: ").append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())).append( " [Entries: ").append(entries.size()); diff --git a/version.properties b/version.properties index 023af81..b18aedb 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 13 12:25:40 PDT 2019 -version.buildmeta=526 +#Tue Mar 17 18:30:32 PDT 2020 +version.buildmeta=566 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+526 +version.semver=0.7.3-beta+566 From acc7c421129852a96c5b3d5636868c5b8159f0e8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 18 Mar 2020 15:25:41 -0700 Subject: [PATCH 333/842] Updated dependencides for Java 14. --- .idea/compiler.xml | 6 +- .idea/misc.xml | 2 +- .idea/mobibot.iml | 4 +- .idea/modules/mobibot.main.iml | 26 ++++---- .idea/modules/mobibot.test.iml | 42 +++++++------ build.gradle | 72 +++++++++++------------ gradle/wrapper/gradle-wrapper.jar | Bin 58695 -> 58694 bytes gradle/wrapper/gradle-wrapper.properties | 5 +- gradlew.bat | 3 + src/test/resources/testng.xml | 2 +- version.properties | 6 +- 11 files changed, 90 insertions(+), 78 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index d0652e5..0ffd2d5 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -12,6 +12,10 @@ <module name="mobibot.main" /> </profile> </annotationProcessing> - <bytecodeTargetLevel target="13" /> + <bytecodeTargetLevel> + <module name="mobibot" target="14" /> + <module name="mobibot.main" target="14" /> + <module name="mobibot.test" target="14" /> + </bytecodeTargetLevel> </component> </project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 4f4003c..21062cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,5 +6,5 @@ <component name="JavaScriptSettings"> <option name="languageLevel" value="ES6" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="true" project-jdk-name="13" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="false" project-jdk-name="13" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 204a365..ff7c6e0 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+564" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+571" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> <excludeFolder url="file://$MODULE_DIR$/.gradle" /> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 2746ab0..8c73c3a 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+564" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+571" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> <exclude-output /> <content url="file://$MODULE_DIR$/../../src/generated/java"> @@ -14,34 +14,34 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.3.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.4.1" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-RC3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.3" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index c6b402b..1811810 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+564" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+571" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> <exclude-output /> <content url="file://$MODULE_DIR$/../../src/test"> @@ -11,46 +11,50 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="mobibot.main" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.3.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.4.1" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-RC3" level="project" /> - <orderEntry type="library" name="Gradle: org.testng:testng:7.1.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0" level="project" /> + <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.3" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.2.2" level="project" /> <orderEntry type="library" name="Gradle: com.beust:jcommander:1.72" level="project" /> <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> - <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.1.0" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.ant:ant-launcher:1.10.3" level="project" /> - <orderEntry type="library" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" /> + <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.guava:guava:19.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.ant:ant-launcher:1.10.3" level="project" /> + <orderEntry type="library" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" /> + <orderEntry type="library" name="Gradle: org.checkerframework:checker-compat-qual:2.0.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.errorprone:error_prone_annotations:2.1.3" level="project" /> + <orderEntry type="library" name="Gradle: com.google.j2objc:j2objc-annotations:1.1" level="project" /> + <orderEntry type="library" name="Gradle: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" /> </component> <component name="TestModuleProperties" production-module="mobibot.main" /> </module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 04db9d4..1e84027 100644 --- a/build.gradle +++ b/build.gradle @@ -5,13 +5,13 @@ plugins { id 'jacoco' id 'java' id 'pmd' - id "com.github.ben-manes.versions" version "0.27.0" - id "com.github.spotbugs" version "3.0.0" + id "com.github.ben-manes.versions" version "0.28.0" + id "com.github.spotbugs" version "4.0.4" id "net.thauvin.erik.gradle.semver" version "1.0.4" id "org.sonarqube" version "2.8" } -import com.github.spotbugs.SpotBugsTask +import com.github.spotbugs.snom.SpotBugsTask defaultTasks 'deploy' @@ -23,9 +23,9 @@ mainClassName = packageName + '.Mobibot' ext { versions = [ - kotlin : '1.3.61', - log4j : '2.13.0', - spotbugs : '4.0.0-RC3' + kotlin : '1.3.70', + log4j : '2.13.1', + spotbugs : '4.0.0' ] } @@ -39,34 +39,34 @@ dependencies { annotationProcessor semverProcessor compileOnly semverProcessor - compile 'pircbot:pircbot:1.5.0' + implementation 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' - compile "org.apache.logging.log4j:log4j-api:$versions.log4j" - compile "org.apache.logging.log4j:log4j-core:$versions.log4j" - compile "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" + implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" + implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" + implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" - compile 'org.apache.commons:commons-lang3:3.9' - compile 'commons-cli:commons-cli:1.4' + implementation 'org.apache.commons:commons-lang3:3.9' + implementation 'commons-cli:commons-cli:1.4' - compile 'commons-net:commons-net:3.6' - compile 'com.squareup.okhttp3:okhttp:4.3.1' + implementation 'commons-net:commons-net:3.6' + implementation 'com.squareup.okhttp3:okhttp:4.4.1' - compile 'com.rometools:rome:1.12.2' + implementation 'com.rometools:rome:1.12.2' - compile 'org.json:json:20190722' - compile 'org.jsoup:jsoup:1.12.2' - compile 'net.objecthunter:exp4j:0.4.8' + implementation 'org.json:json:20190722' + implementation 'org.jsoup:jsoup:1.13.1' + implementation 'net.objecthunter:exp4j:0.4.8' - compile 'org.twitter4j:twitter4j-core:4.0.7' - compile 'net.thauvin.erik:pinboard-poster:1.0.1' + implementation 'org.twitter4j:twitter4j-core:4.0.7' + implementation 'net.thauvin.erik:pinboard-poster:1.0.1' - compile 'net.aksingh:owm-japis:2.5.3.0' + implementation 'net.aksingh:owm-japis:2.5.3.0' // Override own-japis dependencies with newer version of Kotlin - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" - testImplementation 'org.testng:testng:7.1.1' + testImplementation 'org.testng:testng:7.2.0' testImplementation 'org.assertj:assertj-core:3.15.0' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' @@ -78,15 +78,24 @@ dependencies { test { useTestNG() { - suites('src/test/resources/testng.xml') + options.suites('src/test/resources/testng.xml') } } spotbugs { - toolVersion = versions.spotbugs + toolVersion.set("$versions.spotbugs") +} + +tasks.withType(SpotBugsTask) { + reports { + xml.enabled = false + html.enabled = true + } + excludeFilter.set(file("$projectDir/config/spotbugs/excludeFilter.xml")) } pmd { + toolVersion = '6.22.0' ignoreFailures = true ruleSets = [] ruleSetFiles = files("${projectDir}/config/pmd.xml") @@ -113,7 +122,6 @@ javadoc { jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) - archiveVersion.set("") } @@ -150,14 +158,6 @@ jacocoTestReport { } } -tasks.withType(SpotBugsTask) { - reports { - xml.enabled = false - html.enabled = true - } - excludeFilter = file("$projectDir/config/spotbugs/excludeFilter.xml") -} - tasks.withType(Checkstyle) { reports { xml.enabled = false @@ -176,7 +176,7 @@ task copyToDeploy(type: Copy) { } task copyToDeployLib(type: Copy) { - from(configurations.runtime) { + from(configurations.runtimeClasspath) { exclude 'annotations-*.jar' } into deployDir + '/lib' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f3d88b1c2faf2fc91d853cd5d4242b5547257070..490fda8577df6c95960ba7077c43220e5bb2c0d9 100644 GIT binary patch delta 6577 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 MViXjGx&OBR0?kH%b^rhX delta 6547 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 HKP~w$%`S9K diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1b16c34..6b63449 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Wed Mar 18 14:49:13 PDT 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-rc-3-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 9618d8d..62bd9b9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml index 544049c..0486058 100644 --- a/src/test/resources/testng.xml +++ b/src/test/resources/testng.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > +<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" > <suite name="tests"> <test name="full"> <packages> diff --git a/version.properties b/version.properties index b18aedb..a198481 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Mar 17 18:30:32 PDT 2020 -version.buildmeta=566 +#Wed Mar 18 15:07:49 PDT 2020 +version.buildmeta=574 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+566 +version.semver=0.7.3-beta+574 From 4aa8cc2df6743c5dcea989f5665423bc71951750 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 18 Mar 2020 15:47:51 -0700 Subject: [PATCH 334/842] Prevent XML External Entities vulnerability in CurrenctyConverter module. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 8 ++++---- .idea/modules/mobibot.test.iml | 2 +- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- .../thauvin/erik/mobibot/modules/CurrencyConverter.java | 4 ++++ version.properties | 6 +++--- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index ff7c6e0..e4284c1 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+571" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+574" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 8c73c3a..763c148 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+571" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+574" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> <exclude-output /> @@ -12,8 +12,10 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> @@ -30,8 +32,7 @@ <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> @@ -40,7 +41,6 @@ <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.3" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> </component> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 1811810..4435909 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+571" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+574" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> <exclude-output /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 25ac76b..f50da5d 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1584495033449L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1584571512171L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "566"; - public static final String VERSION = "0.7.3-beta+566"; + public static final String BUILDMETA = "579"; + public static final String VERSION = "0.7.3-beta+579"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index cb8ed9c..0d4cfe7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -47,6 +47,7 @@ import org.jdom2.JDOMException; import org.jdom2.Namespace; import org.jdom2.input.SAXBuilder; +import javax.xml.XMLConstants; import java.io.IOException; import java.net.URL; import java.text.NumberFormat; @@ -137,6 +138,9 @@ public final class CurrencyConverter extends ThreadedModule { if (EXCHANGE_RATES.isEmpty()) { try { final SAXBuilder builder = new SAXBuilder(); + // See https://rules.sonarsource.com/java/tag/owasp/RSPEC-2755 + builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); builder.setIgnoringElementContentWhitespace(true); final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); diff --git a/version.properties b/version.properties index a198481..cbd68fc 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Mar 18 15:07:49 PDT 2020 -version.buildmeta=574 +#Wed Mar 18 15:45:11 PDT 2020 +version.buildmeta=579 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+574 +version.semver=0.7.3-beta+579 From b4345015c3fd1cf14b590a566b316f142ca335b2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 18 Mar 2020 16:11:24 -0700 Subject: [PATCH 335/842] Fixed jar manifest building (compile to implementation side effect.) --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- build.gradle | 2 +- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index e4284c1..ee01481 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+574" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+581" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 763c148..b44442f 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+574" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+581" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> <exclude-output /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 4435909..9b82227 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+574" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+581" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> <exclude-output /> diff --git a/build.gradle b/build.gradle index 1e84027..b7a400e 100644 --- a/build.gradle +++ b/build.gradle @@ -121,7 +121,7 @@ javadoc { jar { manifest.attributes('Main-Class': mainClassName, - 'Class-Path': '. ./lib/' + configurations.compile.collect { it.getName() }.join(' ./lib/')) + 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) archiveVersion.set("") } diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index f50da5d..9836bac 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1584571512171L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1584572412391L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "579"; - public static final String VERSION = "0.7.3-beta+579"; + public static final String BUILDMETA = "581"; + public static final String VERSION = "0.7.3-beta+581"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index cbd68fc..adae677 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Mar 18 15:45:11 PDT 2020 -version.buildmeta=579 +#Wed Mar 18 16:00:11 PDT 2020 +version.buildmeta=581 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+579 +version.semver=0.7.3-beta+581 From fa2eb27a1a6ff97545af732dee04086f3959cc53 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 18 Mar 2020 18:24:03 -0700 Subject: [PATCH 336/842] Changed Object to Element. (sonarcloud) --- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 0d4cfe7..cc35860 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -154,8 +154,8 @@ public final class CurrencyConverter extends ThreadedModule { final List<Element> cubes = cubeTime.getChildren(); Element cube; - for (final Object rawCube : cubes) { - cube = (Element) rawCube; + for (final Element rawCube : cubes) { + cube = rawCube; EXCHANGE_RATES.put( cube.getAttribute("currency").getValue(), cube.getAttribute("rate").getValue()); From 3a6d2d10718dc45c09ef011ba7d812b95daa31f7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 18 Mar 2020 18:25:27 -0700 Subject: [PATCH 337/842] Fixed logging messages. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index c8875f2..1c7b44c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -342,7 +342,7 @@ public class Mobibot extends PircBot { if (retries == MAX_RECONNECT) { if (logger.isDebugEnabled()) { logger.debug( - "Unable to reconnect to " + ircServer + " after " + MAX_RECONNECT + " retries.", ex); + "Unable to reconnect to {} after {} retries.", ircServer, MAX_RECONNECT, ex); } e.printStackTrace(System.err); @@ -1522,7 +1522,7 @@ public class Mobibot extends PircBot { twitterModule.post(twitterHandle, getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true); } catch (ModuleException e) { if (logger.isWarnEnabled()) { - logger.warn("Failed to notify @" + twitterHandle + ": " + msg, e); + logger.warn("Failed to notify @{}: {}", twitterHandle, msg, e); } } }).start(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index f9df1af..3a019a6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -177,7 +177,7 @@ public final class Lookup extends AbstractModule { } } catch (IOException ioe) { if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Unable to perform whois IP lookup: " + args, ioe); + bot.getLogger().debug("Unable to perform whois IP lookup: {}", args, ioe); } bot.send("Unable to perform whois IP lookup: " + ioe.getMessage()); From c261c48931bf6518c63ca2345fc7363a70c66850 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 18 Mar 2020 18:26:55 -0700 Subject: [PATCH 338/842] Code format. --- .../net/thauvin/erik/mobibot/Mobibot.java | 248 +++++++++--------- .../thauvin/erik/mobibot/modules/Lookup.java | 8 +- 2 files changed, 130 insertions(+), 126 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 1c7b44c..de9574c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -113,7 +113,7 @@ public class Mobibot extends PircBot { // The info strings. private static final String[] INFO_STRS = {ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", - "https://www.mobitopia.org/mobibot/"}; + "https://www.mobitopia.org/mobibot/"}; // The link match string. private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; @@ -136,15 +136,15 @@ public class Mobibot extends PircBot { // The tags/categories marker. private static final String TAGS_MARKER = "tags:"; - // The version strings. - private static final String[] VERSION_STRS = {"Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate( - ReleaseInfo.BUILDDATE) + ')', "Platform: " + System.getProperty("os.name") + " (" + System.getProperty( - "os.version") + ", " + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + " (build " - + System.getProperty("java.runtime.version") + ')', - "VM: " + System.getProperty("java.vm.name") + " (build " + System - .getProperty("java.vm.version") + ", " + System.getProperty( - "java.vm.info") + ')'}; + /* The version strings.*/ + private static final String[] VERSION_STRS = + {"Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System + .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System + .getProperty("java.runtime.version") + ')', + "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " + + System.getProperty("java.vm.info") + ')'}; // The logger. private static final Logger logger = LogManager.getLogger(Mobibot.class); // The commands list. @@ -302,6 +302,111 @@ public class Mobibot extends PircBot { saveEntries(true); } + /** + * The Truth Is Out There... + * + * @param args The command line arguments. + */ + @SuppressFBWarnings( + { + "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", + "DM_DEFAULT_ENCODING", + "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" + }) + @SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource"}) + public static void main(final String[] args) { + // Setup the command line options + final Options options = new Options() + .addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message") + .addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, + "print debug & logging data directly to the console") + .addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file") + .desc("use " + "alternate properties file").longOpt(Commands.PROPS_ARG).build()) + .addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); + + // Parse the command line + final CommandLineParser parser = new DefaultParser(); + CommandLine line = null; + + try { + line = parser.parse(options, args); + } catch (ParseException e) { + System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); + e.printStackTrace(System.err); + System.exit(1); + } + + if (line.hasOption(Commands.HELP_ARG.charAt(0))) { + // Output the usage + new HelpFormatter().printHelp(Mobibot.class.getName(), options); + } else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) { + for (final String s : INFO_STRS) { + System.out.println(s); + } + } else { + final Properties p = new Properties(); + + try (final InputStream fis = Files.newInputStream( + Paths.get(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + // Load the properties files + p.load(fis); + } catch (FileNotFoundException e) { + System.err.println("Unable to find properties file."); + e.printStackTrace(System.err); + System.exit(1); + } catch (IOException e) { + System.err.println("Unable to open properties file."); + e.printStackTrace(System.err); + System.exit(1); + } + + final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase(Constants.LOCALE)); + final String channel = p.getProperty("channel"); + final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); + + // Redirect the stdout and stderr + if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { + try { + final PrintStream stdout = new PrintStream( + new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + System.setOut(stdout); + } catch (IOException e) { + System.err.println("Unable to open output (stdout) log file."); + e.printStackTrace(System.err); + System.exit(1); + } + + try { + final PrintStream stderr = new PrintStream(new FileOutputStream(logsDir + nickname + ".err", true)); + System.setErr(stderr); + } catch (IOException e) { + System.err.println("Unable to open error (stderr) log file."); + e.printStackTrace(System.err); + System.exit(1); + } + } + + // Create the bot + final Mobibot bot = new Mobibot(nickname, channel, logsDir, p); + + // Connect + bot.connect(); + } + } + + /** + * Sleeps for the specified number of seconds. + * + * @param secs The number of seconds to sleep for. + */ + private static void sleep(final int secs) { + try { + Thread.sleep(secs * 1000L); + } catch (InterruptedException ignore) { + // Do nothing. + } + } + /** * Sends an action to the current channel. * @@ -595,8 +700,8 @@ public class Mobibot extends PircBot { commandsList.add(Commands.USERS_CMD); commandsList.add(Commands.VIEW_CMD); - MODULES.stream().filter(AbstractModule::isEnabled).forEach( - module -> commandsList.addAll(module.getCommands())); + MODULES.stream().filter(AbstractModule::isEnabled) + .forEach(module -> commandsList.addAll(module.getCommands())); if (tell.isEnabled()) { commandsList.add(Tell.TELL_CMD); @@ -625,9 +730,9 @@ public class Mobibot extends PircBot { if (isOp(sender)) { send(sender, "The op commands are:"); - send(sender, helpIndent( - Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " + Commands.SAY_CMD - + " " + Commands.VERSION_CMD)); + send(sender, + helpIndent(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " + + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); } } } @@ -761,94 +866,6 @@ public class Mobibot extends PircBot { twitterNotification("has joined " + ircChannel); } - /** - * The Truth Is Out There... - * - * @param args The command line arguments. - */ - @SuppressFBWarnings( - {"INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", "DM_DEFAULT_ENCODING", "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS"}) - @SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource"}) - public static void main(final String[] args) { - // Setup the command line options - final Options options = new Options(); - options.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message"); - options.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, - "print debug & logging data directly to the console"); - options.addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file").desc( - "use " + "alternate properties file").longOpt(Commands.PROPS_ARG).build()); - options.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); - - // Parse the command line - final CommandLineParser parser = new DefaultParser(); - CommandLine line = null; - - try { - line = parser.parse(options, args); - } catch (ParseException e) { - System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } - - if (line.hasOption(Commands.HELP_ARG.charAt(0))) { - // Output the usage - new HelpFormatter().printHelp(Mobibot.class.getName(), options); - } else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) { - for (final String s : INFO_STRS) { - System.out.println(s); - } - } else { - final Properties p = new Properties(); - - try (final InputStream fis = Files.newInputStream( - Paths.get(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { - // Load the properties files - p.load(fis); - } catch (FileNotFoundException e) { - System.err.println("Unable to find properties file."); - e.printStackTrace(System.err); - System.exit(1); - } catch (IOException e) { - System.err.println("Unable to open properties file."); - e.printStackTrace(System.err); - System.exit(1); - } - - final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase(Constants.LOCALE)); - final String channel = p.getProperty("channel"); - final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); - - // Redirect the stdout and stderr - if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { - try { - final PrintStream stdout = new PrintStream( - new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); - System.setOut(stdout); - } catch (IOException e) { - System.err.println("Unable to open output (stdout) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - - try { - final PrintStream stderr = new PrintStream(new FileOutputStream(logsDir + nickname + ".err", true)); - System.setErr(stderr); - } catch (IOException e) { - System.err.println("Unable to open error (stderr) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - } - - // Create the bot - final Mobibot bot = new Mobibot(nickname, channel, logsDir, p); - - // Connect - bot.connect(); - } - } - /** * {@inheritDoc} */ @@ -1481,19 +1498,6 @@ public class Mobibot extends PircBot { weblogUrl = url; } - /** - * Sleeps for the specified number of seconds. - * - * @param secs The number of seconds to sleep for. - */ - private static void sleep(final int secs) { - try { - Thread.sleep(secs * 1000L); - } catch (InterruptedException ignore) { - // Do nothing. - } - } - /** * Stores the last 10 public messages and actions. * @@ -1620,14 +1624,14 @@ public class Mobibot extends PircBot { entry = entries.get(i); if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase(Constants.LOCALE).contains(lcArgs)) || (entry.getTitle() - .toLowerCase( - Constants.LOCALE) - .contains(lcArgs)) + if ((entry.getLink().toLowerCase(Constants.LOCALE).contains(lcArgs)) + || (entry.getTitle().toLowerCase(Constants.LOCALE).contains(lcArgs)) || (entry.getNick().toLowerCase(Constants.LOCALE).contains(lcArgs))) { if (sent > MAX_ENTRIES) { - send(sender, "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), isPrivate); + send(sender, + "To view more, try: " + Utils + .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), + isPrivate); break; } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 3a019a6..cdd8113 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -72,7 +72,7 @@ public final class Lookup extends AbstractModule { * @throws java.net.UnknownHostException If the host is unknown. */ public static String lookup(final String query) - throws UnknownHostException { + throws UnknownHostException { final StringBuilder buffer = new StringBuilder(); final InetAddress[] results = InetAddress.getAllByName(query); @@ -107,7 +107,7 @@ public final class Lookup extends AbstractModule { * @throws java.io.IOException If a connection error occurs. */ private static String[] whois(final String query) - throws IOException { + throws IOException { return whois(query, WHOIS_HOST); } @@ -120,7 +120,7 @@ public final class Lookup extends AbstractModule { * @throws java.io.IOException If a connection error occurs. */ public static String[] whois(final String query, final String host) - throws IOException { + throws IOException { final WhoisClient whoisClient = new WhoisClient(); final String[] lines; @@ -157,7 +157,7 @@ public final class Lookup extends AbstractModule { bot.send(Lookup.lookup(args)); } catch (UnknownHostException ignore) { if (args.matches( - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) { try { final String[] lines = Lookup.whois(args); From d8da21b0ef18f5f0ecbda64690992cf723363294 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 23 Mar 2020 05:50:59 -0700 Subject: [PATCH 339/842] Updaded dependencies. --- .idea/codeStyles/codeStyleConfig.xml | 5 +++ .idea/copyright/Erik_s_Copyright_Notice.xml | 6 +++ .idea/copyright/profiles_settings.xml | 3 ++ .../inspectionProfiles/profiles_settings.xml | 7 +++ .idea/misc.xml | 2 +- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 39 +++++++++++++++- .idea/modules/mobibot.test.iml | 45 ++++++++++++++++++- README.md | 4 +- build.gradle | 26 +++++++---- detekt-baseline.xml | 37 +++++++++++++++ gradle/wrapper/gradle-wrapper.properties | 37 +++++++++++++-- 12 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/copyright/Erik_s_Copyright_Notice.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 detekt-baseline.xml diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..d91f848 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ +<component name="ProjectCodeStyleConfiguration"> + <state> + <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> + </state> +</component> \ No newline at end of file diff --git a/.idea/copyright/Erik_s_Copyright_Notice.xml b/.idea/copyright/Erik_s_Copyright_Notice.xml new file mode 100644 index 0000000..b9f5293 --- /dev/null +++ b/.idea/copyright/Erik_s_Copyright_Notice.xml @@ -0,0 +1,6 @@ +<component name="CopyrightManager"> + <copyright> + <option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> + <option name="myName" value="Erik's Copyright Notice" /> + </copyright> +</component> \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..1419e40 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ +<component name="CopyrightManager"> + <settings default="Erik's Copyright Notice" /> +</component> \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..dc2dcae --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ +<component name="InspectionProjectProfileManager"> + <settings> + <option name="PROJECT_PROFILE" value="Erik's Default" /> + <option name="USE_PROJECT_PROFILE" value="false" /> + <version value="1.0" /> + </settings> +</component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 21062cd..52412b4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,5 +6,5 @@ <component name="JavaScriptSettings"> <option name="languageLevel" value="ES6" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="false" project-jdk-name="13" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="false" project-jdk-name="14" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index ee01481..b51eeda 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+581" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+660" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index b44442f..81ea0b8 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,40 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+581" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+660" type="JAVA_MODULE" version="4"> + <component name="FacetManager"> + <facet type="kotlin-language" name="Kotlin"> + <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> + <compilerSettings> + <option name="additionalArguments" value="-Xallow-no-source-files" /> + </compilerSettings> + <compilerArguments> + <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.70/c706d9a12aa043400daacbb15b61ba662a1eb9a9/kotlin-stdlib-jdk8-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.70/95aee922bc2a0fff3b7bfa1b6503ff0afe38264a/kotlin-stdlib-jdk7-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="noStdlib" value="true" /> + <option name="noReflect" value="true" /> + <option name="moduleName" value="mobibot" /> + <option name="languageVersion" value="1.3" /> + <option name="apiVersion" value="1.3" /> + <option name="pluginOptions"> + <array /> + </option> + <option name="pluginClasspaths"> + <array> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.70/28a3923786ef1a651ebea81ff0f211dfd7436070/kotlin-script-runtime-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.70/74a96e3d496437e9b9bb02433f997b7e8868b36e/kotlin-scripting-common-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.70/b2af86b5d6380cbf6a0eb6d15fec26cd48b20869/kotlin-scripting-jvm-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> + <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + </array> + </option> + <option name="errors"> + <ArgumentParseErrors /> + </option> + </compilerArguments> + </configuration> + </facet> + </component> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> <exclude-output /> @@ -15,7 +50,7 @@ <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 9b82227..e065203 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,46 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+581" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+660" type="JAVA_MODULE" version="4"> + <component name="FacetManager"> + <facet type="kotlin-language" name="Kotlin"> + <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> + <compilerSettings> + <option name="additionalArguments" value="-Xallow-no-source-files" /> + </compilerSettings> + <compilerArguments> + <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.70/c706d9a12aa043400daacbb15b61ba662a1eb9a9/kotlin-stdlib-jdk8-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.70/95aee922bc2a0fff3b7bfa1b6503ff0afe38264a/kotlin-stdlib-jdk7-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="noStdlib" value="true" /> + <option name="noReflect" value="true" /> + <option name="moduleName" value="mobibot" /> + <option name="friendPaths"> + <array> + <option value="$MODULE_DIR$/../../build/classes/java/main" /> + <option value="$MODULE_DIR$/../../build/classes/kotlin/main" /> + </array> + </option> + <option name="languageVersion" value="1.3" /> + <option name="apiVersion" value="1.3" /> + <option name="pluginOptions"> + <array /> + </option> + <option name="pluginClasspaths"> + <array> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.70/28a3923786ef1a651ebea81ff0f211dfd7436070/kotlin-script-runtime-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.70/74a96e3d496437e9b9bb02433f997b7e8868b36e/kotlin-scripting-common-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.70/b2af86b5d6380cbf6a0eb6d15fec26cd48b20869/kotlin-scripting-jvm-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> + <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + </array> + </option> + <option name="errors"> + <ArgumentParseErrors /> + </option> + </compilerArguments> + </configuration> + </facet> + </component> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> <exclude-output /> @@ -27,7 +68,7 @@ <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.70" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> diff --git a/README.md b/README.md index 7e3e3d6..4b2ed1c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![Build Status](https://travis-ci.org/ethauvin/mobibot.svg?branch=master)](https://travis-ci.org/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![Build Status](https://travis-ci.com/ethauvin/mobibot.svg?branch=master)](https://travis-ci.com/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: @@ -24,4 +24,4 @@ Some very basic instructions: { launch } /usr/bin/nohup java -jar mobibot.jar & -``` \ No newline at end of file +``` diff --git a/build.gradle b/build.gradle index b7a400e..46d3c27 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,16 @@ plugins { - id 'checkstyle' id 'application' + id 'checkstyle' + id 'com.github.ben-manes.versions' version '0.28.0' + id 'com.github.spotbugs' version '4.0.4' id 'idea' + id 'io.gitlab.arturbosch.detekt' version '1.7.0-beta2' id 'jacoco' id 'java' + id 'net.thauvin.erik.gradle.semver' version '1.0.4' + id 'org.jetbrains.kotlin.jvm' version '1.3.70' + id 'org.sonarqube' version '2.8' id 'pmd' - id "com.github.ben-manes.versions" version "0.28.0" - id "com.github.spotbugs" version "4.0.4" - id "net.thauvin.erik.gradle.semver" version "1.0.4" - id "org.sonarqube" version "2.8" } import com.github.spotbugs.snom.SpotBugsTask @@ -25,7 +27,7 @@ ext { versions = [ kotlin : '1.3.70', log4j : '2.13.1', - spotbugs : '4.0.0' + spotbugs : '4.0.1' ] } @@ -62,7 +64,7 @@ dependencies { implementation 'net.thauvin.erik:pinboard-poster:1.0.1' implementation 'net.aksingh:owm-japis:2.5.3.0' - // Override own-japis dependencies with newer version of Kotlin + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" @@ -102,6 +104,10 @@ pmd { consoleOutput = true } +detekt { + baseline = file("detekt-baseline.xml") +} + tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") @@ -151,6 +157,10 @@ sonarqube { } } +jacoco { + toolVersion = '0.8.5' +} + jacocoTestReport { reports { html.enabled = true @@ -194,7 +204,7 @@ task deploy(dependsOn: ['clean', 'build', 'jar']) { mustRunAfter clean } -task release(dependsOn: ['wrapper', 'clean', 'deploy']) { +task release(dependsOn: ['wrapper', 'deploy']) { group = 'Publishing' description = 'Releases new version.' } diff --git a/detekt-baseline.xml b/detekt-baseline.xml new file mode 100644 index 0000000..fee5cbc --- /dev/null +++ b/detekt-baseline.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" ?> +<!-- + ~ detekt-baseline.xml + ~ + ~ Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + ~ All rights reserved. + ~ + ~ Redistribution and use in source and binary forms, with or without + ~ modification, are permitted provided that the following conditions are met: + ~ + ~ Redistributions of source code must retain the above copyright notice, this + ~ list of conditions and the following disclaimer. + ~ + ~ Redistributions in binary form must reproduce the above copyright notice, + ~ this list of conditions and the following disclaimer in the documentation + ~ and/or other materials provided with the distribution. + ~ + ~ Neither the name of this project nor the names of its contributors may be + ~ used to endorse or promote products derived from this software without + ~ specific prior written permission. + ~ + ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + ~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + ~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + ~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + ~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + ~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + ~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + --> + +<SmellBaseline> + <Blacklist></Blacklist> + <Whitelist></Whitelist> +</SmellBaseline> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6b63449..5f7d5c4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,37 @@ -#Wed Mar 18 14:49:13 PDT 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-rc-3-all.zip +# +# gradle-wrapper.properties +# +# Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of this project nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-rc-3-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists From 92576f94964aa761c3e777904e5c10c193fc7d0d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 23 Mar 2020 05:53:11 -0700 Subject: [PATCH 340/842] Implemented Rock Paper Scissors module in Kotlin. --- LICENSE.txt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../net/thauvin/erik/mobibot/FeedReader.java | 15 +- .../net/thauvin/erik/mobibot/Mobibot.java | 131 ++++++++++-------- .../thauvin/erik/mobibot/TwitterOAuth.java | 32 +++++ .../java/net/thauvin/erik/mobibot/Utils.java | 10 +- .../erik/mobibot/modules/AbstractModule.java | 4 +- .../thauvin/erik/mobibot/modules/Calc.java | 8 +- .../mobibot/modules/CurrencyConverter.java | 100 ++++++------- .../thauvin/erik/mobibot/modules/Dice.java | 21 ++- .../erik/mobibot/modules/GoogleSearch.java | 4 +- .../thauvin/erik/mobibot/modules/Joke.java | 20 +-- .../thauvin/erik/mobibot/modules/Lookup.java | 15 +- .../thauvin/erik/mobibot/modules/Ping.java | 34 +++-- .../erik/mobibot/modules/RockPaperScissors.kt | 116 ++++++++++++++++ .../erik/mobibot/modules/StockQuote.java | 4 +- .../erik/mobibot/modules/ThreadedModule.java | 12 +- .../thauvin/erik/mobibot/modules/Twitter.java | 4 +- .../net/thauvin/erik/mobibot/modules/War.java | 21 ++- .../erik/mobibot/modules/Weather2.java | 4 +- .../erik/mobibot/modules/WorldTime.java | 107 +++++++------- .../erik/mobibot/entries/EntryLinkTest.java | 2 +- .../mobibot/modules/AbstractModuleTest.java | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 67 +++++++++ .../erik/mobibot/tell/TellMessageTest.java | 2 +- version.properties | 6 +- 26 files changed, 501 insertions(+), 248 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt diff --git a/LICENSE.txt b/LICENSE.txt index 4efe703..37785b0 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 9836bac..4cf0184 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1584572412391L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1584966079848L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "581"; - public static final String VERSION = "0.7.3-beta+581"; + public static final String BUILDMETA = "682"; + public static final String VERSION = "0.7.3-beta+682"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 43621a9..c73a8be 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -1,7 +1,7 @@ /* * FeedReader.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -88,11 +88,14 @@ class FeedReader implements Runnable { SyndEntry item; final List<SyndEntry> items = feed.getEntries(); - - for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { - item = items.get(i); - bot.send(sender, item.getTitle()); - bot.send(sender, TAB_INDENT + Utils.green(item.getLink())); + if (items.isEmpty()) { + bot.send(sender, "There is currently nothing to view. Why don't you post something?"); + } else { + for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { + item = items.get(i); + bot.send(sender, item.getTitle()); + bot.send(sender, TAB_INDENT + Utils.green(item.getLink())); + } } } catch (MalformedURLException e) { bot.getLogger().debug("Invalid feed URL.", e); diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index de9574c..97cf66e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -1,7 +1,7 @@ /* * Mobibot.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,6 +47,7 @@ import net.thauvin.erik.mobibot.modules.Joke; import net.thauvin.erik.mobibot.modules.Lookup; import net.thauvin.erik.mobibot.modules.ModuleException; import net.thauvin.erik.mobibot.modules.Ping; +import net.thauvin.erik.mobibot.modules.RockPaperScissors; import net.thauvin.erik.mobibot.modules.StockQuote; import net.thauvin.erik.mobibot.modules.Twitter; import net.thauvin.erik.mobibot.modules.War; @@ -62,7 +63,6 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -93,6 +93,9 @@ import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; +import static net.thauvin.erik.mobibot.Utils.bold; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + /** * Implements the #mobitopia bot. * @@ -101,7 +104,8 @@ import java.util.StringTokenizer; * @since 1.0 */ @SuppressWarnings("WeakerAccess") -@Version(properties = "version.properties", className = "ReleaseInfo") +@Version(properties = "version.properties", + className = "ReleaseInfo") public class Mobibot extends PircBot { // The default port. @@ -111,9 +115,10 @@ public class Mobibot extends PircBot { private static final String DEFAULT_SERVER = "irc.freenode.net"; // The info strings. - private static final String[] INFO_STRS = - {ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", - "https://www.mobitopia.org/mobibot/"}; + @SuppressWarnings("indentation") + private static final String[] INFO_STRS = { + ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", + "https://www.mobitopia.org/mobibot/" }; // The link match string. private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; @@ -137,14 +142,15 @@ public class Mobibot extends PircBot { private static final String TAGS_MARKER = "tags:"; /* The version strings.*/ + @SuppressWarnings("indentation") private static final String[] VERSION_STRS = - {"Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', - "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System - .getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System - .getProperty("java.runtime.version") + ')', - "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " - + System.getProperty("java.vm.info") + ')'}; + { "Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty( + "java.runtime.version") + ')', + "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " + + System.getProperty("java.vm.info") + ')' }; // The logger. private static final Logger logger = LogManager.getLogger(Mobibot.class); // The commands list. @@ -272,6 +278,7 @@ public class Mobibot extends PircBot { MODULES.add(new Joke()); MODULES.add(new Lookup()); MODULES.add(new Ping()); + MODULES.add(new RockPaperScissors()); MODULES.add(new StockQuote()); twitterModule = new Twitter(); @@ -308,21 +315,29 @@ public class Mobibot extends PircBot { * @param args The command line arguments. */ @SuppressFBWarnings( - { - "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", - "DM_DEFAULT_ENCODING", - "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" - }) - @SuppressWarnings({"PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource"}) + { + "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", + "DM_DEFAULT_ENCODING", + "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" + }) + @SuppressWarnings({ "PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource" }) public static void main(final String[] args) { // Setup the command line options final Options options = new Options() - .addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message") - .addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, - "print debug & logging data directly to the console") - .addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file") - .desc("use " + "alternate properties file").longOpt(Commands.PROPS_ARG).build()) - .addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info"); + .addOption(Commands.HELP_ARG.substring(0, 1), + Commands.HELP_ARG, + false, + "print this help message") + .addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, + "print debug & logging data directly to the console") + .addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg() + .argName("file") + .desc("use " + "alternate properties file") + .longOpt(Commands.PROPS_ARG).build()) + .addOption(Commands.VERSION_ARG.substring(0, 1), + Commands.VERSION_ARG, + false, + "print version info"); // Parse the command line final CommandLineParser parser = new DefaultParser(); @@ -347,7 +362,7 @@ public class Mobibot extends PircBot { final Properties p = new Properties(); try (final InputStream fis = Files.newInputStream( - Paths.get(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + Paths.get(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { // Load the properties files p.load(fis); } catch (FileNotFoundException e) { @@ -368,7 +383,7 @@ public class Mobibot extends PircBot { if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { try { final PrintStream stdout = new PrintStream( - new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); System.setOut(stdout); } catch (IOException e) { System.err.println("Unable to open output (stdout) log file."); @@ -423,7 +438,7 @@ public class Mobibot extends PircBot { * @param action The action. */ private void action(final String channel, final String action) { - if (StringUtils.isNotBlank(channel) && StringUtils.isNotBlank(action)) { + if (isNotBlank(channel) && isNotBlank(action)) { sendAction(channel, action); } } @@ -431,7 +446,7 @@ public class Mobibot extends PircBot { /** * Connects to the server and joins the channel. */ - @SuppressFBWarnings({"DM_EXIT", "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE"}) + @SuppressFBWarnings({ "DM_EXIT", "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE" }) public final void connect() { try { connect(ircServer, ircPort); @@ -447,7 +462,7 @@ public class Mobibot extends PircBot { if (retries == MAX_RECONNECT) { if (logger.isDebugEnabled()) { logger.debug( - "Unable to reconnect to {} after {} retries.", ircServer, MAX_RECONNECT, ex); + "Unable to reconnect to {} after {} retries.", ircServer, MAX_RECONNECT, ex); } e.printStackTrace(System.err); @@ -470,7 +485,7 @@ public class Mobibot extends PircBot { * @param sender The nick of the person who sent the private message. */ private void feedResponse(final String sender) { - if (StringUtils.isNotBlank(feedUrl)) { + if (isNotBlank(feedUrl)) { new Thread(new FeedReader(this, sender, feedUrl)).start(); } else { send(sender, "There is no weblog setup for this channel."); @@ -561,7 +576,7 @@ public class Mobibot extends PircBot { for (final char c : getNick().toCharArray()) { if (Character.isLetter(c)) { buff.append('[').append(String.valueOf(c).toLowerCase(Constants.LOCALE)).append( - String.valueOf(c).toUpperCase(Constants.LOCALE)).append(']'); + String.valueOf(c).toUpperCase(Constants.LOCALE)).append(']'); } else { buff.append(c); } @@ -606,7 +621,7 @@ public class Mobibot extends PircBot { * @return The indented help string. */ public String helpIndent(final String help, final boolean isBold) { - return " " + (isBold ? Utils.bold(help) : help); + return " " + (isBold ? bold(help) : help); } /** @@ -619,21 +634,21 @@ public class Mobibot extends PircBot { final String lcTopic = topic.toLowerCase(Constants.LOCALE).trim(); if (Commands.HELP_POSTING_KEYWORD.equals(lcTopic)) { - send(sender, Utils.bold("Post a URL, by saying it on a line on its own:")); + send(sender, bold("Post a URL, by saying it on a line on its own:")); send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); - send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + '1')); + send(sender, "I will reply with a label, for example: " + bold(Commands.LINK_CMD + '1')); send(sender, "To add a title, use a its label and a pipe:"); send(sender, helpIndent(Commands.LINK_CMD + "1:|This is the title")); send(sender, "To add a comment: "); send(sender, helpIndent(Commands.LINK_CMD + "1:This is a comment")); - send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + "1.1")); + send(sender, "I will reply with a label, for example: " + bold(Commands.LINK_CMD + "1.1")); send(sender, "To edit a comment, use its label: "); send(sender, helpIndent(Commands.LINK_CMD + "1.1:This is an edited comment")); send(sender, "To delete a comment, use its label and a minus sign: "); send(sender, helpIndent(Commands.LINK_CMD + "1.1:-")); send(sender, "You can also view a posting by saying its label."); } else if (Commands.HELP_TAGS_KEYWORD.equals(lcTopic)) { - send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:")); + send(sender, bold("To categorize or tag a URL, use its label and a T:")); send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); } else if (Commands.VIEW_CMD.equals(lcTopic)) { send(sender, "To list or search the current URL posts:"); @@ -685,7 +700,7 @@ public class Mobibot extends PircBot { } } - send(sender, Utils.bold("Type a URL on " + ircChannel + " to post it.")); + send(sender, bold("Type a URL on " + ircChannel + " to post it.")); send(sender, "For more information on a specific command, type:"); send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); send(sender, "The commands are:"); @@ -743,12 +758,12 @@ public class Mobibot extends PircBot { */ private void identify() { // Identify with NickServ - if (StringUtils.isNotBlank(identPwd)) { + if (isNotBlank(identPwd)) { identify(identPwd); } // Identify with a specified nick - if (StringUtils.isNotBlank(identNick) && StringUtils.isNotBlank(identMsg)) { + if (isNotBlank(identNick) && isNotBlank(identMsg)) { sendMessage(identNick, identMsg); } } @@ -818,7 +833,7 @@ public class Mobibot extends PircBot { final StringBuilder info = new StringBuilder(29); info.append("Uptime: ").append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())).append( - " [Entries: ").append(entries.size()); + " [Entries: ").append(entries.size()); if (tell.isEnabled() && isOp(sender)) { info.append(", Messages: ").append(tell.size()); @@ -836,7 +851,7 @@ public class Mobibot extends PircBot { * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. */ private boolean isIgnoredNick(final String nick) { - return StringUtils.isNotBlank(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE)); + return isNotBlank(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE)); } /** @@ -871,7 +886,7 @@ public class Mobibot extends PircBot { */ @Override protected final void onDisconnect() { - if (StringUtils.isNotBlank(weblogUrl)) { + if (isNotBlank(weblogUrl)) { setVersion(weblogUrl); } @@ -883,7 +898,8 @@ public class Mobibot extends PircBot { /** * {@inheritDoc} */ - @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.") + @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", + justification = "Working on it.") @Override protected final void onMessage(final String channel, final String sender, final String login, final String hostname, final String message) { @@ -923,7 +939,7 @@ public class Mobibot extends PircBot { if (data.length == 1) { title = data[0].trim(); } else { - if (StringUtils.isNotBlank(data[0])) { + if (isNotBlank(data[0])) { title = data[0].trim(); } @@ -936,7 +952,7 @@ public class Mobibot extends PircBot { final Document html = Jsoup.connect(link).userAgent("Mozilla").get(); final String htmlTitle = html.title(); - if (StringUtils.isNotBlank(htmlTitle)) { + if (isNotBlank(htmlTitle)) { title = htmlTitle; } } catch (IOException ignore) { @@ -962,7 +978,7 @@ public class Mobibot extends PircBot { } } else { final EntryLink entry = entries.get(dupIndex); - send(sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry)); + send(sender, bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry)); } } } else if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> @@ -1000,7 +1016,7 @@ public class Mobibot extends PircBot { for (final AbstractModule module : MODULES) { // modules for (final String c : module.getCommands()) { if (cmd.startsWith(c)) { - module.commandResponse(this, sender, args, false); + module.commandResponse(this, sender, cmd, args, false); } } } @@ -1185,7 +1201,8 @@ public class Mobibot extends PircBot { /** * {@inheritDoc} */ - @SuppressFBWarnings(value = {"DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, we want to bail out.") + @SuppressFBWarnings(value = { "DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY" }, + justification = "Yes, we want to bail out.") @Override protected final void onPrivateMessage(final String sender, final String login, final String hostname, final String message) { @@ -1280,7 +1297,7 @@ public class Mobibot extends PircBot { if (module.isPrivateMsgEnabled()) { for (final String c : module.getCommands()) { if (cmd.equals(c)) { - module.commandResponse(this, sender, args, true); + module.commandResponse(this, sender, cmd, args, true); return; } } @@ -1352,7 +1369,7 @@ public class Mobibot extends PircBot { * sent. */ public final void send(final String sender, final String message, final boolean isPrivate) { - if (StringUtils.isNotBlank(message) && StringUtils.isNotBlank(sender)) { + if (isNotBlank(message) && isNotBlank(sender)) { if (isPrivate) { if (logger.isDebugEnabled()) { logger.debug("Sending message to {} : {}", sender, message); @@ -1460,7 +1477,7 @@ public class Mobibot extends PircBot { * @param nicks The nicks to ignore */ final void setIgnoredNicks(final String nicks) { - if (StringUtils.isNotBlank(nicks)) { + if (isNotBlank(nicks)) { final StringTokenizer st = new StringTokenizer(nicks, ","); while (st.hasMoreTokens()) { @@ -1475,7 +1492,7 @@ public class Mobibot extends PircBot { * @param apiToken The API token */ final void setPinboardAuth(final String apiToken) { - if (StringUtils.isNotBlank(apiToken)) { + if (isNotBlank(apiToken)) { pinboard = new Pinboard(this, apiToken, ircServer); } } @@ -1520,7 +1537,7 @@ public class Mobibot extends PircBot { * @param msg The twitter message. */ final void twitterNotification(final String msg) { - if (twitterModule.isEnabled() && StringUtils.isNotBlank(twitterHandle)) { + if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { new Thread(() -> { try { twitterModule.post(twitterHandle, getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true); @@ -1629,8 +1646,8 @@ public class Mobibot extends PircBot { || (entry.getNick().toLowerCase(Constants.LOCALE).contains(lcArgs))) { if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + Utils - .bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), + "To view more, try: " + + bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), isPrivate); break; @@ -1642,7 +1659,7 @@ public class Mobibot extends PircBot { } else { if (sent > MAX_ENTRIES) { send(sender, - "To view more, try: " + Utils.bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), + "To view more, try: " + bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), isPrivate); break; diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index dc2f984..7d807d0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -1,3 +1,35 @@ +/* + * TwitterOAuth.java + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package net.thauvin.erik.mobibot; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 5228e93..1049078 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -1,7 +1,7 @@ /* * Utils.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot; import org.apache.commons.lang3.StringUtils; import org.jibble.pircbot.Colors; +import org.jsoup.Jsoup; import java.io.File; import java.time.LocalDateTime; @@ -233,8 +234,7 @@ public final class Utils { * @return The unescaped string. */ public static String unescapeXml(final String str) { - return str.replace("&", "&").replace("<", "<").replace(">", ">").replace(""", "\"").replace( - "'", "'").replace("'", "'"); + return Jsoup.parse(str).text(); } /** @@ -254,9 +254,9 @@ public final class Utils { final long weeks = days / 7; days %= 7; final long hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours( - TimeUnit.MILLISECONDS.toDays(uptime)); + TimeUnit.MILLISECONDS.toDays(uptime)); final long minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( - TimeUnit.MILLISECONDS.toHours(uptime)); + TimeUnit.MILLISECONDS.toHours(uptime)); if (years > 0) { info.append(years).append(plural(years, " year ", " years ")); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 61e0871..2d5409d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -1,7 +1,7 @@ /* * AbstractModule.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,11 +57,13 @@ public abstract class AbstractModule { * * @param bot The bot's instance. * @param sender The sender. + * @param cmd The command. * @param args The command arguments. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ public abstract void commandResponse(final Mobibot bot, final String sender, + final String cmd, final String args, final boolean isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index b852614..05fb963 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -1,7 +1,7 @@ /* * Calc.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,7 +81,11 @@ public class Calc extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { if (StringUtils.isNotBlank(args)) { bot.send(calc(args)); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index cc35860..a65beaa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -1,7 +1,7 @@ /* * CurrencyConverter.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -87,44 +87,6 @@ public final class CurrencyConverter extends ThreadedModule { commands.add(CURRENCY_CMD); } - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - synchronized (this) { - if (!pubDate.equals(Utils.today())) { - EXCHANGE_RATES.clear(); - } - } - - super.commandResponse(bot, sender, args, isPrivate); - } - - /** - * Converts the specified currencies. - */ - @SuppressFBWarnings("REDOS") - @Override - void run(final Mobibot bot, final String sender, final String query) { - if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(query)) { - if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { - try { - final Message msg = convertCurrency(query); - if (msg.isError()) { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false); - } - bot.send(sender, msg); - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); - } - } else { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); - } - } - } - /** * Converts from a currency to another. * @@ -157,8 +119,8 @@ public final class CurrencyConverter extends ThreadedModule { for (final Element rawCube : cubes) { cube = rawCube; EXCHANGE_RATES.put( - cube.getAttribute("currency").getValue(), - cube.getAttribute("rate").getValue()); + cube.getAttribute("currency").getValue(), + cube.getAttribute("rate").getValue()); } EXCHANGE_RATES.put("EUR", "1"); @@ -166,7 +128,7 @@ public final class CurrencyConverter extends ThreadedModule { throw new ModuleException(query, "An error has occurred while parsing the exchange rates table.", e); } catch (IOException e) { throw new ModuleException( - query, "An error has occurred while fetching the exchange rates table.", e); + query, "An error has occurred while fetching the exchange rates table.", e); } } @@ -181,23 +143,23 @@ public final class CurrencyConverter extends ThreadedModule { } else { try { final double amt = Double.parseDouble(cmds[0].replace(",", "")); - final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1] - .toUpperCase(Constants.LOCALE))); + final double from = + Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase(Constants.LOCALE))); final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase(Constants.LOCALE))); return new PublicMessage( - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) + ' ' + cmds[1].toUpperCase(Constants.LOCALE) + " = " + NumberFormat.getCurrencyInstance(Locale.US) - .format((amt * to) / from) - .substring(1) + .format((amt * to) / from) + .substring(1) + ' ' + cmds[3].toUpperCase(Constants.LOCALE)); } catch (Exception e) { throw new ModuleException("convertCurrency(" + query + ')', - "The supported currencies are: " + EXCHANGE_RATES.keySet(), e); + "The supported currencies are: " + EXCHANGE_RATES.keySet(), e); } } } else if (CURRENCY_RATES_KEYWORD.equals(query)) { @@ -219,6 +181,48 @@ public final class CurrencyConverter extends ThreadedModule { return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet()); } + /** + * {@inheritDoc} + */ + @Override + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { + synchronized (this) { + if (!pubDate.equals(Utils.today())) { + EXCHANGE_RATES.clear(); + } + } + + super.commandResponse(bot, sender, cmd, args, isPrivate); + } + + /** + * Converts the specified currencies. + */ + @SuppressFBWarnings("REDOS") + @Override + void run(final Mobibot bot, final String sender, final String cmd, final String query) { + if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(query)) { + if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { + try { + final Message msg = convertCurrency(query); + if (msg.isError()) { + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false); + } + bot.send(sender, msg); + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); + bot.send(sender, e.getMessage()); + } + } else { + helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); + } + } + } + /** * {@inheritDoc} */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 1e69b10..4b66917 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -1,7 +1,7 @@ /* * Dice.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,15 +57,14 @@ public final class Dice extends AbstractModule { } /** - * Rolls the dice. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { final SecureRandom r = new SecureRandom(); int i = r.nextInt(6) + 1; @@ -73,15 +72,15 @@ public final class Dice extends AbstractModule { final int playerTotal = i + y; bot.send(bot.getChannel(), - sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " - + Utils.bold(playerTotal)); + sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + + Utils.bold(playerTotal)); i = r.nextInt(6) + 1; y = r.nextInt(6) + 1; final int total = i + y; bot.action( - "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); + "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); if (playerTotal < total) { bot.action("wins."); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 32b685b..34a7918 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -1,7 +1,7 @@ /* * GoogleSearch.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -96,7 +96,7 @@ public final class GoogleSearch extends ThreadedModule { * Searches Google. */ @Override - void run(final Mobibot bot, final String sender, final String query) { + void run(final Mobibot bot, final String sender, final String cmd, final String query) { if (StringUtils.isNotBlank(query)) { try { final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 4c4c1ad..fef56c2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -1,7 +1,7 @@ /* * Joke.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -56,7 +56,7 @@ public final class Joke extends ThreadedModule { private static final String JOKE_CMD = "joke"; // The ICNDB URL. private static final String JOKE_URL = - "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; + "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; /** * Creates a new {@link Joke} instance. @@ -79,7 +79,7 @@ public final class Joke extends ThreadedModule { final StringBuilder sb = new StringBuilder(); try (final BufferedReader reader = - new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { sb.append(line); @@ -88,8 +88,8 @@ public final class Joke extends ThreadedModule { final JSONObject json = new JSONObject(sb.toString()); return new PublicMessage( - json.getJSONObject("value").get("joke").toString().replace("\\'", "'") - .replace("\\\"", "\"")); + json.getJSONObject("value").get("joke").toString().replace("\\'", "'") + .replace("\\\"", "\"")); } } catch (Exception e) { throw new ModuleException("randomJoke()", "An error has occurred retrieving a random joke.", e); @@ -100,15 +100,19 @@ public final class Joke extends ThreadedModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - new Thread(() -> run(bot, sender, args)).start(); + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { + new Thread(() -> run(bot, sender, cmd, args)).start(); } /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. */ @Override - void run(final Mobibot bot, final String sender, final String arg) { + void run(final Mobibot bot, final String sender, final String cmd, final String arg) { try { bot.send(Utils.cyan(randomJoke().getMessage())); } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index cdd8113..8c98c6a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -1,7 +1,7 @@ /* * Lookup.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -143,15 +143,14 @@ public final class Lookup extends AbstractModule { } /** - * Process a command. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { if (args.matches("(\\S.)+(\\S)+")) { try { bot.send(Lookup.lookup(args)); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 07254bd..214a1ed 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -1,7 +1,7 @@ /* * Ping.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,19 +50,19 @@ public class Ping extends AbstractModule { * The ping responses. */ static final List<String> PINGS = - Arrays.asList( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!"); + Arrays.asList( + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!"); /** * The ping command. */ @@ -80,7 +80,11 @@ public class Ping extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { final SecureRandom r = new SecureRandom(); bot.action(PINGS.get(r.nextInt(PINGS.size()))); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt new file mode 100644 index 0000000..387bc29 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -0,0 +1,116 @@ +/* + * RockPaperScissors.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import kotlin.random.Random + + + +/** + * Simple module example in Kotlin. + */ +class RockPaperScissors : AbstractModule() { + init { + with(commands) { + add(Shapes.ROCK.value) + add(Shapes.SCISSORS.value) + add(Shapes.PAPER.value) + } + } + + enum class Shapes(val value: String) { + ROCK("rock"), PAPER("paper"), SCISSORS("scissors") + } + + enum class Results { + WIN, LOSE, DRAW + } + + companion object { + /** + * Returns the the randomly picked shape and result. + */ + fun winLoseOrDraw(hand: Shapes): Pair<Shapes, Results> { + val botHand = Shapes.values()[Random.nextInt(0, Shapes.values().size)] + val result: Results + if (botHand == hand) { + result = Results.DRAW + } else { + when (botHand) { + Shapes.ROCK -> { + result = if (hand == Shapes.PAPER) { + Results.WIN + } else { + Results.LOSE + } + } + Shapes.PAPER -> { + result = if (hand == Shapes.ROCK) { + Results.LOSE + } else { + Results.WIN + } + } + Shapes.SCISSORS -> { + result = if (hand == Shapes.ROCK) { + Results.WIN + } else { + Results.LOSE + } + } + } + } + return Pair(botHand, result) + } + } + + override fun commandResponse(bot: Mobibot?, sender: String?, cmd: String?, args: String?, isPrivate: Boolean) { + val result = winLoseOrDraw(Shapes.valueOf(cmd!!.toUpperCase())) + val picked = "picked ${Utils.bold(result.first.value)}." + when (result.second) { + Results.WIN -> bot!!.action("$picked You win.") + Results.LOSE -> bot!!.action("$picked You lose.") + else -> bot!!.action("$picked We have a draw.") + } + } + + override fun helpResponse(bot: Mobibot?, sender: String?, args: String?, isPrivate: Boolean) { + bot!!.send(sender, "To play Rock Paper Scissors:") + bot.send( + sender, + bot.helpIndent("${bot.nick}: ${Shapes.ROCK.value} or ${Shapes.PAPER.value} or ${Shapes.SCISSORS.value}") + ) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index d9cc1c6..a7832fd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -1,7 +1,7 @@ /* * StockQuote.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -211,7 +211,7 @@ public final class StockQuote extends ThreadedModule { * Returns the specified stock quote from Alpha Advantage. */ @Override - void run(final Mobibot bot, final String sender, final String symbol) { + void run(final Mobibot bot, final String sender, final String cmd, final String symbol) { if (StringUtils.isNotBlank(symbol)) { try { final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java index 99917f7..7119950 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java @@ -1,7 +1,7 @@ /* * ThreadedModule.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,9 +46,13 @@ public abstract class ThreadedModule extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { if (isEnabled() && args.length() > 0) { - new Thread(() -> run(bot, sender, args)).start(); + new Thread(() -> run(bot, sender, cmd, args)).start(); } else { helpResponse(bot, sender, args, isPrivate); } @@ -57,5 +61,5 @@ public abstract class ThreadedModule extends AbstractModule { /** * Runs the thread. */ - abstract void run(Mobibot bot, String sender, String args); + abstract void run(Mobibot bot, String sender, @SuppressWarnings("unused") String cmd, String args); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index aaf6a1d..9b7d9c6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -1,7 +1,7 @@ /* * Twitter.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -148,7 +148,7 @@ public final class Twitter extends ThreadedModule { * Posts to twitter. */ @Override - void run(final Mobibot bot, final String sender, final String message) { + void run(final Mobibot bot, final String sender, final String cmd, final String message) { try { bot.send(sender, post(sender, message, false).getMessage()); } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 9267c0f..a438b1e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,7 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,9 +49,9 @@ public final class War extends AbstractModule { private static final String WAR_CMD = "war"; // The deck of card. private static final String[] WAR_DECK = - new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; + new String[]{ "Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2" }; // The suits for the deck of card. - private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; + private static final String[] WAR_SUITS = new String[]{ "Hearts", "Spades", "Diamonds", "Clubs" }; /** * The default constructor. @@ -62,15 +62,14 @@ public final class War extends AbstractModule { } /** - * Plays war. - * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { final SecureRandom r = new SecureRandom(); int i; @@ -81,7 +80,7 @@ public final class War extends AbstractModule { y = r.nextInt(WAR_DECK.length); bot.send(bot.getChannel(), - sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); if (i != y) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index aad3e42..52271ba 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -1,7 +1,7 @@ /* * Weather2.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -217,7 +217,7 @@ public class Weather2 extends ThreadedModule { * Fetches the weather data from a specific city. */ @Override - void run(final Mobibot bot, final String sender, final String args) { + void run(final Mobibot bot, final String sender, final String cmd, final String args) { if (StringUtils.isNotBlank(args)) { try { final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 6000a91..745c83a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -1,7 +1,7 @@ /* * WorldTime.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -146,9 +146,9 @@ public final class WorldTime extends AbstractModule { countries.put("INTERNET", BEATS_KEYWORD); countries.put("BEATS", BEATS_KEYWORD); - ZoneId.getAvailableZoneIds().stream().filter(tz -> - !tz.contains("/") && tz.length() == 3 && !countries.containsKey(tz)).forEach(tz -> - countries.put(tz, tz)); + ZoneId.getAvailableZoneIds().stream() + .filter(tz -> !tz.contains("/") && tz.length() == 3 && !countries.containsKey(tz)) + .forEach(tz -> countries.put(tz, tz)); COUNTRIES_MAP = Collections.unmodifiableMap(countries); } @@ -162,15 +162,58 @@ public final class WorldTime extends AbstractModule { } /** - * Responds with the current time in the specified timezone/country. + * Returns the current Internet (beat) Time. * - * @param bot The bot's instance. - * @param sender The sender. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + * @return The Internet Time string. + */ + private static String internetTime() { + final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); + final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) + + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); + return String.format("%c%03d", '@', beats); + } + + /** + * Returns the world time. + * + * <ul> + * <li>PST</li> + * <li>BEATS</li> + * </ul> + * + * @param query The query. + * @return The {@link Message} containing the world time. + */ + @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") + static Message worldTime(final String query) { + final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim() + .toUpperCase(Constants.LOCALE)))); + final String response; + + if (tz != null) { + if (BEATS_KEYWORD.equals(tz)) { + response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); + } else { + response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( + DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) + + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); + } + } else { + return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet()); + } + + return new PublicMessage(response); + } + + /** + * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void commandResponse(final Mobibot bot, + final String sender, + final String cmd, + final String args, + final boolean isPrivate) { final Message msg = worldTime(args); if (isPrivate) { @@ -203,48 +246,4 @@ public final class WorldTime extends AbstractModule { public boolean isPrivateMsgEnabled() { return true; } - - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - private static String internetTime() { - final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); - final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) - + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); - return String.format("%c%03d", '@', beats); - } - - /** - * Returns the world time. - * - * <ul> - * <li>PST</li> - * <li>BEATS</li> - * </ul> - * - * @param query The query. - * @return The {@link Message} containing the world time. - */ - @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") - static Message worldTime(final String query) { - final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim() - .toUpperCase(Constants.LOCALE)))); - final String response; - - if (tz != null) { - if (BEATS_KEYWORD.equals(tz)) { - response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); - } else { - response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( - DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) - + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); - } - } else { - return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet()); - } - - return new PublicMessage(response); - } } diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java index fd0650d..cc4ad92 100644 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java @@ -1,7 +1,7 @@ /* * EntryLinkTest.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java index fb87f9a..5eab364 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java @@ -1,7 +1,7 @@ /* * AbstractModuleTest.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt new file mode 100644 index 0000000..1df6908 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -0,0 +1,67 @@ +/* + * RockPaperScissorsTest.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.modules.RockPaperScissors.Results +import net.thauvin.erik.mobibot.modules.RockPaperScissors.Shapes +import org.assertj.core.api.Assertions.assertThat +import org.testng.annotations.Test + +class RockPaperScissorsTest { + @Test(invocationCount = 5) + fun testWinLoseOrDraw() { + var play = RockPaperScissors.winLoseOrDraw(Shapes.SCISSORS) + // println("SCISSORS vs ${play.first}: ${play.second}") + when (play.first) { + Shapes.SCISSORS -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.DRAW) + Shapes.ROCK -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.LOSE) + else -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.WIN) + } + + play = RockPaperScissors.winLoseOrDraw(Shapes.ROCK) + // println("ROCK vs ${play.first}: ${play.second}") + when (play.first) { + Shapes.SCISSORS -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.WIN) + Shapes.ROCK -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.DRAW) + else -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.LOSE) + } + + play = RockPaperScissors.winLoseOrDraw(Shapes.PAPER) + // println("PAPER vs ${play.first}: ${play.second}") + when (play.first) { + Shapes.SCISSORS -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.LOSE) + Shapes.ROCK -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.WIN) + else -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.DRAW) + } + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java b/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java index 28f0fec..04927a8 100644 --- a/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java @@ -1,7 +1,7 @@ /* * TellMessageTest.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/version.properties b/version.properties index adae677..0f65d20 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Mar 18 16:00:11 PDT 2020 -version.buildmeta=581 +#Mon Mar 23 05:21:18 PDT 2020 +version.buildmeta=682 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+581 +version.semver=0.7.3-beta+682 From fd6c1309e37b02e7f03c370167029062e46985c1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 23 Mar 2020 12:25:59 -0700 Subject: [PATCH 341/842] Adeed automatic posting of channel links to Twitter. --- properties/mobibot.properties | 3 ++ .../net/thauvin/erik/mobibot/Constants.java | 23 ++++++------ .../net/thauvin/erik/mobibot/Mobibot.java | 17 +++++++++ .../thauvin/erik/mobibot/modules/Twitter.java | 35 +++++++++---------- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 1932bd0..0a64786 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -37,6 +37,9 @@ tell-max-size=50 # Twitter handle to receive channel join notifications #twitter-handle= +# Automatically post links to twitter +#twitter-auto-post=true + # # Create custom search engine at: https://cse.google.com/ # and get API key from: https://console.developers.google.com/ diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index 4160517..af65e31 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -46,21 +46,22 @@ public final class Constants { * The connect/read timeout in ms. */ public static final int CONNECT_TIMEOUT = 5000; - - /** - * The empty title string. - */ - public static final String NO_TITLE = "No Title"; - - /** - * The Twitter handle property key. - */ - public static final String TWITTER_HANDLE_PROP = "twitter-handle"; - /** * Default locale. */ public static final Locale LOCALE = Locale.getDefault(); + /** + * The empty title string. + */ + public static final String NO_TITLE = "No Title"; + /** + * The Twitter handle property key. + */ + public static final String TWITTER_HANDLE_PROP = "twitter-handle"; + /** + * The twitter post flag property key. + */ + public static final String TWITTER_AUTOPOST_PROP = "twitter-auto-post"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 97cf66e..f0b275f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -176,6 +176,8 @@ public class Mobibot extends PircBot { // The tell object. private final Tell tell; // The Twitter handle for channel join notifications. + // Automatically post links to Twitter + private final boolean twitterAutoPost; private final String twitterHandle; // The Twitter module. private final Twitter twitterModule; @@ -284,6 +286,7 @@ public class Mobibot extends PircBot { twitterModule = new Twitter(); MODULES.add(twitterModule); twitterHandle = p.getProperty(Constants.TWITTER_HANDLE_PROP, ""); + twitterAutoPost = Boolean.parseBoolean(p.getProperty(Constants.TWITTER_AUTOPOST_PROP, "false")); MODULES.add(new War()); MODULES.add(new Weather2()); @@ -970,6 +973,20 @@ public class Mobibot extends PircBot { pinboard.addPost(entry); } + // Post link to twitter + if (twitterAutoPost && twitterModule.isEnabled()) { + final String msg = title + ' ' + link + " via " + sender + " on " + getChannel(); + new Thread(() -> { + try { + twitterModule.post(twitterHandle, msg, false); + } catch (ModuleException e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to post link on twitter.", e); + } + } + }).start(); + } + saveEntries(isBackup); if (Constants.NO_TITLE.equals(entry.getTitle())) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 9b7d9c6..1b7af6e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -48,12 +48,12 @@ import twitter4j.conf.ConfigurationBuilder; * @since 1.0 */ public final class Twitter extends ThreadedModule { - // The property keys. + // Property keys static final String CONSUMER_KEY_PROP = "twitter-consumerKey"; static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; static final String TOKEN_PROP = "twitter-token"; static final String TOKEN_SECRET_PROP = "twitter-tokenSecret"; - // The twitter command. + // Twitter command private static final String TWITTER_CMD = "twitter"; /** @@ -90,19 +90,16 @@ public final class Twitter extends ThreadedModule { final boolean isDm) throws ModuleException { try { final ConfigurationBuilder cb = new ConfigurationBuilder(); - cb.setDebugEnabled(true) - .setOAuthConsumerKey(consumerKey) - .setOAuthConsumerSecret(consumerSecret) - .setOAuthAccessToken(token) - .setOAuthAccessTokenSecret(tokenSecret); + cb.setDebugEnabled(true).setOAuthConsumerKey(consumerKey).setOAuthConsumerSecret(consumerSecret) + .setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret); final TwitterFactory tf = new TwitterFactory(cb.build()); final twitter4j.Twitter twitter = tf.getInstance(); if (!isDm) { - final Status status = twitter.updateStatus(message + " (" + handle + ')'); - return new NoticeMessage("You message was posted to http://twitter.com/" + twitter.getScreenName() - + "/statuses/" + status.getId()); + final Status status = twitter.updateStatus(message); + return new NoticeMessage("You message was posted to https://twitter.com/" + twitter.getScreenName() + + "/statuses/" + status.getId()); } else { final DirectMessage dm = twitter.sendDirectMessage(handle, message); return new NoticeMessage(dm.getText()); @@ -134,14 +131,15 @@ public final class Twitter extends ThreadedModule { * @return The {@link Message} to send back. * @throws ModuleException If an error occurs while posting. */ - public Message post(final String handle, final String message, final boolean isDm) throws ModuleException { + public Message post(final String handle, final String message, final boolean isDm) + throws ModuleException { return twitterPost(properties.get(CONSUMER_KEY_PROP), - properties.get(CONSUMER_SECRET_PROP), - properties.get(TOKEN_PROP), - properties.get(TOKEN_SECRET_PROP), - handle, - message, - isDm); + properties.get(CONSUMER_SECRET_PROP), + properties.get(TOKEN_PROP), + properties.get(TOKEN_SECRET_PROP), + handle, + message, + isDm); } /** @@ -150,7 +148,8 @@ public final class Twitter extends ThreadedModule { @Override void run(final Mobibot bot, final String sender, final String cmd, final String message) { try { - bot.send(sender, post(sender, message, false).getMessage()); + bot.send(sender, + post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMessage()); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage()); From c078a51ebf6bd3540240e56df0a963f044133d8c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 23 Mar 2020 13:17:18 -0700 Subject: [PATCH 342/842] Cleanup, copyright, etc. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../net/thauvin/erik/mobibot/FeedReader.java | 10 +- .../net/thauvin/erik/mobibot/Mobibot.java | 105 ++++++++++-------- .../erik/mobibot/entries/EntriesMgr.java | 4 +- .../erik/mobibot/entries/EntryComment.java | 8 +- .../erik/mobibot/entries/EntryLink.java | 22 ++-- .../thauvin/erik/mobibot/modules/Calc.java | 2 +- .../mobibot/modules/CurrencyConverter.java | 10 +- .../thauvin/erik/mobibot/modules/Dice.java | 2 +- .../erik/mobibot/modules/GoogleSearch.java | 8 +- .../thauvin/erik/mobibot/modules/Joke.java | 4 +- .../thauvin/erik/mobibot/modules/Lookup.java | 2 +- .../erik/mobibot/modules/StockQuote.java | 10 +- .../net/thauvin/erik/mobibot/modules/War.java | 6 +- .../erik/mobibot/modules/Weather2.java | 2 +- .../erik/mobibot/modules/WorldTime.java | 4 +- .../net/thauvin/erik/mobibot/msg/Message.java | 4 +- .../net/thauvin/erik/mobibot/tell/Tell.java | 22 ++-- .../erik/mobibot/tell/TellMessagesMgr.java | 4 +- .../mobibot/modules/GoogleSearchTest.java | 4 +- .../mobibot/modules/RockPaperScissorsTest.kt | 6 +- .../erik/mobibot/modules/StockQuoteTest.java | 4 +- version.properties | 38 ++++++- 26 files changed, 167 insertions(+), 126 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index b51eeda..e4d0451 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+660" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+682" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 81ea0b8..1208121 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+660" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+682" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index e065203..720e36a 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+660" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+682" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 4cf0184..6511d10 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1584966079848L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1584991174842L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "682"; - public static final String VERSION = "0.7.3-beta+682"; + public static final String BUILDMETA = "704"; + public static final String VERSION = "0.7.3-beta+704"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index c73a8be..6f52e3a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -49,19 +49,19 @@ import java.util.List; * @since 1.0 */ class FeedReader implements Runnable { - // The maximum number of feed items to display. + // Maximum number of feed items to display private static final int MAX_ITEMS = 5; - // The tab indent (4 spaces). + // Tab indent (4 spaces) private static final String TAB_INDENT = " "; - // The bot. + // Bot private final Mobibot bot; - // The nick of the person who sent the message. + // Nick of the person who sent the message private final String sender; - // The URL to fetch. + // URL to fetch private final String url; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index f0b275f..edc8fea 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -108,40 +108,40 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; className = "ReleaseInfo") public class Mobibot extends PircBot { - // The default port. + // Default port private static final int DEFAULT_PORT = 6667; - // The default server. + // Default server private static final String DEFAULT_SERVER = "irc.freenode.net"; - // The info strings. + // Info strings @SuppressWarnings("indentation") private static final String[] INFO_STRS = { ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", "https://www.mobitopia.org/mobibot/" }; - // The link match string. + // Link match string private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - // The default maximum number of entries to display. + // Default maximum number of entries to display private static final int MAX_ENTRIES = 8; - // The default maximum recap entries. + // Default maximum recap entries private static final int MAX_RECAP = 10; - // The maximum number of times the bot will try to reconnect, if disconnected. + // Maximum number of times the bot will try to reconnect, if disconnected private static final int MAX_RECONNECT = 10; - // The number of milliseconds to delay between consecutive messages. + // Number of milliseconds to delay between consecutive messages private static final long MESSAGE_DELAY = 1000L; - // The modules. + // Modules private static final List<AbstractModule> MODULES = new ArrayList<>(0); - // The tags/categories marker. + // Tags/categories marker private static final String TAGS_MARKER = "tags:"; - /* The version strings.*/ + // Version strings @SuppressWarnings("indentation") private static final String[] VERSION_STRS = { "Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', @@ -151,53 +151,53 @@ public class Mobibot extends PircBot { "java.runtime.version") + ')', "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " + System.getProperty("java.vm.info") + ')' }; - // The logger. + // Logger private static final Logger logger = LogManager.getLogger(Mobibot.class); - // The commands list. + // Commands list private final List<String> commandsList = new ArrayList<>(); - // The entries array. + // Entries array private final List<EntryLink> entries = new ArrayList<>(0); - // The history/backlogs array. + // History/backlogs array private final List<String> history = new ArrayList<>(0); - // The ignored nicks array. + // Ignored nicks array private final Set<String> ignoredNicks = new HashSet<>(0); - // The main channel. + // Main channel private final String ircChannel; - // The IRC port. + // IRC port private final int ircPort; - // The IRC server. + // IRC server private final String ircServer; - // The logger default level. + // Logger default level private final Level loggerLevel; - // The log directory. + // Log directory private final String logsDir; - // The recap array. + // Recap array private final List<String> recap = new ArrayList<>(0); - // The tell object. + // Tell object private final Tell tell; - // The Twitter handle for channel join notifications. // Automatically post links to Twitter private final boolean twitterAutoPost; + // Twitter handle for channel join notifications private final String twitterHandle; - // The Twitter module. + // Twitter module private final Twitter twitterModule; - // The backlogs URL. + // Backlogs URL private String backLogsUrl = ""; - // The default tags/categories. + // Default tags/categories private String defaultTags = ""; - // The feed URL. + // Feed URL private String feedUrl = ""; - // The ident message. + // Ident message private String identMsg = ""; - // The ident nick. + // Ident nick private String identNick = ""; - // The NickServ ident password. + // NickServ ident password private String identPwd = ""; - // The pinboard posts handler. + // Pinboard posts handler private Pinboard pinboard; - // Today's date. + // Today's date private String today = Utils.today(); - // The weblog URL. + // Weblog URL private String weblogUrl = ""; /** @@ -225,7 +225,7 @@ public class Mobibot extends PircBot { // Set the logger level loggerLevel = logger.getLevel(); - // Load the current entries, if any. + // Load the current entries, if any try { today = EntriesMgr.loadEntries(logsDir + EntriesMgr.CURRENT_XML, ircChannel, entries); @@ -238,18 +238,18 @@ public class Mobibot extends PircBot { today = Utils.today(); } } catch (IOException ignore) { - // Do nothing. + // Do nothing } catch (FeedException e) { if (logger.isErrorEnabled()) { logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); } } - // Load the backlogs, if any. + // Load the backlogs, if any try { EntriesMgr.loadBacklogs(logsDir + EntriesMgr.NAV_XML, history); } catch (IOException ignore) { - // Do nothing. + // Do nothing } catch (FeedException e) { if (logger.isErrorEnabled()) { logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); @@ -421,7 +421,7 @@ public class Mobibot extends PircBot { try { Thread.sleep(secs * 1000L); } catch (InterruptedException ignore) { - // Do nothing. + // Do nothing } } @@ -956,7 +956,12 @@ public class Mobibot extends PircBot { final String htmlTitle = html.title(); if (isNotBlank(htmlTitle)) { - title = htmlTitle; + final String[] split = htmlTitle.split("( \\| )", 2); + if (split.length == 2) { + title = split[0]; + } else { + title = htmlTitle; + } } } catch (IOException ignore) { // Do nothing @@ -969,6 +974,7 @@ public class Mobibot extends PircBot { final EntryLink entry = entries.get(index); send(channel, EntriesUtils.buildLink(index, entry)); + // Add link to pinboard if (pinboard != null) { pinboard.addPost(entry); } @@ -1038,7 +1044,7 @@ public class Mobibot extends PircBot { } } } - } else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { // L1:<comment>, L1:-, L1:|<title>, etc. + } else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { // L1:<comment>, L1:-, L1:|<title>, etc isCommand = true; final String[] cmds = message.substring(1).split(":", 2); @@ -1165,7 +1171,7 @@ public class Mobibot extends PircBot { } } } - } else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { // L1.1:<command> + } else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { // L11:<command> isCommand = true; final String[] cmds = message.substring(1).split("[.:]", 3); @@ -1178,15 +1184,15 @@ public class Mobibot extends PircBot { if (cindex < entry.getCommentsCount()) { final String cmd = cmds[2].trim(); - // L1.1: + // L11: if (cmd.length() == 0) { final EntryComment comment = entry.getComment(cindex); send(channel, EntriesUtils.buildComment(index, cindex, comment)); - } else if ("-".equals(cmd)) { // L1.1:- + } else if ("-".equals(cmd)) { // L11:- entry.deleteComment(cindex); send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); saveEntries(false); - } else if (cmd.charAt(0) == '?') { // L1.1:?<author> + } else if (cmd.charAt(0) == '?') { // L11:?<author> if (isOp(sender)) { if (cmd.length() > 1) { final EntryComment comment = entry.getComment(cindex); @@ -1258,7 +1264,7 @@ public class Mobibot extends PircBot { } else if (Commands.USERS_CMD.equals(cmd)) { usersResponse(sender, true); } else if ((cmds.length > 1) && isOp(sender) && Commands.ADDLOG_CMD.equals(cmd)) { - // e.g. 2014-04-01 + // e.g 2014-04-01 final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); if (backlog.exists()) { history.add(0, args); @@ -1557,7 +1563,10 @@ public class Mobibot extends PircBot { if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { new Thread(() -> { try { - twitterModule.post(twitterHandle, getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true); + twitterModule.post( + twitterHandle, + getName() + ' ' + ReleaseInfo.VERSION + " " + msg, + true); } catch (ModuleException e) { if (logger.isWarnEnabled()) { logger.warn("Failed to notify @{}: {}", twitterHandle, msg, e); @@ -1647,7 +1656,7 @@ public class Mobibot extends PircBot { i = 0; } } catch (NumberFormatException ignore) { - // Do nothing. + // Do nothing } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index 3c450b3..d1ba70b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -1,7 +1,7 @@ /* * EntriesMgr.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,7 +81,7 @@ public final class EntriesMgr { */ public static final String XML_EXT = ".xml"; - // The maximum number of backlogs to keep. + // Maximum number of backlogs to keep private static final int MAX_BACKLOGS = 10; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java index cc0b244..1661350 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java @@ -1,7 +1,7 @@ /* * EntryComment.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,10 +44,10 @@ import java.time.LocalDateTime; */ @SuppressWarnings({"PMD.DataClass"}) public class EntryComment implements Serializable { - // The serial version UID. + // Serial version UID static final long serialVersionUID = 1L; - // The creation date. + // Creation date private final LocalDateTime date = LocalDateTime.now(); private String comment = ""; @@ -69,7 +69,7 @@ public class EntryComment implements Serializable { */ @SuppressWarnings("UnusedDeclaration") protected EntryComment() { - // Required for serialization. + // Required for serialization } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 05e19bb..0365ae4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -1,7 +1,7 @@ /* * EntryLink.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,31 +51,31 @@ import java.util.concurrent.CopyOnWriteArrayList; * @since 1.0 */ public class EntryLink implements Serializable { - // The serial version UID. + // Serial version UID static final long serialVersionUID = 1L; - // The link's comments + // Link's comments private final List<EntryComment> comments = new CopyOnWriteArrayList<>(); - // The tags/categories + // Tags/categories private final List<SyndCategory> tags = new CopyOnWriteArrayList<>(); - // The channel + // Channel private String channel; - // The creation date + // Creation date private Date date = Calendar.getInstance().getTime(); - // The link's URL + // Link's URL private String link; - // The author's login + // Author's login private String login = ""; - // The author's nickname + // Author's nickname private String nick; - // The link's title + // Link's title private String title; /** @@ -313,7 +313,7 @@ public class EntryLink implements Serializable { mod = part.charAt(0); if (mod == '-') { - // Don't remove the channel tag, if any. + // Don't remove the channel tag, if any if (!channel.substring(1).equals(tag.getName())) { this.tags.remove(tag); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 05fb963..6d4a304 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -47,7 +47,7 @@ import java.text.DecimalFormat; * @since 1.0 */ public class Calc extends AbstractModule { - // The Calc command. + // Calc command private static final String CALC_CMD = "calc"; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index a65beaa..a43f241 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -70,13 +70,13 @@ public final class CurrencyConverter extends ThreadedModule { */ static final String CURRENCY_RATES_KEYWORD = "rates"; - // The currency command. + // Currency command private static final String CURRENCY_CMD = "currency"; - // The exchange rates. + // Exchange rates private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); - // The exchange rates table URL. + // Exchange rates table URL private static final String EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"; - // The last exchange rates table publication date. + // Last exchange rates table publication date private static String pubDate = ""; /** @@ -100,7 +100,7 @@ public final class CurrencyConverter extends ThreadedModule { if (EXCHANGE_RATES.isEmpty()) { try { final SAXBuilder builder = new SAXBuilder(); - // See https://rules.sonarsource.com/java/tag/owasp/RSPEC-2755 + // See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755 builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); builder.setIgnoringElementContentWhitespace(true); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 4b66917..62b750c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -45,7 +45,7 @@ import java.security.SecureRandom; * @since 1.0 */ public final class Dice extends AbstractModule { - // The dice command. + // Dice command private static final String DICE_CMD = "dice"; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 34a7918..e180fb5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -60,13 +60,13 @@ import java.util.List; * @since 1.0 */ public final class GoogleSearch extends ThreadedModule { - // The Google API Key property. + // Google API Key property static final String GOOGLE_API_KEY_PROP = "google-api-key"; - // The Google Custom Search Engine ID property. + // Google Custom Search Engine ID property static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; - // The Google command + // Google command private static final String GOOGLE_CMD = "google"; - // The tab indent (4 spaces). + // Tab indent (4 spaces) private static final String TAB_INDENT = " "; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index fef56c2..2210baf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -52,9 +52,9 @@ import java.nio.charset.StandardCharsets; * @since 1.0 */ public final class Joke extends ThreadedModule { - // The joke command. + // Joke command private static final String JOKE_CMD = "joke"; - // The ICNDB URL. + // ICNDB URL private static final String JOKE_URL = "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 8c98c6a..a3b808f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -53,7 +53,7 @@ public final class Lookup extends AbstractModule { */ static final String WHOIS_HOST = "whois.arin.net"; - // The lookup command. + // Lookup command private static final String LOOKUP_CMD = "lookup"; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index a7832fd..42c5057 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -68,9 +68,9 @@ public final class StockQuote extends ThreadedModule { * The Invalid Symbol error string. */ static final String INVALID_SYMBOL = "Invalid symbol."; - // The Alpha Advantage URL. + // Alpha Advantage URL private static final String ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function="; - // The quote command. + // Quote command private static final String STOCK_CMD = "stock"; /** @@ -95,7 +95,7 @@ public final class StockQuote extends ThreadedModule { throw new ModuleException(debugMessage, Utils.unescapeXml(info)); } } catch (JSONException ignore) { - // Do nothing. + // Do nothing } try { @@ -104,7 +104,7 @@ public final class StockQuote extends ThreadedModule { throw new ModuleException(debugMessage, Utils.unescapeXml(error)); } } catch (JSONException ignore) { - // Do nothing. + // Do nothing } try { @@ -113,7 +113,7 @@ public final class StockQuote extends ThreadedModule { throw new ModuleException(debugMessage, Utils.unescapeXml(error)); } } catch (JSONException ignore) { - // Do nothing. + // Do nothing } return json; diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index a438b1e..115e6ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -45,12 +45,12 @@ import java.security.SecureRandom; * @since 1.0 */ public final class War extends AbstractModule { - // The war command + // War command private static final String WAR_CMD = "war"; - // The deck of card. + // Deck of card private static final String[] WAR_DECK = new String[]{ "Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2" }; - // The suits for the deck of card. + // Suits for the deck of card private static final String[] WAR_SUITS = new String[]{ "Hearts", "Spades", "Diamonds", "Clubs" }; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 52271ba..5f1a376 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -65,7 +65,7 @@ public class Weather2 extends ThreadedModule { */ static final String OWM_API_KEY_PROP = "owm-api-key"; - // The weather command. + // Weather command private static final String WEATHER_CMD = "weather"; /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 745c83a..7bbc09d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -56,9 +56,9 @@ import java.util.TreeMap; */ @SuppressWarnings("PMD.UseConcurrentHashMap") public final class WorldTime extends AbstractModule { - // The beats (Internet Time) keyword. + // Beats (Internet Time) keyword private static final String BEATS_KEYWORD = ".beats"; - // The supported countries. + // Supported countries private static final Map<String, String> COUNTRIES_MAP; diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java index 9ee7af2..cc70c2a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java @@ -1,7 +1,7 @@ /* * Message.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -52,7 +52,7 @@ public class Message { * Creates a new message. */ public Message() { - // This constructor is intentionally empty. + // This constructor is intentionally empty } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java index 17f70d2..9692505 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java @@ -1,7 +1,7 @@ /* * Tell.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,26 +54,26 @@ public class Tell { */ public static final String TELL_CMD = "tell"; - // The default maximum number of days to keep messages. + // Default maximum number of days to keep messages private static final int DEFAULT_TELL_MAX_DAYS = 7; - // The default message max queue size. + // Default message max queue size private static final int DEFAULT_TELL_MAX_SIZE = 50; - // The serialized object file extension. + // Serialized object file extension private static final String SER_EXT = ".ser"; - // The all keyword. + // All keyword private static final String TELL_ALL_KEYWORD = "all"; //T he delete command. private static final String TELL_DEL_KEYWORD = "del"; - // The bot instance. + // Bot instance private final Mobibot bot; - // The maximum number of days to keep messages. + // Maximum number of days to keep messages private final int maxDays; - // The message maximum queue size. + // Message maximum queue size private final int maxSize; - // The messages queue. + // Messages queue private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); - // The serialized object file. + // Serialized object file private final String serializedObject; /** @@ -88,7 +88,7 @@ public class Tell { this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS); this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE); - // Load the message queue. + // Load the message queue serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT; messages.addAll(TellMessagesMgr.load(serializedObject, bot.getLogger())); diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java index a445815..ad6cb0a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java @@ -1,7 +1,7 @@ /* * TellMessagesMgr.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -99,7 +99,7 @@ final class TellMessagesMgr { return ((List<TellMessage>) input.readObject()); } } catch (FileNotFoundException ignore) { - // Do nothing. + // Do nothing } catch (IOException e) { logger.error("An IO error occurred loading the messages queue.", e); } catch (Exception e) { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 0ba872f..13388bd 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -1,7 +1,7 @@ /* * GoogleSearchTest.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -78,7 +78,7 @@ public class GoogleSearchTest extends LocalProperties { assertThatThrownBy(() -> GoogleSearch.searchGoogle("", "apikey", "apiKey")).as("no query").isInstanceOf( ModuleException.class).hasNoCause(); } catch (ModuleException e) { - // Avoid displaying api keys in CI logs. + // Avoid displaying api keys in CI logs if ("true".equals(System.getenv("CI"))) { throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey, cseKey)); } else { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 1df6908..eca4bef 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -41,7 +41,7 @@ class RockPaperScissorsTest { @Test(invocationCount = 5) fun testWinLoseOrDraw() { var play = RockPaperScissors.winLoseOrDraw(Shapes.SCISSORS) - // println("SCISSORS vs ${play.first}: ${play.second}") + // println("SCISSORS vs ${play.first}: ${playsecond}") when (play.first) { Shapes.SCISSORS -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.DRAW) Shapes.ROCK -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.LOSE) @@ -49,7 +49,7 @@ class RockPaperScissorsTest { } play = RockPaperScissors.winLoseOrDraw(Shapes.ROCK) - // println("ROCK vs ${play.first}: ${play.second}") + // println("ROCK vs ${play.first}: ${playsecond}") when (play.first) { Shapes.SCISSORS -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.WIN) Shapes.ROCK -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.DRAW) @@ -57,7 +57,7 @@ class RockPaperScissorsTest { } play = RockPaperScissors.winLoseOrDraw(Shapes.PAPER) - // println("PAPER vs ${play.first}: ${play.second}") + // println("PAPER vs ${play.first}: ${playsecond}") when (play.first) { Shapes.SCISSORS -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.LOSE) Shapes.ROCK -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.WIN) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index f1e60c0..b3a4971 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -1,7 +1,7 @@ /* * StockQuoteTest.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,7 +73,7 @@ public class StockQuoteTest extends LocalProperties { ModuleException.class).hasNoCause(); } catch (ModuleException e) { - // Avoid displaying api keys in CI logs. + // Avoid displaying api keys in CI logs if ("true".equals(System.getenv("CI"))) { throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey)); } else { diff --git a/version.properties b/version.properties index 0f65d20..a8206a8 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,41 @@ +# +# version.properties +# +# Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of this project nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + #Generated by the Semver Plugin for Gradle -#Mon Mar 23 05:21:18 PDT 2020 -version.buildmeta=682 +#Mon Mar 23 12:19:33 PDT 2020 +version.buildmeta=704 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+682 +version.semver=0.7.3-beta+704 From 2f50c6c1c3bb49beda7cbf2d6143fdbff9b21b2a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 23 Mar 2020 22:51:01 -0700 Subject: [PATCH 343/842] Reworked RockPaperScissor modules with actions and colors. --- .idea/compiler.xml | 9 +++ .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 22 +++---- .idea/modules/mobibot.test.iml | 22 +++---- build.gradle | 6 +- gradle/wrapper/gradle-wrapper.properties | 32 ---------- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../java/net/thauvin/erik/mobibot/Utils.java | 10 +++ .../erik/mobibot/modules/RockPaperScissors.kt | 62 +++++++++++-------- version.properties | 38 +----------- 10 files changed, 86 insertions(+), 123 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 0ffd2d5..bec3928 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -11,6 +11,15 @@ </processorPath> <module name="mobibot.main" /> </profile> + <profile name="Gradle Imported" enabled="true"> + <outputRelativeToContentRoot value="true" /> + <option name="semver.project.dir" value="K:\java\mobibot" /> + <processorPath useClasspath="false"> + <entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" /> + <entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" /> + </processorPath> + <module name="mobibot.main" /> + </profile> </annotationProcessing> <bytecodeTargetLevel> <module name="mobibot" target="14" /> diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index e4d0451..328850f 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+682" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+708" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 1208121..9a0a8a9 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+682" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+708" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.70/c706d9a12aa043400daacbb15b61ba662a1eb9a9/kotlin-stdlib-jdk8-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.70/95aee922bc2a0fff3b7bfa1b6503ff0afe38264a/kotlin-stdlib-jdk7-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -19,11 +19,11 @@ </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.70/28a3923786ef1a651ebea81ff0f211dfd7436070/kotlin-script-runtime-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.70/74a96e3d496437e9b9bb02433f997b7e8868b36e/kotlin-scripting-common-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.70/b2af86b5d6380cbf6a0eb6d15fec26cd48b20869/kotlin-scripting-jvm-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.71/7f5522e7a9d1736fabfdb4335630f64504ce8f20/kotlin-script-runtime-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.71/80b8eb0986d1391e6a1dcf9aba968d59165dc4f/kotlin-scripting-common-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.71/ee1ba4f199415255ea6ea55cc5dc41856b2729fd/kotlin-scripting-jvm-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> @@ -65,18 +65,18 @@ <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.70" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.71" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.71" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.3" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 720e36a..d94873f 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+682" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+708" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.70/c706d9a12aa043400daacbb15b61ba662a1eb9a9/kotlin-stdlib-jdk8-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.70/95aee922bc2a0fff3b7bfa1b6503ff0afe38264a/kotlin-stdlib-jdk7-1.3.70.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -25,11 +25,11 @@ </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.70/28a3923786ef1a651ebea81ff0f211dfd7436070/kotlin-script-runtime-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.70/74a96e3d496437e9b9bb02433f997b7e8868b36e/kotlin-scripting-common-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.70/b2af86b5d6380cbf6a0eb6d15fec26cd48b20869/kotlin-scripting-jvm-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.70/e5d97e25bb5b30dcfc022ec1c8f3959a875257fb/kotlin-stdlib-1.3.70.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.71/7f5522e7a9d1736fabfdb4335630f64504ce8f20/kotlin-script-runtime-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.71/80b8eb0986d1391e6a1dcf9aba968d59165dc4f/kotlin-scripting-common-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.71/ee1ba4f199415255ea6ea55cc5dc41856b2729fd/kotlin-scripting-jvm-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> @@ -66,13 +66,13 @@ <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.70" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.71" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.71" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> @@ -85,7 +85,7 @@ <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.71" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> diff --git a/build.gradle b/build.gradle index 46d3c27..08a3b86 100644 --- a/build.gradle +++ b/build.gradle @@ -4,11 +4,11 @@ plugins { id 'com.github.ben-manes.versions' version '0.28.0' id 'com.github.spotbugs' version '4.0.4' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.7.0-beta2' + id 'io.gitlab.arturbosch.detekt' version '1.7.0' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.3.70' + id 'org.jetbrains.kotlin.jvm' version '1.3.71' id 'org.sonarqube' version '2.8' id 'pmd' } @@ -25,7 +25,7 @@ mainClassName = packageName + '.Mobibot' ext { versions = [ - kotlin : '1.3.70', + kotlin : '1.3.71', log4j : '2.13.1', spotbugs : '4.0.1' ] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5f7d5c4..b133221 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,35 +1,3 @@ -# -# gradle-wrapper.properties -# -# Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# Neither the name of this project nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-rc-3-bin.zip diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 6511d10..6906800 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1584991174842L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1585027973338L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "704"; - public static final String VERSION = "0.7.3-beta+704"; + public static final String BUILDMETA = "712"; + public static final String VERSION = "0.7.3-beta+712"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 1049078..1489ecf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -208,6 +208,16 @@ public final class Utils { } } + /** + * Makes the given string red. + * + * @param s The string. + * @return The red string. + */ + public static String red(final String s) { + return colorize(s, Colors.RED); + } + /** * Makes the given string reverse color. * diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 387bc29..a6418db 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -37,7 +37,6 @@ import net.thauvin.erik.mobibot.Utils import kotlin.random.Random - /** * Simple module example in Kotlin. */ @@ -58,51 +57,60 @@ class RockPaperScissors : AbstractModule() { WIN, LOSE, DRAW } + companion object { /** - * Returns the the randomly picked shape and result. + * Returns the the randomly picked shape, result and action (cuts, crushes, covers, vs.) */ - fun winLoseOrDraw(hand: Shapes): Pair<Shapes, Results> { + fun winLoseOrDraw(hand: Shapes): Triple<Shapes, Results, String> { val botHand = Shapes.values()[Random.nextInt(0, Shapes.values().size)] val result: Results + val action: String if (botHand == hand) { result = Results.DRAW + action = "vs." } else { - when (botHand) { - Shapes.ROCK -> { - result = if (hand == Shapes.PAPER) { - Results.WIN - } else { - Results.LOSE - } + val shapes = arrayOf(hand, botHand) + if (shapes.contains(Shapes.ROCK) && shapes.contains(Shapes.SCISSORS)) { + action = "crushes" + result = if (hand == Shapes.ROCK) { + Results.WIN + } else { + Results.LOSE } - Shapes.PAPER -> { - result = if (hand == Shapes.ROCK) { - Results.LOSE - } else { - Results.WIN - } + } else if (shapes.contains(Shapes.PAPER) && shapes.contains(Shapes.ROCK)) { + action = "covers" + result = if (hand == Shapes.PAPER) { + Results.WIN + } else { + Results.LOSE } - Shapes.SCISSORS -> { - result = if (hand == Shapes.ROCK) { - Results.WIN - } else { - Results.LOSE - } + } else { // SCISSORS vs. PAPER + action = "cuts" + result = if (hand == Shapes.SCISSORS) { + Results.WIN + } else { + Results.LOSE } } } - return Pair(botHand, result) + return Triple(botHand, result, action) } } override fun commandResponse(bot: Mobibot?, sender: String?, cmd: String?, args: String?, isPrivate: Boolean) { val result = winLoseOrDraw(Shapes.valueOf(cmd!!.toUpperCase())) - val picked = "picked ${Utils.bold(result.first.value)}." when (result.second) { - Results.WIN -> bot!!.action("$picked You win.") - Results.LOSE -> bot!!.action("$picked You lose.") - else -> bot!!.action("$picked We have a draw.") + Results.WIN -> bot!!.action( + "${Utils.green(cmd)} ${Utils.bold(result.third)} ${Utils.red(result.first.value)} ~ You win ~" + ) + Results.LOSE -> bot!!.action( + "${Utils.green(result.first.value)} ${Utils.bold(result.third)} ${Utils.red(cmd)} ~ You lose ~" + ) + else -> bot!!.action( + "${Utils.green(cmd)} ${Utils.bold(result.third)} ${Utils.green(result.first.value)}" + + " ~ The game is tied ~" + ) } } diff --git a/version.properties b/version.properties index a8206a8..baa7520 100644 --- a/version.properties +++ b/version.properties @@ -1,41 +1,9 @@ -# -# version.properties -# -# Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# Neither the name of this project nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - #Generated by the Semver Plugin for Gradle -#Mon Mar 23 12:19:33 PDT 2020 -version.buildmeta=704 +#Mon Mar 23 22:32:51 PDT 2020 +version.buildmeta=712 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+704 +version.semver=0.7.3-beta+712 From a381b06a0ace5735ee826f7509c43b027f41134a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 23 Mar 2020 23:22:57 -0700 Subject: [PATCH 344/842] Added command to list enabled modules (op). --- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 ++--- .../net/thauvin/erik/mobibot/Commands.java | 5 +++++ .../net/thauvin/erik/mobibot/Mobibot.java | 22 +++++++++++++++++-- version.properties | 6 ++--- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 6906800..c9b839b 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1585027973338L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1585029881513L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "712"; - public static final String VERSION = "0.7.3-beta+712"; + public static final String BUILDMETA = "714"; + public static final String VERSION = "0.7.3-beta+714"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java index 1518e3c..7bcdf43 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ b/src/main/java/net/thauvin/erik/mobibot/Commands.java @@ -100,6 +100,10 @@ public final class Commands { * The me command. */ static final String ME_CMD = "me"; + /** + * The modules command. + */ + static final String MODULES_CMD = "modules"; /** * The msg command. */ @@ -133,6 +137,7 @@ public final class Commands { */ static final String VERSION_CMD = "version"; + /** * Disables the default constructor. * diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index edc8fea..50f7b38 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -749,8 +749,17 @@ public class Mobibot extends PircBot { if (isOp(sender)) { send(sender, "The op commands are:"); send(sender, - helpIndent(Commands.CYCLE_CMD + " " + Commands.ME_CMD + " " + Commands.MSG_CMD + " " - + Commands.SAY_CMD + " " + Commands.VERSION_CMD)); + helpIndent(Commands.CYCLE_CMD + + " " + + Commands.ME_CMD + + " " + + Commands.MODULES_CMD + + " " + + Commands.MSG_CMD + + " " + + Commands.SAY_CMD + + " " + + Commands.VERSION_CMD)); } } } @@ -1278,6 +1287,15 @@ public class Mobibot extends PircBot { } else { helpResponse(sender, Commands.ME_CMD); } + } else if (Commands.MODULES_CMD.equals(cmd) && isOp(sender)) { + if (MODULES.isEmpty()) { + send(sender, "There are not enabled modules.", true); + } else { + send(sender, "The enabled modules are:"); + for (final AbstractModule mod : MODULES) { + send(sender, helpIndent(mod.getClass().getSimpleName())); + } + } } else if ((cmds.length > 1) && isOp(sender) && Commands.NICK_CMD.equals(cmd)) { changeNick(args); } else if (Commands.SAY_CMD.equals(cmd) && isOp(sender)) { diff --git a/version.properties b/version.properties index baa7520..aa42206 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Mar 23 22:32:51 PDT 2020 -version.buildmeta=712 +#Mon Mar 23 23:04:40 PDT 2020 +version.buildmeta=714 version.major=0 version.minor=7 version.patch=3 version.prerelease=beta version.project=mobibot -version.semver=0.7.3-beta+712 +version.semver=0.7.3-beta+714 From 5bf6901ce20a30b4649e5315f2e5ee10cdbdf181 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 24 Mar 2020 14:55:32 -0700 Subject: [PATCH 345/842] Reworked using enum class. --- .../erik/mobibot/modules/RockPaperScissors.kt | 112 ++++++++---------- .../mobibot/modules/RockPaperScissorsTest.kt | 42 +++---- 2 files changed, 68 insertions(+), 86 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index a6418db..903bf90 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -43,82 +43,74 @@ import kotlin.random.Random class RockPaperScissors : AbstractModule() { init { with(commands) { - add(Shapes.ROCK.value) - add(Shapes.SCISSORS.value) - add(Shapes.PAPER.value) + add(Hands.ROCK.name.toLowerCase()) + add(Hands.PAPER.name.toLowerCase()) + add(Hands.SCISSORS.name.toLowerCase()) } } - enum class Shapes(val value: String) { - ROCK("rock"), PAPER("paper"), SCISSORS("scissors") - } + enum class Hands(val action: String) { + ROCK("crushes") { + override fun beats(hand: Hands): Boolean { + return hand == SCISSORS + } + }, + PAPER("covers") { + override fun beats(hand: Hands): Boolean { + return hand == ROCK + } + }, + SCISSORS("cuts") { + override fun beats(hand: Hands): Boolean { + return hand == PAPER + } + }; - enum class Results { - WIN, LOSE, DRAW + abstract fun beats(hand: Hands): Boolean } - companion object { - /** - * Returns the the randomly picked shape, result and action (cuts, crushes, covers, vs.) - */ - fun winLoseOrDraw(hand: Shapes): Triple<Shapes, Results, String> { - val botHand = Shapes.values()[Random.nextInt(0, Shapes.values().size)] - val result: Results - val action: String - if (botHand == hand) { - result = Results.DRAW - action = "vs." - } else { - val shapes = arrayOf(hand, botHand) - if (shapes.contains(Shapes.ROCK) && shapes.contains(Shapes.SCISSORS)) { - action = "crushes" - result = if (hand == Shapes.ROCK) { - Results.WIN - } else { - Results.LOSE - } - } else if (shapes.contains(Shapes.PAPER) && shapes.contains(Shapes.ROCK)) { - action = "covers" - result = if (hand == Shapes.PAPER) { - Results.WIN - } else { - Results.LOSE - } - } else { // SCISSORS vs. PAPER - action = "cuts" - result = if (hand == Shapes.SCISSORS) { - Results.WIN - } else { - Results.LOSE - } - } + // For testing. + fun winLoseOrDraw(player: String, bot: String ): String { + val hand = Hands.valueOf(player.toUpperCase()) + val botHand = Hands.valueOf(bot.toUpperCase()) + + return when { + hand == botHand -> "draw" + hand.beats(botHand) -> "win" + else -> "lose" } - return Triple(botHand, result, action) } } - override fun commandResponse(bot: Mobibot?, sender: String?, cmd: String?, args: String?, isPrivate: Boolean) { - val result = winLoseOrDraw(Shapes.valueOf(cmd!!.toUpperCase())) - when (result.second) { - Results.WIN -> bot!!.action( - "${Utils.green(cmd)} ${Utils.bold(result.third)} ${Utils.red(result.first.value)} ~ You win ~" - ) - Results.LOSE -> bot!!.action( - "${Utils.green(result.first.value)} ${Utils.bold(result.third)} ${Utils.red(cmd)} ~ You lose ~" - ) - else -> bot!!.action( - "${Utils.green(cmd)} ${Utils.bold(result.third)} ${Utils.green(result.first.value)}" - + " ~ The game is tied ~" - ) + override fun commandResponse(bot: Mobibot, sender: String, cmd: String, args: String?, isPrivate: Boolean) { + val hand = Hands.valueOf(cmd.toUpperCase()) + val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] + when { + hand == botHand -> { + bot.action("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)} ~ The game is tied ~") + } + hand.beats(botHand) -> { + bot.action( + "${Utils.green(hand.name)} ${Utils.bold(hand.action)} ${Utils.red(botHand.name)} ~ You win ~" + ) + } + else -> { + bot.action( + "${Utils.green(botHand.name)} ${Utils.bold(botHand.action)} ${Utils.red(botHand.name)} ~ You lose ~" + ) + } } } - override fun helpResponse(bot: Mobibot?, sender: String?, args: String?, isPrivate: Boolean) { - bot!!.send(sender, "To play Rock Paper Scissors:") + override fun helpResponse(bot: Mobibot, sender: String, args: String?, isPrivate: Boolean) { + bot.send(sender, "To play Rock Paper Scissors:") bot.send( sender, - bot.helpIndent("${bot.nick}: ${Shapes.ROCK.value} or ${Shapes.PAPER.value} or ${Shapes.SCISSORS.value}") + bot.helpIndent( + "${bot.nick}: ${Hands.ROCK.name.toLowerCase()} or ${Hands.PAPER.name.toLowerCase()}" + + " or ${Hands.SCISSORS.name.toLowerCase()}" + ) ) } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index eca4bef..6ca4a84 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -32,36 +32,26 @@ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.modules.RockPaperScissors.Results -import net.thauvin.erik.mobibot.modules.RockPaperScissors.Shapes + import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test class RockPaperScissorsTest { - @Test(invocationCount = 5) + @Test fun testWinLoseOrDraw() { - var play = RockPaperScissors.winLoseOrDraw(Shapes.SCISSORS) - // println("SCISSORS vs ${play.first}: ${playsecond}") - when (play.first) { - Shapes.SCISSORS -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.DRAW) - Shapes.ROCK -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.LOSE) - else -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.WIN) - } - - play = RockPaperScissors.winLoseOrDraw(Shapes.ROCK) - // println("ROCK vs ${play.first}: ${playsecond}") - when (play.first) { - Shapes.SCISSORS -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.WIN) - Shapes.ROCK -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.DRAW) - else -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.LOSE) - } - - play = RockPaperScissors.winLoseOrDraw(Shapes.PAPER) - // println("PAPER vs ${play.first}: ${playsecond}") - when (play.first) { - Shapes.SCISSORS -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.LOSE) - Shapes.ROCK -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.WIN) - else -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.DRAW) - } + assertThat( + RockPaperScissors.winLoseOrDraw("scissors", "paper")).`as`("scissors vs. paper").isEqualTo("win") + assertThat( + RockPaperScissors.winLoseOrDraw("paper", "rock")).`as`("paper vs. rock").isEqualTo("win") + assertThat( + RockPaperScissors.winLoseOrDraw("rock", "scissors")).`as`("rock vs. scissors").isEqualTo("win") + assertThat( + RockPaperScissors.winLoseOrDraw("paper", "scissors")).`as`("paper vs. scissors").isEqualTo("lose") + assertThat( + RockPaperScissors.winLoseOrDraw("rock", "paper")).`as`("rock vs. paper").isEqualTo("lose") + assertThat( + RockPaperScissors.winLoseOrDraw("scissors", "rock")).`as`("scissors vs. rock").isEqualTo("lose") + assertThat( + RockPaperScissors.winLoseOrDraw("scissors", "scissors")).`as`("scissors vs. scissors").isEqualTo("draw") } } From 72961dadc9c9da955d0aa032682498cf3d042a2d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 24 Mar 2020 20:22:50 -0700 Subject: [PATCH 346/842] Added twitter timer to allow for links editing, etc. --- .../net/thauvin/erik/mobibot/Constants.java | 8 +- .../net/thauvin/erik/mobibot/Mobibot.java | 86 ++++++++++++++----- .../net/thauvin/erik/mobibot/TwitterTimer.kt | 41 +++++++++ 3 files changed, 112 insertions(+), 23 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index af65e31..dd2cbaa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -55,13 +55,17 @@ public final class Constants { */ public static final String NO_TITLE = "No Title"; /** - * The Twitter handle property key. + * The timer delay in minutes. */ - public static final String TWITTER_HANDLE_PROP = "twitter-handle"; + public static final long TIMER_DELAY = 10L; /** * The twitter post flag property key. */ public static final String TWITTER_AUTOPOST_PROP = "twitter-auto-post"; + /** + * The Twitter handle property key. + */ + public static final String TWITTER_HANDLE_PROP = "twitter-handle"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 50f7b38..925906b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -92,6 +92,7 @@ import java.util.List; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; +import java.util.Timer; import static net.thauvin.erik.mobibot.Utils.bold; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -140,7 +141,6 @@ public class Mobibot extends PircBot { // Tags/categories marker private static final String TAGS_MARKER = "tags:"; - // Version strings @SuppressWarnings("indentation") private static final String[] VERSION_STRS = @@ -153,6 +153,8 @@ public class Mobibot extends PircBot { + System.getProperty("java.vm.info") + ')' }; // Logger private static final Logger logger = LogManager.getLogger(Mobibot.class); + // Timer + private static final Timer timer = new Timer(true); // Commands list private final List<String> commandsList = new ArrayList<>(); // Entries array @@ -175,6 +177,8 @@ public class Mobibot extends PircBot { private final List<String> recap = new ArrayList<>(0); // Tell object private final Tell tell; + // The Twitter auto-posts list. + private final List<Integer> twitterAutoLinks = Collections.synchronizedList(new ArrayList<>()); // Automatically post links to Twitter private final boolean twitterAutoPost; // Twitter handle for channel join notifications @@ -318,11 +322,9 @@ public class Mobibot extends PircBot { * @param args The command line arguments. */ @SuppressFBWarnings( - { - "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", - "DM_DEFAULT_ENCODING", - "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" - }) + { "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", + "DM_DEFAULT_ENCODING", + "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" }) @SuppressWarnings({ "PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource" }) public static void main(final String[] args) { // Setup the command line options @@ -851,6 +853,10 @@ public class Mobibot extends PircBot { info.append(", Messages: ").append(tell.size()); } + if (twitterAutoPost && twitterModule.isEnabled()) { + info.append(", Twitter: ").append(twitterAutoLinks.size()); + } + info.append(", Recap: ").append(recap.size()).append(']'); send(sender, info.toString(), isPrivate); @@ -988,18 +994,10 @@ public class Mobibot extends PircBot { pinboard.addPost(entry); } - // Post link to twitter + // Queue link for posting to twitter if (twitterAutoPost && twitterModule.isEnabled()) { - final String msg = title + ' ' + link + " via " + sender + " on " + getChannel(); - new Thread(() -> { - try { - twitterModule.post(twitterHandle, msg, false); - } catch (ModuleException e) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to post link on twitter.", e); - } - } - }).start(); + twitterAutoLinks.add(index); + timer.schedule(new TwitterTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L); } saveEntries(isBackup); @@ -1088,9 +1086,15 @@ public class Mobibot extends PircBot { pinboard.deletePost(entry); } + if (twitterAutoPost && twitterModule.isEnabled()) { + twitterAutoLinks.remove(index); + } + entries.remove(index); send(channel, "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); saveEntries(false); + + } else { send(sender, "Please ask a channel op to remove this entry for you."); } @@ -1257,9 +1261,11 @@ public class Mobibot extends PircBot { System.exit(0); } else if (Commands.DIE_CMD.equals(cmd) && isOp(sender)) { send(ircChannel, sender + " has just signed my death sentence."); + timer.cancel(); + twitterShutdown(); twitterNotification("killed by " + sender + " on " + ircChannel); saveEntries(true); - sleep(3); + sleep(10); quitServer("The Bot Is Out There!"); System.exit(0); } else if (Commands.CYCLE_CMD.equals(cmd)) { @@ -1291,7 +1297,7 @@ public class Mobibot extends PircBot { if (MODULES.isEmpty()) { send(sender, "There are not enabled modules.", true); } else { - send(sender, "The enabled modules are:"); + send(sender, "The enabled modules are: "); for (final AbstractModule mod : MODULES) { send(sender, helpIndent(mod.getClass().getSimpleName())); } @@ -1564,14 +1570,39 @@ public class Mobibot extends PircBot { * @param isAction Set to <code>true</code> if the message is an action. */ private void storeRecap(final String sender, final String message, final boolean isAction) { - recap.add(Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + " -> " + sender + (isAction ? " " : ": ") - + message); + recap.add( + Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + " -> " + sender + (isAction ? " " : ": ") + + message); if (recap.size() > MAX_RECAP) { recap.remove(0); } } + /** + * Auto-post to twitter. + * + * @param index The post entry index. + */ + final void twitterAutoPost(final int index) { + if (twitterAutoPost && twitterModule.isEnabled() + && twitterAutoLinks.contains(index) && entries.size() >= index) { + final EntryLink entry = entries.get(index); + final String msg = + entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + getChannel(); + new Thread(() -> { + try { + twitterModule.post(twitterHandle, msg, false); + } catch (ModuleException e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to post link on twitter.", e); + } + } + }).start(); + twitterAutoLinks.remove((Object) index); + } + } + /** * Send a notification to the registered Twitter handle. * @@ -1594,6 +1625,19 @@ public class Mobibot extends PircBot { } } + /** + * Post all the links on twitter on shutdown. + */ + final void twitterShutdown() { + if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { + synchronized (twitterAutoLinks) { + for (final int i : twitterAutoLinks) { + twitterAutoPost(i); + } + } + } + } + /** * Responds with the users on a channel. * diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt new file mode 100644 index 0000000..f3a5450 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt @@ -0,0 +1,41 @@ +/* + * TwitterTimer.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import java.util.* + +class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() { + override fun run() { + bot.twitterAutoPost(index) + } +} From 68ddcd1ccc326cb1b0e4d8ac861ec73c85bd1f33 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 25 Mar 2020 00:19:23 -0700 Subject: [PATCH 347/842] Added tags keywords. --- properties/mobibot.properties | 1 + .../net/thauvin/erik/mobibot/Mobibot.java | 67 ++++++++++--------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 0a64786..4043eeb 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -12,6 +12,7 @@ ident=changepwd logs=./logs ignore=chanserv,nickserv tags=mobile mobitopia +tags-keywords=android ios apple google weblog=http://www.mobitopia.org/ feed=http://www.mobitopia.org/rss.xml diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 925906b..9a379da 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -111,34 +111,25 @@ public class Mobibot extends PircBot { // Default port private static final int DEFAULT_PORT = 6667; - // Default server private static final String DEFAULT_SERVER = "irc.freenode.net"; - // Info strings @SuppressWarnings("indentation") private static final String[] INFO_STRS = { ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", "https://www.mobitopia.org/mobibot/" }; - // Link match string private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - // Default maximum number of entries to display private static final int MAX_ENTRIES = 8; - // Default maximum recap entries private static final int MAX_RECAP = 10; - // Maximum number of times the bot will try to reconnect, if disconnected private static final int MAX_RECONNECT = 10; - // Number of milliseconds to delay between consecutive messages private static final long MESSAGE_DELAY = 1000L; - // Modules private static final List<AbstractModule> MODULES = new ArrayList<>(0); - // Tags/categories marker private static final String TAGS_MARKER = "tags:"; // Version strings @@ -175,10 +166,12 @@ public class Mobibot extends PircBot { private final String logsDir; // Recap array private final List<String> recap = new ArrayList<>(0); + // Tags keywords matches + private final List<String> tagsKeywords = new ArrayList<>(); // Tell object private final Tell tell; // The Twitter auto-posts list. - private final List<Integer> twitterAutoLinks = Collections.synchronizedList(new ArrayList<>()); + private final List<Integer> twitterAutoLinks = new ArrayList<>(); // Automatically post links to Twitter private final boolean twitterAutoPost; // Twitter handle for channel join notifications @@ -308,6 +301,7 @@ public class Mobibot extends PircBot { // Set the tags setTags(p.getProperty("tags", "")); + setTagsKeywords(p.getProperty("tags-keywords", "")); // Set the ignored nicks setIgnoredNicks(p.getProperty("ignore", "")); @@ -895,7 +889,6 @@ public class Mobibot extends PircBot { */ public final void joinChannel() { joinChannel(ircChannel); - twitterNotification("has joined " + ircChannel); } @@ -983,6 +976,14 @@ public class Mobibot extends PircBot { } } + if (!tagsKeywords.isEmpty()) { + for (final String match : tagsKeywords) { + if (title.matches("(?i).*\\b" + match.trim() + "\\b.*")) { + tags.append(' ').append(match.trim()); + } + } + } + entries.add(new EntryLink(link, title, sender, login, channel, tags.toString())); final int index = entries.size() - 1; @@ -1023,7 +1024,6 @@ public class Mobibot extends PircBot { args = cmds[1].trim(); } - if (cmd.startsWith(Commands.HELP_CMD)) { // mobibot: help helpResponse(sender, args); } else if (Commands.RECAP_CMD.equals(cmd)) { // mobibot: recap @@ -1093,8 +1093,6 @@ public class Mobibot extends PircBot { entries.remove(index); send(channel, "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); saveEntries(false); - - } else { send(sender, "Please ask a channel op to remove this entry for you."); } @@ -1265,7 +1263,7 @@ public class Mobibot extends PircBot { twitterShutdown(); twitterNotification("killed by " + sender + " on " + ircChannel); saveEntries(true); - sleep(10); + sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); } else if (Commands.CYCLE_CMD.equals(cmd)) { @@ -1337,7 +1335,6 @@ public class Mobibot extends PircBot { Configurator.setLevel(logger.getName(), Level.DEBUG); } - send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); } else { for (final AbstractModule module : MODULES) { @@ -1553,6 +1550,19 @@ public class Mobibot extends PircBot { defaultTags = tags; } + /** + * Sets the tags keywords matches. + * + * @param matches The tags keywords. + */ + final void setTagsKeywords(final String matches) { + if (isNotBlank(matches)) { + tagsKeywords.addAll(Arrays.asList(matches.split(", +?| +"))); + } else { + tagsKeywords.clear(); + } + } + /** * Sets the weblog URL. * @@ -1630,10 +1640,8 @@ public class Mobibot extends PircBot { */ final void twitterShutdown() { if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { - synchronized (twitterAutoLinks) { - for (final int i : twitterAutoLinks) { - twitterAutoPost(i); - } + for (final int i : twitterAutoLinks) { + twitterAutoPost(i); } } } @@ -1649,22 +1657,17 @@ public class Mobibot extends PircBot { final String[] nicks = new String[users.length]; for (int i = 0; i < users.length; i++) { - nicks[i] = users[i].getNick(); + final String nick = users[i].getNick(); + if (isOp(nick)) { + nicks[i] = '@' + users[i].getNick(); + } else { + nicks[i] = users[i].getNick(); + } } Arrays.sort(nicks, String.CASE_INSENSITIVE_ORDER); - final StringBuilder buff = new StringBuilder(0); - - for (final String nick : nicks) { - if (isOp(nick)) { - buff.append('@'); - } - - buff.append(nick).append(' '); - } - - send(sender, buff.toString(), isPrivate); + send(sender, String.join(" ", nicks), isPrivate); } /** From a435f0be4954c68c4cfc727d1a506d28e5801303 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 25 Mar 2020 00:20:15 -0700 Subject: [PATCH 348/842] Cleanup. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 4 ++-- build.gradle | 8 +++++--- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 328850f..1b43317 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+708" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 9a0a8a9..294ae5d 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+708" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index d94873f..75edcff 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+708" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> diff --git a/build.gradle b/build.gradle index 08a3b86..50b3de9 100644 --- a/build.gradle +++ b/build.gradle @@ -13,8 +13,6 @@ plugins { id 'pmd' } -import com.github.spotbugs.snom.SpotBugsTask - defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' @@ -88,7 +86,7 @@ spotbugs { toolVersion.set("$versions.spotbugs") } -tasks.withType(SpotBugsTask) { +tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { reports { xml.enabled = false html.enabled = true @@ -108,6 +106,10 @@ detekt { baseline = file("detekt-baseline.xml") } +tasks.withType(io.gitlab.arturbosch.detekt.Detekt) { + exclude("**/**.java") +} + tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index c9b839b..0653d4b 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1585029881513L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1585119580379L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 7; public static final int PATCH = 3; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "714"; - public static final String VERSION = "0.7.3-beta+714"; + public static final String BUILDMETA = "760"; + public static final String VERSION = "0.7.3-beta+760"; /** * Disables the default constructor. From e0d191980895dd857257483616665581a8bc55a7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 25 Mar 2020 00:40:15 -0700 Subject: [PATCH 349/842] Fixed redundant method call. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 9a379da..67549a6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -978,8 +978,9 @@ public class Mobibot extends PircBot { if (!tagsKeywords.isEmpty()) { for (final String match : tagsKeywords) { - if (title.matches("(?i).*\\b" + match.trim() + "\\b.*")) { - tags.append(' ').append(match.trim()); + final String m = match.trim(); + if (title.matches("(?i).*\\b" + m + "\\b.*")) { + tags.append(' ').append(m); } } } From 0d913eca4d8ec20e1d95697581c8f58bafb019fc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 25 Mar 2020 00:48:45 -0700 Subject: [PATCH 350/842] Only show twitter count to ops. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 67549a6..3db3c42 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -843,12 +843,14 @@ public class Mobibot extends PircBot { info.append("Uptime: ").append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())).append( " [Entries: ").append(entries.size()); - if (tell.isEnabled() && isOp(sender)) { - info.append(", Messages: ").append(tell.size()); - } + if (isOp(sender)) { + if (tell.isEnabled()) { + info.append(", Messages: ").append(tell.size()); + } - if (twitterAutoPost && twitterModule.isEnabled()) { - info.append(", Twitter: ").append(twitterAutoLinks.size()); + if (twitterAutoPost && twitterModule.isEnabled()) { + info.append(", Twitter: ").append(twitterAutoLinks.size()); + } } info.append(", Recap: ").append(recap.size()).append(']'); From 4b95b0bbe27a0e3416036bde6e65c92b8ed36702 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 11:31:52 -0700 Subject: [PATCH 351/842] Version 0.8 starts. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 4 +- build.gradle | 4 +- config/spotbugs/excludeFilter.xml | 7 +- detekt-baseline.xml | 52 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 12 +- .../net/thauvin/erik/mobibot/Commands.java | 149 -- .../net/thauvin/erik/mobibot/Constants.java | 38 +- .../net/thauvin/erik/mobibot/Mobibot.java | 1268 +++++------------ .../net/thauvin/erik/mobibot/TwitterTimer.kt | 2 +- .../java/net/thauvin/erik/mobibot/Utils.java | 10 + .../erik/mobibot/commands/AbstractCommand.kt | 67 + .../thauvin/erik/mobibot/commands/Cycle.kt | 68 + .../thauvin/erik/mobibot/commands/Ignore.kt | 111 ++ .../net/thauvin/erik/mobibot/commands/Info.kt | 92 ++ .../net/thauvin/erik/mobibot/commands/Me.kt | 62 + .../erik/mobibot/commands/MobibotVersion.kt | 64 + .../thauvin/erik/mobibot/commands/Modules.kt | 68 + .../net/thauvin/erik/mobibot/commands/Msg.kt | 67 + .../net/thauvin/erik/mobibot/commands/Nick.kt | 62 + .../thauvin/erik/mobibot/commands/Recap.kt | 95 ++ .../net/thauvin/erik/mobibot/commands/Say.kt | 63 + .../thauvin/erik/mobibot/commands/Users.kt | 71 + .../erik/mobibot/commands/links/Comment.kt | 130 ++ .../erik/mobibot/commands/links/Posting.kt | 147 ++ .../erik/mobibot/commands/links/Tags.kt | 92 ++ .../erik/mobibot/commands/links/UrlMgr.kt | 243 ++++ .../erik/mobibot/commands/links/View.kt | 130 ++ .../mobibot/{ => commands}/tell/Tell.java | 104 +- .../{ => commands}/tell/TellMessage.java | 2 +- .../{ => commands}/tell/TellMessagesMgr.java | 2 +- .../erik/mobibot/entries/EntriesUtils.java | 11 +- .../erik/mobibot/modules/AbstractModule.java | 2 +- .../thauvin/erik/mobibot/modules/Calc.java | 8 +- .../mobibot/modules/CurrencyConverter.java | 14 +- .../thauvin/erik/mobibot/modules/Dice.java | 12 +- .../erik/mobibot/modules/GoogleSearch.java | 128 +- .../thauvin/erik/mobibot/modules/Joke.java | 6 +- .../thauvin/erik/mobibot/modules/Lookup.java | 7 +- .../thauvin/erik/mobibot/modules/Ping.java | 7 +- .../erik/mobibot/modules/RockPaperScissors.kt | 17 +- .../erik/mobibot/modules/StockQuote.java | 34 +- .../thauvin/erik/mobibot/modules/Twitter.java | 7 +- .../net/thauvin/erik/mobibot/modules/War.java | 12 +- .../erik/mobibot/modules/Weather2.java | 22 +- .../erik/mobibot/modules/WorldTime.java | 11 +- .../{ => commands}/tell/TellMessageTest.java | 2 +- version.properties | 12 +- 49 files changed, 2284 insertions(+), 1318 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/Commands.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Info.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Me.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Say.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Users.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt rename src/main/java/net/thauvin/erik/mobibot/{ => commands}/tell/Tell.java (75%) rename src/main/java/net/thauvin/erik/mobibot/{ => commands}/tell/TellMessage.java (99%) rename src/main/java/net/thauvin/erik/mobibot/{ => commands}/tell/TellMessagesMgr.java (99%) rename src/test/java/net/thauvin/erik/mobibot/{ => commands}/tell/TellMessageTest.java (98%) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 1b43317..7b66ece 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 294ae5d..49a5e9b 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 75edcff..b92cc97 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> diff --git a/build.gradle b/build.gradle index 50b3de9..d9102a2 100644 --- a/build.gradle +++ b/build.gradle @@ -88,7 +88,7 @@ spotbugs { tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { reports { - xml.enabled = false + xml.enabled = true html.enabled = true } excludeFilter.set(file("$projectDir/config/spotbugs/excludeFilter.xml")) @@ -140,7 +140,7 @@ clean { } run { - args '--v' + //args '--v' } incrementBuildMeta { diff --git a/config/spotbugs/excludeFilter.xml b/config/spotbugs/excludeFilter.xml index 3d88834..49bdd81 100644 --- a/config/spotbugs/excludeFilter.xml +++ b/config/spotbugs/excludeFilter.xml @@ -6,7 +6,7 @@ <Match> <Or> <Package name="net.thauvin.erik.mobibot.*"/> - <Package name="net.thauvin.erik.mobibot.tell.*"/> + <Package name="net.thauvin.erik.mobibot.commands.tell.*"/> <Package name="net.thauvin.erik.mobibot.entries.*"/> </Or> <Or> @@ -21,7 +21,7 @@ <Class name="net.thauvin.erik.mobibot.Mobibot"/> <Class name="net.thauvin.erik.mobibot.Pinboard"/> <Class name="net.thauvin.erik.mobibot.FeedReader"/> - <Class name="net.thauvin.erik.mobibot.tell.Tell"/> + <Class name="net.thauvin.erik.mobibot.commands.tell.Tell"/> <Package name="net.thauvin.erik.mobibot.modules.*"/> <Package name="net.thauvin.erik.mobibot.entries.*"/> </Or> @@ -33,4 +33,7 @@ <Bug pattern="PATH_TRAVERSAL_OUT"/> <Confidence value="1"/> </Match> + <Match> + <Source name="~.*\.kt"/> + </Match> </FindBugsFilter> diff --git a/detekt-baseline.xml b/detekt-baseline.xml index fee5cbc..2f5da4b 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -1,37 +1,23 @@ <?xml version="1.0" ?> -<!-- - ~ detekt-baseline.xml - ~ - ~ Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - ~ All rights reserved. - ~ - ~ Redistribution and use in source and binary forms, with or without - ~ modification, are permitted provided that the following conditions are met: - ~ - ~ Redistributions of source code must retain the above copyright notice, this - ~ list of conditions and the following disclaimer. - ~ - ~ Redistributions in binary form must reproduce the above copyright notice, - ~ this list of conditions and the following disclaimer in the documentation - ~ and/or other materials provided with the distribution. - ~ - ~ Neither the name of this project nor the names of its contributors may be - ~ used to endorse or promote products derived from this software without - ~ specific prior written permission. - ~ - ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - ~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - ~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - ~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - ~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - ~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - ~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - --> - <SmellBaseline> <Blacklist></Blacklist> - <Whitelist></Whitelist> + <Whitelist> + <ID>ComplexMethod:Links.kt$Links$commandResponse</ID> + <ID>ComplexMethod:UrlMgr.kt$UrlMgr$commandResponse</ID> + <ID>ComplexMethod:View.kt$View$commandResponse</ID> + <ID>LongMethod:Links.kt$Links$commandResponse</ID> + <ID>LongMethod:UrlMgr.kt$UrlMgr$commandResponse</ID> + <ID>LongMethod:View.kt$View$commandResponse</ID> + <ID>LongParameterList:AbstractCommand.kt$AbstractCommand$( bot: Mobibot, sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + <ID>LoopWithTooManyJumpStatements:View.kt$View$while (i < max) { entry = getEntry(i) if (lcArgs.isNotEmpty()) { if (entry.link.toLowerCase().contains(lcArgs) || entry.title.toLowerCase().contains(lcArgs) || entry.nick.toLowerCase().contains(lcArgs)) { if (sent > maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } } else { if (sent > maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } i++ }</ID> + <ID>MagicNumber:Comments.kt$Comments$3</ID> + <ID>MagicNumber:Cycle.kt$Cycle$10</ID> + <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> + <ID>MagicNumber:View.kt$View$8</ID> + <ID>NestedBlockDepth:Comments.kt$Comments$commandResponse</ID> + <ID>NestedBlockDepth:Ignore.kt$Ignore$commandResponse</ID> + <ID>NestedBlockDepth:Links.kt$Links$commandResponse</ID> + <ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID> + <ID>NestedBlockDepth:View.kt$View$commandResponse</ID> + </Whitelist> </SmellBaseline> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 0653d4b..3421c5d 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1585119580379L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1585416159853L), ZoneId.systemDefault()); public static final int MAJOR = 0; - public static final int MINOR = 7; - public static final int PATCH = 3; - public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "760"; - public static final String VERSION = "0.7.3-beta+760"; + public static final int MINOR = 8; + public static final int PATCH = 0; + public static final String PRERELEASE = "alpha"; + public static final String BUILDMETA = "045"; + public static final String VERSION = "0.8.0-alpha+045"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Commands.java b/src/main/java/net/thauvin/erik/mobibot/Commands.java deleted file mode 100644 index 7bcdf43..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/Commands.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Commands.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot; - -/** - * The <code>commands</code>, <code>keywords</code> and <code>arguments</code>. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-26 - * @since 1.0 - */ -public final class Commands { - /** - * The link command. - */ - public static final String LINK_CMD = "L"; - /** - * The view command. - */ - public static final String VIEW_CMD = "view"; - /** - * The add (back)log command. - */ - static final String ADDLOG_CMD = "addlog"; - /** - * The cycle command. - */ - static final String CYCLE_CMD = "cycle"; - /** - * Debug command line argument. - */ - static final String DEBUG_ARG = "debug"; - /** - * The debug command. - */ - static final String DEBUG_CMD = "debug"; - /** - * The die command. - */ - static final String DIE_CMD = "die"; - /** - * Help command line argument. - */ - static final String HELP_ARG = "help"; - /** - * The help command. - */ - static final String HELP_CMD = "help"; - /** - * The help on posting keyword. - */ - static final String HELP_POSTING_KEYWORD = "posting"; - /** - * The help on tags keyword. - */ - static final String HELP_TAGS_KEYWORD = "tags"; - /** - * The ignore command. - */ - static final String IGNORE_CMD = "ignore"; - /** - * The ignore <code>me</code> keyword. - */ - static final String IGNORE_ME_KEYWORD = "me"; - /** - * The info command. - */ - static final String INFO_CMD = "info"; - /** - * The me command. - */ - static final String ME_CMD = "me"; - /** - * The modules command. - */ - static final String MODULES_CMD = "modules"; - /** - * The msg command. - */ - static final String MSG_CMD = "msg"; - /** - * The nick command. - */ - static final String NICK_CMD = "nick"; - /** - * Properties command line argument. - */ - static final String PROPS_ARG = "properties"; - /** - * The recap command. - */ - static final String RECAP_CMD = "recap"; - /** - * The say command. - */ - static final String SAY_CMD = "say"; - /** - * The users command. - */ - static final String USERS_CMD = "users"; - /** - * Properties version line argument. - */ - static final String VERSION_ARG = "version"; - /** - * The version command. - */ - static final String VERSION_CMD = "version"; - - - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private Commands() { - throw new UnsupportedOperationException("Illegal constructor call."); - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index dd2cbaa..411c688 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -1,7 +1,7 @@ /* * Constants.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,10 +42,38 @@ import java.util.Locale; * @since 1.0 */ public final class Constants { + /** + * The add (back)log command. + */ + public static final String ADDLOG_CMD = "addlog"; /** * The connect/read timeout in ms. */ public static final int CONNECT_TIMEOUT = 5000; + /** + * Debug command line argument. + */ + public static final String DEBUG_ARG = "debug"; + /** + * The debug command. + */ + public static final String DEBUG_CMD = "debug"; + /** + * The die command. + */ + public static final String DIE_CMD = "die"; + /** + * Help command line argument. + */ + public static final String HELP_ARG = "help"; + /** + * The help command. + */ + public static final String HELP_CMD = "help"; + /** + * The link command. + */ + public static final String LINK_CMD = "L"; /** * Default locale. */ @@ -54,6 +82,10 @@ public final class Constants { * The empty title string. */ public static final String NO_TITLE = "No Title"; + /** + * Properties command line argument. + */ + public static final String PROPS_ARG = "properties"; /** * The timer delay in minutes. */ @@ -66,6 +98,10 @@ public final class Constants { * The Twitter handle property key. */ public static final String TWITTER_HANDLE_PROP = "twitter-handle"; + /** + * Properties version line argument. + */ + public static final String VERSION_ARG = "version"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 3db3c42..e695d4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -32,11 +32,26 @@ package net.thauvin.erik.mobibot; -import com.rometools.rome.io.FeedException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.commands.AbstractCommand; +import net.thauvin.erik.mobibot.commands.Cycle; +import net.thauvin.erik.mobibot.commands.Ignore; +import net.thauvin.erik.mobibot.commands.Info; +import net.thauvin.erik.mobibot.commands.Me; +import net.thauvin.erik.mobibot.commands.MobibotVersion; +import net.thauvin.erik.mobibot.commands.Modules; +import net.thauvin.erik.mobibot.commands.Msg; +import net.thauvin.erik.mobibot.commands.Nick; +import net.thauvin.erik.mobibot.commands.Recap; +import net.thauvin.erik.mobibot.commands.Say; +import net.thauvin.erik.mobibot.commands.Users; +import net.thauvin.erik.mobibot.commands.links.Comment; +import net.thauvin.erik.mobibot.commands.links.Posting; +import net.thauvin.erik.mobibot.commands.links.Tags; +import net.thauvin.erik.mobibot.commands.links.UrlMgr; +import net.thauvin.erik.mobibot.commands.links.View; +import net.thauvin.erik.mobibot.commands.tell.Tell; import net.thauvin.erik.mobibot.entries.EntriesMgr; -import net.thauvin.erik.mobibot.entries.EntriesUtils; -import net.thauvin.erik.mobibot.entries.EntryComment; import net.thauvin.erik.mobibot.entries.EntryLink; import net.thauvin.erik.mobibot.modules.AbstractModule; import net.thauvin.erik.mobibot.modules.Calc; @@ -54,7 +69,6 @@ import net.thauvin.erik.mobibot.modules.War; import net.thauvin.erik.mobibot.modules.Weather2; import net.thauvin.erik.mobibot.modules.WorldTime; import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.tell.Tell; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -63,15 +77,13 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; -import org.jibble.pircbot.Colors; import org.jibble.pircbot.PircBot; import org.jibble.pircbot.User; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; import java.io.File; import java.io.FileNotFoundException; @@ -79,22 +91,16 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; -import java.lang.management.ManagementFactory; import java.nio.file.Files; import java.nio.file.Paths; -import java.time.Clock; -import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; -import java.util.StringTokenizer; import java.util.Timer; -import static net.thauvin.erik.mobibot.Utils.bold; import static org.apache.commons.lang3.StringUtils.isNotBlank; /** @@ -108,52 +114,44 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; @Version(properties = "version.properties", className = "ReleaseInfo") public class Mobibot extends PircBot { - + // Info strings + @SuppressWarnings("indentation") + public static final List<String> INFO = + List.of(ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", + "https://www.mobitopia.org/mobibot/"); + // Version strings + @SuppressWarnings("indentation") + public static final List<String> MOBIBOT_VERSIONS = + List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty( + "java.runtime.version") + ')', + "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + + ", " + + System.getProperty("java.vm.info") + ')'); + // Timer + public static final Timer timer = new Timer(true); // Default port private static final int DEFAULT_PORT = 6667; // Default server private static final String DEFAULT_SERVER = "irc.freenode.net"; - // Info strings - @SuppressWarnings("indentation") - private static final String[] INFO_STRS = { - ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", - "https://www.mobitopia.org/mobibot/" }; - // Link match string - private static final String LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"; - // Default maximum number of entries to display - private static final int MAX_ENTRIES = 8; - // Default maximum recap entries - private static final int MAX_RECAP = 10; // Maximum number of times the bot will try to reconnect, if disconnected private static final int MAX_RECONNECT = 10; // Number of milliseconds to delay between consecutive messages private static final long MESSAGE_DELAY = 1000L; - // Modules - private static final List<AbstractModule> MODULES = new ArrayList<>(0); - // Tags/categories marker - private static final String TAGS_MARKER = "tags:"; - // Version strings - @SuppressWarnings("indentation") - private static final String[] VERSION_STRS = - { "Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', - "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " - + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty( - "java.runtime.version") + ')', - "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", " - + System.getProperty("java.vm.info") + ')' }; // Logger private static final Logger logger = LogManager.getLogger(Mobibot.class); - // Timer - private static final Timer timer = new Timer(true); - // Commands list - private final List<String> commandsList = new ArrayList<>(); - // Entries array - private final List<EntryLink> entries = new ArrayList<>(0); - // History/backlogs array - private final List<String> history = new ArrayList<>(0); - // Ignored nicks array - private final Set<String> ignoredNicks = new HashSet<>(0); + // Ignore command + public final Ignore ignoreCommand; + // Automatically post links to Twitter + public final boolean isTwitterAutoPost; + // Tell object + public final Tell tell; + // Commands + private final List<AbstractCommand> commands = new ArrayList<>(); + // Commands Names + private final List<String> commandsNames = new ArrayList<>(); // Main channel private final String ircChannel; // IRC port @@ -164,24 +162,22 @@ public class Mobibot extends PircBot { private final Level loggerLevel; // Log directory private final String logsDir; - // Recap array - private final List<String> recap = new ArrayList<>(0); - // Tags keywords matches - private final List<String> tagsKeywords = new ArrayList<>(); - // Tell object - private final Tell tell; - // The Twitter auto-posts list. - private final List<Integer> twitterAutoLinks = new ArrayList<>(); - // Automatically post links to Twitter - private final boolean twitterAutoPost; + // Modules + private final List<AbstractModule> modules = new ArrayList<>(0); + // Modules + private final List<String> modulesNames = new ArrayList<>(0); + // Operators commands names + private final List<String> opsCommandsNames = new ArrayList<>(); + // Today's date + private final String today = Utils.today(); + // Twitter auto-posts. + private final Set<Integer> twitterEntries = new HashSet<>(); // Twitter handle for channel join notifications private final String twitterHandle; // Twitter module private final Twitter twitterModule; // Backlogs URL private String backLogsUrl = ""; - // Default tags/categories - private String defaultTags = ""; // Feed URL private String feedUrl = ""; // Ident message @@ -192,8 +188,6 @@ public class Mobibot extends PircBot { private String identPwd = ""; // Pinboard posts handler private Pinboard pinboard; - // Today's date - private String today = Utils.today(); // Weblog URL private String weblogUrl = ""; @@ -222,34 +216,16 @@ public class Mobibot extends PircBot { // Set the logger level loggerLevel = logger.getLevel(); - // Load the current entries, if any + // Load the current entries and backlogs, if any try { - today = EntriesMgr.loadEntries(logsDir + EntriesMgr.CURRENT_XML, ircChannel, entries); + UrlMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, ircChannel); if (logger.isDebugEnabled()) { - logger.debug("Last feed: {}", today); + logger.debug("Last feed: {}", UrlMgr.getToday()); } - - if (!Utils.today().equals(today)) { - entries.clear(); - today = Utils.today(); - } - } catch (IOException ignore) { - // Do nothing - } catch (FeedException e) { + } catch (Exception e) { if (logger.isErrorEnabled()) { - logger.error("An error occurred while parsing the '" + EntriesMgr.CURRENT_XML + "' file.", e); - } - } - - // Load the backlogs, if any - try { - EntriesMgr.loadBacklogs(logsDir + EntriesMgr.NAV_XML, history); - } catch (IOException ignore) { - // Do nothing - } catch (FeedException e) { - if (logger.isErrorEnabled()) { - logger.error("An error occurred while parsing the '" + EntriesMgr.NAV_XML + "' file.", e); + logger.error("An error occurred while loading the logs.", e); } } @@ -269,28 +245,53 @@ public class Mobibot extends PircBot { // Set the pinboard authentication setPinboardAuth(p.getProperty("pinboard-api-token")); + // Set the ignored nicks + ignoreCommand = new Ignore(p.getProperty("ignore", "")); + + // Load the commands + commands.add(new Cycle()); + commands.add(ignoreCommand); + commands.add(new Info()); + commands.add(new Me()); + commands.add(new MobibotVersion()); + commands.add(new Modules()); + commands.add(new Msg()); + commands.add(new Nick()); + commands.add(new Recap()); + commands.add(new Say()); + commands.add(new Users()); + + // Load the links commands + commands.add(new Comment()); + commands.add(new Posting()); + commands.add(new Tags()); + commands.add(new UrlMgr(p.getProperty("tags", ""), p.getProperty("tags-keywords", ""))); + commands.add(new View()); + + // Load the modules - MODULES.add(new Calc()); - MODULES.add(new CurrencyConverter()); - MODULES.add(new Dice()); - MODULES.add(new GoogleSearch()); - MODULES.add(new Joke()); - MODULES.add(new Lookup()); - MODULES.add(new Ping()); - MODULES.add(new RockPaperScissors()); - MODULES.add(new StockQuote()); + addModule(new Calc()); + addModule(new CurrencyConverter()); + addModule(new Dice()); + addModule(new GoogleSearch()); + addModule(new Joke()); + addModule(new Lookup()); + addModule(new Ping()); + addModule(new RockPaperScissors()); + addModule(new StockQuote()); twitterModule = new Twitter(); - MODULES.add(twitterModule); + addModule(twitterModule); twitterHandle = p.getProperty(Constants.TWITTER_HANDLE_PROP, ""); - twitterAutoPost = Boolean.parseBoolean(p.getProperty(Constants.TWITTER_AUTOPOST_PROP, "false")); - - MODULES.add(new War()); - MODULES.add(new Weather2()); - MODULES.add(new WorldTime()); + isTwitterAutoPost = + Boolean.parseBoolean(p.getProperty(Constants.TWITTER_AUTOPOST_PROP, "false")) + && twitterModule.isEnabled(); + addModule(new War()); + addModule(new Weather2()); + addModule(new WorldTime()); // Load the modules properties - MODULES.stream().filter(AbstractModule::hasProperties).forEach(module -> { + modules.stream().filter(AbstractModule::hasProperties).forEach(module -> { for (final String s : module.getPropertyKeys()) { module.setProperty(s, p.getProperty(s, "")); } @@ -299,15 +300,8 @@ public class Mobibot extends PircBot { // Get the tell command settings tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); - // Set the tags - setTags(p.getProperty("tags", "")); - setTagsKeywords(p.getProperty("tags-keywords", "")); - - // Set the ignored nicks - setIgnoredNicks(p.getProperty("ignore", "")); - // Save the entries - saveEntries(true); + UrlMgr.saveEntries(this, true); } /** @@ -323,18 +317,18 @@ public class Mobibot extends PircBot { public static void main(final String[] args) { // Setup the command line options final Options options = new Options() - .addOption(Commands.HELP_ARG.substring(0, 1), - Commands.HELP_ARG, + .addOption(Constants.HELP_ARG.substring(0, 1), + Constants.HELP_ARG, false, "print this help message") - .addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false, + .addOption(Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG, false, "print debug & logging data directly to the console") - .addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg() + .addOption(Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg() .argName("file") .desc("use " + "alternate properties file") - .longOpt(Commands.PROPS_ARG).build()) - .addOption(Commands.VERSION_ARG.substring(0, 1), - Commands.VERSION_ARG, + .longOpt(Constants.PROPS_ARG).build()) + .addOption(Constants.VERSION_ARG.substring(0, 1), + Constants.VERSION_ARG, false, "print version info"); @@ -350,18 +344,18 @@ public class Mobibot extends PircBot { System.exit(1); } - if (line.hasOption(Commands.HELP_ARG.charAt(0))) { + if (line.hasOption(Constants.HELP_ARG.charAt(0))) { // Output the usage new HelpFormatter().printHelp(Mobibot.class.getName(), options); - } else if (line.hasOption(Commands.VERSION_ARG.charAt(0))) { - for (final String s : INFO_STRS) { + } else if (line.hasOption(Constants.VERSION_ARG.charAt(0))) { + for (final String s : INFO) { System.out.println(s); } } else { final Properties p = new Properties(); try (final InputStream fis = Files.newInputStream( - Paths.get(line.getOptionValue(Commands.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + Paths.get(line.getOptionValue(Constants.PROPS_ARG.charAt(0), "./mobibot.properties")))) { // Load the properties files p.load(fis); } catch (FileNotFoundException e) { @@ -379,7 +373,7 @@ public class Mobibot extends PircBot { final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); // Redirect the stdout and stderr - if (!line.hasOption(Commands.DEBUG_ARG.charAt(0))) { + if (!line.hasOption(Constants.DEBUG_ARG.charAt(0))) { try { final PrintStream stdout = new PrintStream( new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); @@ -408,19 +402,6 @@ public class Mobibot extends PircBot { } } - /** - * Sleeps for the specified number of seconds. - * - * @param secs The number of seconds to sleep for. - */ - private static void sleep(final int secs) { - try { - Thread.sleep(secs * 1000L); - } catch (InterruptedException ignore) { - // Do nothing - } - } - /** * Sends an action to the current channel. * @@ -442,6 +423,27 @@ public class Mobibot extends PircBot { } } + /** + * Adds a module. + * + * @param module The module to add. + */ + private void addModule(final AbstractModule module) { + modules.add(module); + modulesNames.add(module.getClass().getSimpleName()); + } + + /** + * Adds pin on pinboard. + * + * @param entry The entry to add. + */ + public final void addPin(final EntryLink entry) { + if (pinboard != null) { + pinboard.addPost(entry); + } + } + /** * Connects to the server and joins the channel. */ @@ -470,14 +472,22 @@ public class Mobibot extends PircBot { } } } - - setVersion(INFO_STRS[0]); - + setVersion(INFO.get(0)); identify(); - joinChannel(); } + /** + * Deletes pin on pinboard. + * + * @param entry The entry to delete. + */ + public final void deletePin(final EntryLink entry) { + if (pinboard != null) { + pinboard.deletePost(entry); + } + } + /** * Responds with the title and links from the RSS feed. * @@ -491,24 +501,6 @@ public class Mobibot extends PircBot { } } - /** - * Returns the index of the specified duplicate entry, if any. - * - * @param link The link. - * @return The index or -1 if none. - */ - private int findDupEntry(final String link) { - synchronized (entries) { - for (int i = 0; i < entries.size(); i++) { - if (link.equals(entries.get(i).getLink())) { - return i; - } - } - } - - return -1; - } - /** * Returns the backlogs URL. * @@ -564,6 +556,15 @@ public class Mobibot extends PircBot { return logsDir; } + /** + * Returns the enabled modules names. + * + * @return The modules names. + */ + public final List<String> getModulesNames() { + return modulesNames; + } + /** * Returns the bot's nickname regexp pattern. * @@ -603,159 +604,114 @@ public class Mobibot extends PircBot { } /** - * Returns indented and bold help string. + * Responds with the commands help, if any. * - * @param help The help string. - * @return The indented help string. + * @param sender The nick of the person requesting Constants. + * @param topic The help topic. + * @return {@code true} if the topic was found, {@code false} otherwise. */ - public final String helpIndent(final String help) { - return helpIndent(help, true); + private boolean helpCommands(final String sender, final String topic) { + for (final AbstractCommand command : commands) { + if (command.isVisible() && command.getCommand().startsWith(topic)) { + return command.helpResponse(this, topic, sender, isOp(sender), true); + } + } + return false; } /** - * Returns indented help string. + * Responds with the default Constants. * - * @param help The help string. - * @param isBold The bold flag. - * @return The indented help string. + * @param sender The nick of the person requesting Constants. + * @param isOp The channel operator flag. */ - public String helpIndent(final String help, final boolean isBold) { - return " " + (isBold ? bold(help) : help); + public void helpDefault(final String sender, final boolean isOp) { + send(sender, Utils.bold("Type a URL on " + ircChannel + " to post it.")); + send(sender, "For more information on a specific command, type:"); + send(sender, Utils.helpIndent(getNick() + ": " + Constants.HELP_CMD + " <command>")); + send(sender, Utils.bold("The commands are:")); + + if (commandsNames.isEmpty()) { + // Feed command + commandsNames.add(getChannelName()); + + // Commands + for (final AbstractCommand command : commands) { + if (command.isVisible()) { + if (command.isOp()) { + opsCommandsNames.add(command.getCommand()); + } else { + commandsNames.add(command.getCommand()); + } + } + } + + // Modules commands + modules.stream().filter(AbstractModule::isEnabled) + .forEach(module -> commandsNames.addAll(module.getCommands())); + + // Tell command + if (tell.isEnabled()) { + commandsNames.add(Tell.TELL_CMD); + } + + Collections.sort(commandsNames); + Collections.sort(opsCommandsNames); + } + + // Print 6 commands per line + final int chunk = 6; + for (int i = 0; i < commandsNames.size(); i += chunk) { + send(sender, Utils.helpIndent( + String.join(" ", commandsNames.subList(i, Math.min(commandsNames.size(), i + chunk))))); + } + + if (isOp) { + send(sender, Utils.bold("The op commands are:")); + send(sender, Utils.helpIndent(String.join(" ", opsCommandsNames))); + } } /** - * Responds with the bot's help. + * Responds with the modules help, if any. + * + * @param sender The nick of the person requesting Constants. + * @param topic The help topic. + * @return {@code true} if the topic was found, {@code false} otherwise. + */ + private boolean helpModules(final String sender, final String topic) { + for (final AbstractModule module : modules) { + for (final String cmd : module.getCommands()) { + if (topic.equals(cmd)) { + module.helpResponse(this, sender, topic, true); + return true; + } + } + } + return false; + } + + /** + * Responds with the bot's Constants. * * @param sender The nick of the person who sent the private message. * @param topic The help topic, if any. */ private void helpResponse(final String sender, final String topic) { - final String lcTopic = topic.toLowerCase(Constants.LOCALE).trim(); - - if (Commands.HELP_POSTING_KEYWORD.equals(lcTopic)) { - send(sender, bold("Post a URL, by saying it on a line on its own:")); - send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]")); - send(sender, "I will reply with a label, for example: " + bold(Commands.LINK_CMD + '1')); - send(sender, "To add a title, use a its label and a pipe:"); - send(sender, helpIndent(Commands.LINK_CMD + "1:|This is the title")); - send(sender, "To add a comment: "); - send(sender, helpIndent(Commands.LINK_CMD + "1:This is a comment")); - send(sender, "I will reply with a label, for example: " + bold(Commands.LINK_CMD + "1.1")); - send(sender, "To edit a comment, use its label: "); - send(sender, helpIndent(Commands.LINK_CMD + "1.1:This is an edited comment")); - send(sender, "To delete a comment, use its label and a minus sign: "); - send(sender, helpIndent(Commands.LINK_CMD + "1.1:-")); - send(sender, "You can also view a posting by saying its label."); - } else if (Commands.HELP_TAGS_KEYWORD.equals(lcTopic)) { - send(sender, bold("To categorize or tag a URL, use its label and a T:")); - send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]")); - } else if (Commands.VIEW_CMD.equals(lcTopic)) { - send(sender, "To list or search the current URL posts:"); - send(sender, helpIndent(getNick() + ": " + Commands.VIEW_CMD) + " [<start>] [<query>]"); - } else if (lcTopic.equalsIgnoreCase(getChannelName())) { - send(sender, "To list the last 5 posts from the channel's weblog:"); - send(sender, helpIndent(getNick() + ": " + getChannelName())); - } else if (Commands.RECAP_CMD.equals(lcTopic)) { - send(sender, "To list the last 10 public channel messages:"); - send(sender, helpIndent(getNick() + ": " + Commands.RECAP_CMD)); - } else if (Commands.USERS_CMD.equals(lcTopic)) { - send(sender, "To list the users present on the channel:"); - send(sender, helpIndent(getNick() + ": " + Commands.USERS_CMD)); - } else if (Commands.INFO_CMD.equals(lcTopic)) { - send(sender, "To view information about the bot:"); - send(sender, helpIndent(getNick() + ": " + Commands.INFO_CMD)); + final boolean isOp = isOp(sender); + if (StringUtils.isBlank(topic)) { + helpDefault(sender, isOp); } else { - final String msg = "/msg "; - if (Commands.CYCLE_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot leave the channel and come back:"); - send(sender, helpIndent(msg + getNick() + ' ' + Commands.CYCLE_CMD)); - } else if (Commands.ME_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot perform an action:"); - send(sender, helpIndent(msg + getNick() + ' ' + Commands.ME_CMD + " <action>")); - } else if (Commands.SAY_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot say something on the channel:"); - send(sender, helpIndent(msg + getNick() + ' ' + Commands.SAY_CMD + " <text>")); - } else if (Commands.VERSION_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To view the version data (bot, java, etc.):"); - send(sender, helpIndent(msg + getNick() + ' ' + Commands.VERSION_CMD)); - } else if (Commands.MSG_CMD.equals(lcTopic) && isOp(sender)) { - send(sender, "To have the bot send a private message to someone:"); - send(sender, helpIndent(msg + getNick() + ' ' + Commands.MSG_CMD + " <nick> <text>")); - } else if (Commands.IGNORE_CMD.equals(lcTopic)) { - send(sender, "To check your ignore status:"); - send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD)); - - send(sender, "To toggle your ignore status:"); - send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD)); + final String lcTopic = topic.toLowerCase(Constants.LOCALE).trim(); + if (lcTopic.equals(getChannelName())) { + send(sender, Utils.bold("To list the last 5 posts from the channel's weblog:")); + send(sender, Utils.helpIndent(getNick() + ": " + getChannelName())); } else if (Tell.TELL_CMD.equals(lcTopic) && tell.isEnabled()) { tell.helpResponse(sender); } else { - for (final AbstractModule module : MODULES) { - for (final String cmd : module.getCommands()) { - if (lcTopic.equals(cmd)) { - module.helpResponse(this, sender, topic, true); - return; - } - } - } - - send(sender, bold("Type a URL on " + ircChannel + " to post it.")); - send(sender, "For more information on a specific command, type:"); - send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>")); - send(sender, "The commands are:"); - - if (commandsList.isEmpty()) { - commandsList.add(Commands.IGNORE_CMD); - commandsList.add(Commands.INFO_CMD); - commandsList.add(getChannelName()); - commandsList.add(Commands.HELP_POSTING_KEYWORD); - commandsList.add(Commands.HELP_TAGS_KEYWORD); - commandsList.add(Commands.RECAP_CMD); - commandsList.add(Commands.USERS_CMD); - commandsList.add(Commands.VIEW_CMD); - - MODULES.stream().filter(AbstractModule::isEnabled) - .forEach(module -> commandsList.addAll(module.getCommands())); - - if (tell.isEnabled()) { - commandsList.add(Tell.TELL_CMD); - } - - Collections.sort(commandsList); - } - - final StringBuilder sb = new StringBuilder(0); - - for (int i = 0, cmdCount = 1; i < commandsList.size(); i++, cmdCount++) { - if (sb.length() > 0) { - sb.append(" "); - } - - sb.append(commandsList.get(i)); - - // 6 commands per line or last command - if (sb.length() > 0 && (cmdCount == 6 || i == (commandsList.size() - 1))) { - send(sender, helpIndent(sb.toString())); - - sb.setLength(0); - cmdCount = 0; - } - } - - if (isOp(sender)) { - send(sender, "The op commands are:"); - send(sender, - helpIndent(Commands.CYCLE_CMD - + " " - + Commands.ME_CMD - + " " - + Commands.MODULES_CMD - + " " - + Commands.MSG_CMD - + " " - + Commands.SAY_CMD - + " " - + Commands.VERSION_CMD)); + // Command, Modules or Default + if (!helpCommands(sender, topic) && !helpModules(sender, lcTopic)) { + helpDefault(sender, isOp); } } } @@ -776,98 +732,6 @@ public class Mobibot extends PircBot { } } - /** - * Processes the {@link net.thauvin.erik.mobibot.Commands#IGNORE_CMD} command. - * - * @param sender The sender. - * @param args The command arguments. - */ - private void ignoreResponse(final String sender, final String args) { - if (!isOp(sender)) { - final String nick = sender.toLowerCase(Constants.LOCALE); - final boolean isMe = args.toLowerCase(Constants.LOCALE).startsWith(Commands.IGNORE_ME_KEYWORD); - if (isMe) { - if (ignoredNicks.remove(nick)) { - send(sender, "You are no longer ignored."); - } else { - ignoredNicks.add(nick); - send(sender, "You are now ignored."); - } - } else { - if (ignoredNicks.contains(nick)) { - send(sender, "You are currently ignored."); - } else { - send(sender, "You are not currently ignored."); - } - } - } else { - if (args.length() > 0) { - final String[] nicks = args.toLowerCase(Constants.LOCALE).split(" "); - - for (final String nick : nicks) { - final String ignore; - - if (Commands.IGNORE_ME_KEYWORD.equals(nick)) { - ignore = sender.toLowerCase(Constants.LOCALE); - } else { - ignore = nick; - } - - if (!ignoredNicks.remove(ignore)) { - ignoredNicks.add(ignore); - } - } - } - - send(sender, "The following nicks are ignored: " + ignoredNicks); - } - } - - /** - * Responds with the bot's information. - * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void infoResponse(final String sender, final boolean isPrivate) { - for (final String info : INFO_STRS) { - if (info.startsWith("https://")) { - send(sender, info, Colors.DARK_GREEN, isPrivate); - } else { - send(sender, info, isPrivate); - } - } - - final StringBuilder info = new StringBuilder(29); - - info.append("Uptime: ").append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())).append( - " [Entries: ").append(entries.size()); - - if (isOp(sender)) { - if (tell.isEnabled()) { - info.append(", Messages: ").append(tell.size()); - } - - if (twitterAutoPost && twitterModule.isEnabled()) { - info.append(", Twitter: ").append(twitterAutoLinks.size()); - } - } - - info.append(", Recap: ").append(recap.size()).append(']'); - - send(sender, info.toString(), isPrivate); - } - - /** - * Determines whether the specified nick should be ignored. - * - * @param nick The nick. - * @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise. - */ - private boolean isIgnoredNick(final String nick) { - return isNotBlank(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE)); - } - /** * Returns <code>true</code> if the specified sender is an Op on the {@link #ircChannel channel}. * @@ -902,9 +766,7 @@ public class Mobibot extends PircBot { if (isNotBlank(weblogUrl)) { setVersion(weblogUrl); } - sleep(5); - connect(); } @@ -922,100 +784,7 @@ public class Mobibot extends PircBot { boolean isCommand = false; - // Capture URLs posted on the channel - if (message.matches(LINK_MATCH) && !isIgnoredNick(sender)) { - isCommand = true; - - final String[] cmds = message.split(" ", 2); - - if (cmds.length == 1 || (!cmds[1].contains(getNick()))) { - final String link = cmds[0].trim(); - boolean isBackup = false; - - final int dupIndex = findDupEntry(link); - - if (dupIndex == -1) { - if (!Utils.today().equals(today)) { - isBackup = true; - saveEntries(true); - - entries.clear(); - today = Utils.today(); - } - - final StringBuilder tags = new StringBuilder(defaultTags); - String title = Constants.NO_TITLE; - - if (cmds.length == 2) { - final String[] data = cmds[1].trim().split(TAGS_MARKER, 2); - - if (data.length == 1) { - title = data[0].trim(); - } else { - if (isNotBlank(data[0])) { - title = data[0].trim(); - } - - tags.append(' ').append(data[1].trim()); - } - } - - if (Constants.NO_TITLE.equals(title)) { - try { - final Document html = Jsoup.connect(link).userAgent("Mozilla").get(); - final String htmlTitle = html.title(); - - if (isNotBlank(htmlTitle)) { - final String[] split = htmlTitle.split("( \\| )", 2); - if (split.length == 2) { - title = split[0]; - } else { - title = htmlTitle; - } - } - } catch (IOException ignore) { - // Do nothing - } - } - - if (!tagsKeywords.isEmpty()) { - for (final String match : tagsKeywords) { - final String m = match.trim(); - if (title.matches("(?i).*\\b" + m + "\\b.*")) { - tags.append(' ').append(m); - } - } - } - - entries.add(new EntryLink(link, title, sender, login, channel, tags.toString())); - - final int index = entries.size() - 1; - final EntryLink entry = entries.get(index); - send(channel, EntriesUtils.buildLink(index, entry)); - - // Add link to pinboard - if (pinboard != null) { - pinboard.addPost(entry); - } - - // Queue link for posting to twitter - if (twitterAutoPost && twitterModule.isEnabled()) { - twitterAutoLinks.add(index); - timer.schedule(new TwitterTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L); - } - - saveEntries(isBackup); - - if (Constants.NO_TITLE.equals(entry.getTitle())) { - send(sender, "Please specify a title, by typing:", true); - send(sender, helpIndent(Commands.LINK_CMD + (index + 1) + ":|This is the title"), true); - } - } else { - final EntryLink entry = entries.get(dupIndex); - send(sender, bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry)); - } - } - } else if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> + if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> isCommand = true; final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); @@ -1027,209 +796,47 @@ public class Mobibot extends PircBot { args = cmds[1].trim(); } - if (cmd.startsWith(Commands.HELP_CMD)) { // mobibot: help + if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help helpResponse(sender, args); - } else if (Commands.RECAP_CMD.equals(cmd)) { // mobibot: recap - recapResponse(sender, false); - } else if (Commands.USERS_CMD.equals(cmd)) { // mobibot: users - usersResponse(sender, false); - } else if (Commands.INFO_CMD.equals(cmd)) { // mobibot: info - infoResponse(sender, false); - } else if (Commands.VERSION_CMD.equals(cmd)) { // mobbiot: version - versionResponse(sender, false); } else if (cmd.equalsIgnoreCase(getChannelName())) { // mobibot: <channel> feedResponse(sender); - } else if (cmd.startsWith(Commands.VIEW_CMD)) { // mobibot: view - viewResponse(sender, args, false); } else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) { // mobibot: tell tell.response(sender, args); - } else if (cmd.startsWith(Commands.IGNORE_CMD)) { // mobibot: ignore - ignoreResponse(sender, args); } else { - for (final AbstractModule module : MODULES) { // modules - for (final String c : module.getCommands()) { - if (cmd.startsWith(c)) { - module.commandResponse(this, sender, cmd, args, false); + boolean skip = false; + // Commands + for (final AbstractCommand command : commands) { + if (command.isPublic() && command.getCommand().startsWith(cmd)) { + command.commandResponse(this, sender, login, args, isOp(sender), false); + skip = true; + break; + } + } + if (!skip) { + // Modules + for (final AbstractModule module : modules) { // modules + for (final String c : module.getCommands()) { + if (cmd.startsWith(c)) { + module.commandResponse(this, sender, cmd, args, false); + break; + } } } } } - } else if (message.matches(Commands.LINK_CMD + "[0-9]+:.*")) { // L1:<comment>, L1:-, L1:|<title>, etc - isCommand = true; - - final String[] cmds = message.substring(1).split(":", 2); - final int index = Integer.parseInt(cmds[0]) - 1; - - // L1:<comment> - if (index < entries.size()) { - final String cmd = cmds[1].trim(); - - if (cmd.length() == 0) { - final EntryLink entry = entries.get(index); - send(channel, EntriesUtils.buildLink(index, entry)); - - if (entry.hasTags()) { - send(channel, EntriesUtils.buildTags(index, entry)); - } - - if (entry.hasComments()) { - final EntryComment[] comments = entry.getComments(); - - for (int i = 0; i < comments.length; i++) { - send(channel, EntriesUtils.buildComment(index, i, comments[i])); - } - } - } else { - // L1:- - if ("-".equals(cmd)) { - final EntryLink entry = entries.get(index); - - if (entry.getLogin().equals(login) || isOp(sender)) { - if (pinboard != null) { - pinboard.deletePost(entry); - } - - if (twitterAutoPost && twitterModule.isEnabled()) { - twitterAutoLinks.remove(index); - } - - entries.remove(index); - send(channel, "Entry " + Commands.LINK_CMD + (index + 1) + " removed."); - saveEntries(false); - } else { - send(sender, "Please ask a channel op to remove this entry for you."); - } - } else if (cmd.charAt(0) == '|') { // L1:|<title> - if (cmd.length() > 1) { - final EntryLink entry = entries.get(index); - entry.setTitle(cmd.substring(1).trim()); - - if (pinboard != null) { - pinboard.updatePost(entry.getLink(), entry); - } - - send(channel, EntriesUtils.buildLink(index, entry)); - saveEntries(false); - } - } else if (cmd.charAt(0) == '=') { // L1:=<url> - final EntryLink entry = entries.get(index); - - if (entry.getLogin().equals(login) || isOp(sender)) { - final String link = cmd.substring(1); - - if (link.matches(LINK_MATCH)) { - final String oldLink = entry.getLink(); - - entry.setLink(link); - - if (pinboard != null) { - pinboard.updatePost(oldLink, entry); - } - - send(channel, EntriesUtils.buildLink(index, entry)); - saveEntries(false); - } - } else { - send(sender, "Please ask a channel op to change this link for you."); - } - } else if (cmd.charAt(0) == '?') { // L1:?<author> - if (isOp(sender)) { - if (cmd.length() > 1) { - final EntryLink entry = entries.get(index); - entry.setNick(cmd.substring(1)); - send(channel, EntriesUtils.buildLink(index, entry)); - saveEntries(false); - } - } else { - send(sender, "Please ask a channel op to change the author of this link for you."); - } - } else { - final EntryLink entry = entries.get(index); - final int cindex = entry.addComment(cmd, sender); - - final EntryComment comment = entry.getComment(cindex); - send(sender, EntriesUtils.buildComment(index, cindex, comment)); - saveEntries(false); - } - } - } - } else if (message.matches(Commands.LINK_CMD + "[0-9]+T:.*")) { // L1T:<+-tag> - isCommand = true; - - final String[] cmds = message.substring(1).split("T:", 2); - final int index = Integer.parseInt(cmds[0]) - 1; - - if (index < entries.size()) { - final String cmd = cmds[1].trim(); - - final EntryLink entry = entries.get(index); - - if (cmd.length() != 0) { - if (entry.getLogin().equals(login) || isOp(sender)) { - entry.setTags(cmd); - - if (pinboard != null) { - pinboard.updatePost(entry.getLink(), entry); - } - - send(channel, EntriesUtils.buildTags(index, entry)); - saveEntries(false); - } else { - send(sender, "Please ask a channel op to change the tags for you."); - } - } else { - if (entry.hasTags()) { - send(channel, EntriesUtils.buildTags(index, entry)); - } else { - send(sender, "The entry has no tags. Why don't add some?"); - } - } - } - } else if (message.matches(Commands.LINK_CMD + "[0-9]+\\.[0-9]+:.*")) { // L11:<command> - isCommand = true; - - final String[] cmds = message.substring(1).split("[.:]", 3); - final int index = Integer.parseInt(cmds[0]) - 1; - - if (index < entries.size()) { - final EntryLink entry = entries.get(index); - final int cindex = Integer.parseInt(cmds[1]) - 1; - - if (cindex < entry.getCommentsCount()) { - final String cmd = cmds[2].trim(); - - // L11: - if (cmd.length() == 0) { - final EntryComment comment = entry.getComment(cindex); - send(channel, EntriesUtils.buildComment(index, cindex, comment)); - } else if ("-".equals(cmd)) { // L11:- - entry.deleteComment(cindex); - send(channel, "Comment " + Commands.LINK_CMD + (index + 1) + '.' + (cindex + 1) + " removed."); - saveEntries(false); - } else if (cmd.charAt(0) == '?') { // L11:?<author> - if (isOp(sender)) { - if (cmd.length() > 1) { - final EntryComment comment = entry.getComment(cindex); - comment.setNick(cmd.substring(1)); - send(channel, EntriesUtils.buildComment(index, cindex, comment)); - saveEntries(false); - } - } else { - send(sender, "Please ask a channel op to change the author of this comment for you."); - } - } else { - entry.setComment(cindex, cmd, sender); - - final EntryComment comment = entry.getComment(cindex); - send(sender, EntriesUtils.buildComment(index, cindex, comment)); - saveEntries(false); - } + } else { + // Commands + for (final AbstractCommand command : commands) { + if (command.matches(message)) { + command.commandResponse(this, sender, login, message, isOp(sender), false); + isCommand = true; + break; } } } if (!isCommand) { - storeRecap(sender, message, false); + Recap.storeRecap(sender, message, false); } tell.send(sender, true); @@ -1255,92 +862,48 @@ public class Mobibot extends PircBot { args = cmds[1].trim(); } - if (cmd.startsWith(Commands.HELP_CMD)) { + final boolean isOp = isOp(sender); + + if (cmd.startsWith(Constants.HELP_CMD)) { helpResponse(sender, args); - } else if ("kill".equals(cmd) && isOp(sender)) { + } else if (isOp && "kill".equals(cmd)) { sendRawLine("QUIT : Poof!"); System.exit(0); - } else if (Commands.DIE_CMD.equals(cmd) && isOp(sender)) { + } else if (isOp && Constants.DIE_CMD.equals(cmd)) { send(ircChannel, sender + " has just signed my death sentence."); timer.cancel(); twitterShutdown(); twitterNotification("killed by " + sender + " on " + ircChannel); - saveEntries(true); + sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); - } else if (Commands.CYCLE_CMD.equals(cmd)) { - send(ircChannel, sender + " has just asked me to leave. I'll be back!"); - sleep(0); - partChannel(ircChannel); - sleep(10); - joinChannel(ircChannel); - } else if (Commands.RECAP_CMD.equals(cmd)) { - recapResponse(sender, true); - } else if (Commands.USERS_CMD.equals(cmd)) { - usersResponse(sender, true); - } else if ((cmds.length > 1) && isOp(sender) && Commands.ADDLOG_CMD.equals(cmd)) { + } else if (isOp && (cmds.length > 1) && Constants.ADDLOG_CMD.equals(cmd)) { // e.g 2014-04-01 final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); if (backlog.exists()) { - history.add(0, args); - send(sender, history.toString(), true); + UrlMgr.addHistory(0, args); + send(sender, UrlMgr.getHistory().toString(), true); } else { send(sender, "The specified log could not be found."); } - } else if (Commands.ME_CMD.equals(cmd) && isOp(sender)) { - if (args.length() > 1) { - action(args); - } else { - helpResponse(sender, Commands.ME_CMD); - } - } else if (Commands.MODULES_CMD.equals(cmd) && isOp(sender)) { - if (MODULES.isEmpty()) { - send(sender, "There are not enabled modules.", true); - } else { - send(sender, "The enabled modules are: "); - for (final AbstractModule mod : MODULES) { - send(sender, helpIndent(mod.getClass().getSimpleName())); - } - } - } else if ((cmds.length > 1) && isOp(sender) && Commands.NICK_CMD.equals(cmd)) { - changeNick(args); - } else if (Commands.SAY_CMD.equals(cmd) && isOp(sender)) { - if (cmds.length > 1) { - send(ircChannel, args, true); - } else { - helpResponse(sender, Commands.SAY_CMD); - } - } else if (Commands.MSG_CMD.equals(cmd) && isOp(sender)) { - if (cmds.length > 1) { - final String[] msg = args.split(" ", 2); - - if (args.length() > 2) { - send(msg[0], msg[1], true); - } else { - helpResponse(sender, Commands.MSG_CMD); - } - } else { - helpResponse(sender, Commands.MSG_CMD); - } - } else if (Commands.VIEW_CMD.equals(cmd)) { - viewResponse(sender, args, true); } else if (Tell.TELL_CMD.equals(cmd) && tell.isEnabled()) { tell.response(sender, args); - } else if (Commands.INFO_CMD.equals(cmd)) { - infoResponse(sender, true); - } else if (Commands.VERSION_CMD.equals(cmd)) { - versionResponse(sender, true); - } else if (Commands.DEBUG_CMD.equals(cmd) && isOp(sender)) { + } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { if (logger.isDebugEnabled()) { Configurator.setLevel(logger.getName(), loggerLevel); } else { Configurator.setLevel(logger.getName(), Level.DEBUG); - } send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); } else { - for (final AbstractModule module : MODULES) { + for (final AbstractCommand command : commands) { + if (command.getCommand().startsWith(cmd)) { + command.commandResponse(this, sender, login, args, isOp, true); + return; + } + } + for (final AbstractModule module : modules) { if (module.isPrivateMsgEnabled()) { for (final String c : module.getCommands()) { if (cmd.equals(c)) { @@ -1350,8 +913,7 @@ public class Mobibot extends PircBot { } } } - - helpResponse(sender, ""); + helpDefault(sender, isOp); } } @@ -1362,7 +924,7 @@ public class Mobibot extends PircBot { protected final void onAction(final String sender, final String login, final String hostname, final String target, final String action) { if (target != null && target.equals(ircChannel)) { - storeRecap(sender, action, true); + Recap.storeRecap(sender, action, true); } } @@ -1382,31 +944,6 @@ public class Mobibot extends PircBot { tell.send(newNick); } - /** - * Responds with the last 10 public messages. - * - * @param sender The nick of the person who sent the private message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void recapResponse(final String sender, final boolean isPrivate) { - if (!recap.isEmpty()) { - for (final String r : recap) { - send(sender, r, isPrivate); - } - } else { - send(sender, "Sorry, nothing to recap.", true); - } - } - - /** - * Saves the entries. - * - * @param isDayBackup Set the <code>true</code> if the daily backup file should also be created. - */ - final void saveEntries(final boolean isDayBackup) { - EntriesMgr.saveEntries(this, entries, history, isDayBackup); - } - /** * Sends a private message or notice. * @@ -1518,21 +1055,6 @@ public class Mobibot extends PircBot { identMsg = msg; } - /** - * Sets the Ignored nicks. - * - * @param nicks The nicks to ignore - */ - final void setIgnoredNicks(final String nicks) { - if (isNotBlank(nicks)) { - final StringTokenizer st = new StringTokenizer(nicks, ","); - - while (st.hasMoreTokens()) { - ignoredNicks.add(st.nextToken().trim().toLowerCase(Constants.LOCALE)); - } - } - } - /** * Sets the pinboard authentication. * @@ -1544,28 +1066,6 @@ public class Mobibot extends PircBot { } } - /** - * Sets the default tags/categories. - * - * @param tags The tags. - */ - final void setTags(final String tags) { - defaultTags = tags; - } - - /** - * Sets the tags keywords matches. - * - * @param matches The tags keywords. - */ - final void setTagsKeywords(final String matches) { - if (isNotBlank(matches)) { - tagsKeywords.addAll(Arrays.asList(matches.split(", +?| +"))); - } else { - tagsKeywords.clear(); - } - } - /** * Sets the weblog URL. * @@ -1576,31 +1076,36 @@ public class Mobibot extends PircBot { } /** - * Stores the last 10 public messages and actions. + * Sleeps for the specified number of seconds. * - * @param sender The nick of the person who sent the private message. - * @param message The actual message sent. - * @param isAction Set to <code>true</code> if the message is an action. + * @param secs The number of seconds to sleep for. */ - private void storeRecap(final String sender, final String message, final boolean isAction) { - recap.add( - Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + " -> " + sender + (isAction ? " " : ": ") - + message); - - if (recap.size() > MAX_RECAP) { - recap.remove(0); + public final void sleep(final int secs) { + try { + Thread.sleep(secs * 1000L); + } catch (InterruptedException ignore) { + // Do nothing } } /** - * Auto-post to twitter. + * Add an entry to be posted on twitter. + * + * @param index The entry index. + */ + public void twitterAddEntry(final int index) { + twitterEntries.add(index); + } + + /** + * Post an entry to twitter. * * @param index The post entry index. */ - final void twitterAutoPost(final int index) { - if (twitterAutoPost && twitterModule.isEnabled() - && twitterAutoLinks.contains(index) && entries.size() >= index) { - final EntryLink entry = entries.get(index); + @SuppressFBWarnings("SUI_CONTAINS_BEFORE_REMOVE") + public final void twitterEntryPost(final int index) { + if (isTwitterAutoPost && twitterEntries.contains(index) && UrlMgr.getEntriesCount() >= index) { + final EntryLink entry = UrlMgr.getEntry(index); final String msg = entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + getChannel(); new Thread(() -> { @@ -1608,14 +1113,23 @@ public class Mobibot extends PircBot { twitterModule.post(twitterHandle, msg, false); } catch (ModuleException e) { if (logger.isWarnEnabled()) { - logger.warn("Failed to post link on twitter.", e); + logger.warn("Failed to post entry on twitter.", e); } } }).start(); - twitterAutoLinks.remove((Object) index); + twitterEntries.remove(index); } } + /** + * Return the total count of links to be posted to twitter. + * + * @return The count of twitter links. + */ + public final int twitterLinksCount() { + return twitterEntries.size(); + } + /** * Send a notification to the registered Twitter handle. * @@ -1638,133 +1152,35 @@ public class Mobibot extends PircBot { } } + /** + * Removes entry from twitter auto-post. + * + * @param index The entry's index. + */ + public final void twitterRemoveEntry(final int index) { + twitterEntries.remove(index); + } + /** * Post all the links on twitter on shutdown. */ final void twitterShutdown() { if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { - for (final int i : twitterAutoLinks) { - twitterAutoPost(i); + for (final int i : twitterEntries) { + twitterEntryPost(i); } } } /** - * Responds with the users on a channel. + * Updates pin on pinboard. * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. + * @param oldUrl The old pin url. + * @param entry The entry to update. */ - private void usersResponse(final String sender, final boolean isPrivate) { - final User[] users = getUsers(ircChannel); - final String[] nicks = new String[users.length]; - - for (int i = 0; i < users.length; i++) { - final String nick = users[i].getNick(); - if (isOp(nick)) { - nicks[i] = '@' + users[i].getNick(); - } else { - nicks[i] = users[i].getNick(); - } - } - - Arrays.sort(nicks, String.CASE_INSENSITIVE_ORDER); - - send(sender, String.join(" ", nicks), isPrivate); - } - - /** - * Responds with the bot's version info. - * - * @param sender The nick of the person who sent the message. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void versionResponse(final String sender, final boolean isPrivate) { - if (isOp(sender)) { - for (final String version : VERSION_STRS) { - send(sender, version, isPrivate); - } - } - } - - /** - * Responds with the stored links. - * - * @param sender The nick of the person who sent the message. - * @param args The view command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - private void viewResponse(final String sender, final String args, final boolean isPrivate) { - if (!entries.isEmpty()) { - final int max = entries.size(); - String lcArgs = args.toLowerCase(Constants.LOCALE); - int i = 0; - - if ((lcArgs.length() <= 0) && (max > MAX_ENTRIES)) { - i = max - MAX_ENTRIES; - } - - if (lcArgs.matches("^\\d+(| .*)")) { - final String[] split = lcArgs.split(" ", 2); - - try { - i = Integer.parseInt(split[0]); - - if (i > 0) { - i--; - } - - if (split.length == 2) { - lcArgs = split[1].trim(); - } else { - lcArgs = ""; - } - - if (i > max) { - i = 0; - } - } catch (NumberFormatException ignore) { - // Do nothing - } - } - - EntryLink entry; - int sent = 0; - - for (; i < max; i++) { - entry = entries.get(i); - - if (lcArgs.length() > 0) { - if ((entry.getLink().toLowerCase(Constants.LOCALE).contains(lcArgs)) - || (entry.getTitle().toLowerCase(Constants.LOCALE).contains(lcArgs)) - || (entry.getNick().toLowerCase(Constants.LOCALE).contains(lcArgs))) { - if (sent > MAX_ENTRIES) { - send(sender, - "To view more, try: " - + bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs), - isPrivate); - - break; - } - - send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate); - sent++; - } - } else { - if (sent > MAX_ENTRIES) { - send(sender, - "To view more, try: " + bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)), - isPrivate); - - break; - } - - send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate); - sent++; - } - } - } else { - send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate); + public final void updatePin(final String oldUrl, final EntryLink entry) { + if (pinboard != null) { + pinboard.updatePost(oldUrl, entry); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt index f3a5450..09abe27 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt @@ -36,6 +36,6 @@ import java.util.* class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() { override fun run() { - bot.twitterAutoPost(index) + bot.twitterEntryPost(index) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 1489ecf..056d216 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -159,6 +159,16 @@ public final class Utils { return colorize(s, Colors.DARK_GREEN); } + /** + * Returns indented help string. + * + * @param help The help string. + * @return The indented help string. + */ + public static String helpIndent(final String help) { + return " " + help; + } + /** * Returns the specified date as an ISO local date string. * diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt new file mode 100644 index 0000000..aeaac2d --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -0,0 +1,67 @@ +/* + * AbstractCommand.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot + +abstract class AbstractCommand { + abstract val command: String + abstract val help: List<String> + abstract val isOp: Boolean + abstract val isPublic: Boolean + abstract val isVisible: Boolean + + abstract fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) + + open fun helpResponse(bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { + if (!this.isOp || this.isOp == isOp) { + for (h in help) { + bot.send(sender, String.format(h, bot.nick), isPrivate) + } + return true + } + return false + } + + open fun matches(message: String): Boolean { + return false + } + +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt new file mode 100644 index 0000000..fa18a1b --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -0,0 +1,68 @@ +/* + * Cycle.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils + +class Cycle : AbstractCommand() { + private val wait = 10 + override val command = "cycle" + override val help = listOf( + Utils.bold("To have the bot leave the channel and come back:"), + Utils.helpIndent("/msg %s $command") + ) + override val isOp = true + override val isPublic = false + override val isVisible = true + + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + bot.send(bot.channel, "$sender has just asked me to leave. I'll be back!") + bot.sleep(wait) + bot.partChannel(bot.channel) + bot.sleep(wait) + bot.joinChannel(bot.channel) + } else { + bot.helpDefault(sender, isOp) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt new file mode 100644 index 0000000..b67fc18 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -0,0 +1,111 @@ +/* + * Ignore.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import java.util.* + +class Ignore(defaultIgnore: String) : AbstractCommand() { + private val keyword = "me" + + init { + if (defaultIgnore.isNotBlank()) { + ignored.addAll(defaultIgnore.split(", +?| +")) + } + } + + override val command = IGNORE + override val help = listOf( + Utils.bold("To check your ignore status:"), + Utils.helpIndent("%s: $command"), + Utils.bold("To toggle your ignore status:"), + Utils.helpIndent("%s: $command $keyword") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + companion object { + const val IGNORE = "ignore" + private val ignored = HashSet<String>() + + @JvmStatic + fun isNotIgnored(nick: String): Boolean { + return !ignored.contains(nick.toLowerCase()) + } + } + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + val nick = sender.toLowerCase() + val isMe = args.toLowerCase().startsWith(keyword) + if (isMe) { + if (ignored.remove(nick)) { + bot.send(sender, "You are no longer ignored.") + } else { + ignored.add(nick) + bot.send(sender, "You are now ignored.") + } + } else { + if (ignored.contains(nick)) { + bot.send(sender, "You are currently ignored.") + } else { + bot.send(sender, "You are not currently ignored.") + } + } + } else { + if (args.isNotEmpty()) { + val nicks = args.toLowerCase().split(" ") + for (nick in nicks) { + val ignore = if (keyword == nick) { + sender.toLowerCase() + } else { + nick + } + if (!ignored.remove(ignore)) { + ignored.add(ignore) + } + } + } + bot.send(sender, "The following nicks are ignored: ${ignored.joinToString(", ")}") + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt new file mode 100644 index 0000000..7fe62e4 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -0,0 +1,92 @@ +/* + * Info.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.links.UrlMgr +import org.jibble.pircbot.Colors +import java.lang.management.ManagementFactory + +class Info : AbstractCommand() { + override val command = "info" + override val help = listOf( + Utils.bold("To view information about the bot:"), + Utils.helpIndent("%s: $command") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + for (info in Mobibot.INFO) { + if (info.startsWith("https://")) { + bot.send(sender, info, Colors.DARK_GREEN, isPrivate) + } else { + bot.send(sender, info, isPrivate) + } + } + + val info = StringBuilder("Uptime: ") + + with(info) { + append(Utils.uptime(ManagementFactory.getRuntimeMXBean().uptime)) + append(" [Entries: ") + append(UrlMgr.entriesCount) + + if (isOp) { + if (bot.tell.isEnabled) { + append(", Messages: ") + append(bot.tell.size()) + } + if (bot.isTwitterAutoPost) { + append(", Twitter: ") + append(bot.twitterLinksCount()) + } + } + + append(", Recap: ") + append(Recap.recapCount()) + append(']') + } + + bot.send(sender, info.toString(), isPrivate) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt new file mode 100644 index 0000000..a229009 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt @@ -0,0 +1,62 @@ +/* + * Me.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils + +class Me : AbstractCommand() { + override val command = "me" + override val help = listOf( + Utils.bold("To have the bot perform an action:"), + Utils.helpIndent("/msg %s $command <action>") + ) + override val isOp = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + bot.action(args) + } else { + bot.helpDefault(sender, isOp) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt b/src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt new file mode 100644 index 0000000..79c6224 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt @@ -0,0 +1,64 @@ +/* + * AbstractCommandImpl.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils + +class MobibotVersion : AbstractCommand() { + override val command = "version" + override val help = listOf( + Utils.bold("To view the version data (bot, java, etc.):"), + Utils.helpIndent("/msg %s $command") + ) + override val isOp = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + for (v in Mobibot.MOBIBOT_VERSIONS) { + bot.send(sender, v, isPrivate) + } + } else { + bot.helpDefault(sender, isOp) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt new file mode 100644 index 0000000..de494a4 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -0,0 +1,68 @@ +/* + * Modules.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils + +class Modules : AbstractCommand() { + override val command = "modules" + override val help = listOf( + Utils.bold("To view a list of enabled modules:"), + Utils.helpIndent("/msg %s $command") + ) + override val isOp = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + val modulesNames = bot.modulesNames + if (modulesNames.isEmpty()) { + bot.send(sender, "There are no enabled modules.", true) + } else { + bot.send(sender, Utils.bold("The enabled modules are: ")) + bot.send(sender, Utils.helpIndent(modulesNames.joinToString(", "))) + } + } else { + bot.helpDefault(sender, isOp) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt new file mode 100644 index 0000000..5c01b83 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt @@ -0,0 +1,67 @@ +/* + * Msg.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils + +class Msg : AbstractCommand() { + override val command = "msg" + override val help = listOf( + Utils.bold("To have the bot send a private message to someone:"), + Utils.helpIndent("/msg %s $command <nick> <text>") + ) + override val isOp = true + override val isPublic = true + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + val msg = args.split(" ", limit = 2) + if (args.length > 2) { + bot.send(msg[0], msg[1], true) + } else { + helpResponse(bot, command, sender, isOp, isPrivate) + } + } else { + bot.helpDefault(sender, isOp) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt new file mode 100644 index 0000000..41396ed --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt @@ -0,0 +1,62 @@ +/* + * Nick.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils + +class Nick : AbstractCommand() { + override val command = "nick" + override val help = listOf( + Utils.bold("To change the bot's nickname:"), + Utils.helpIndent("/msg %s $command <nick>") + ) + override val isOp = true + override val isPublic = true + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + bot.changeNick(args) + } else { + bot.helpDefault(sender, isOp) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt new file mode 100644 index 0000000..2ffc255 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -0,0 +1,95 @@ +/* + * Recap.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import java.time.Clock +import java.time.LocalDateTime +import java.util.* + +class Recap : AbstractCommand() { + override val command = "recap" + override val help = listOf( + Utils.bold("To list the last 10 public channel messages:"), + Utils.helpIndent("%s: $command"), + Utils.helpIndent("/msg %s $command") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + companion object { + private val recaps = ArrayList<String>(0) + + @JvmStatic + fun recapCount(): Int { + return recaps.size + } + + /** + * Stores the last 10 public messages and actions. + * + * @param sender The nick of the person who sent the private message. + * @param message The actual message sent. + * @param isAction Set to `true` if the message is an action. + */ + @JvmStatic + fun storeRecap(sender: String, message: String, isAction: Boolean) { + recaps.add( + Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + + " -> ${sender}: " + (if (isAction) " " else ": ") + message + ) + if (recaps.size > 10) { + recaps.removeAt(0) + } + } + } + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (recaps.isNotEmpty()) { + for (r in recaps) { + bot.send(sender, r, isPrivate) + } + } else { + bot.send(sender, "Sorry, nothing to recap.", true) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt new file mode 100644 index 0000000..c0b1e79 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt @@ -0,0 +1,63 @@ +/* + * Say.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils + +class Say : AbstractCommand() { + override val command = "say" + override val help = listOf( + Utils.bold("To have the bot say something on the channel:"), + Utils.helpIndent("/msg %s $command <text>") + ) + override val isOp = true + override val isPublic = false + override val isVisible = true + + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + bot.send(bot.channel, args, true) + } else { + bot.helpDefault(sender, isOp) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt new file mode 100644 index 0000000..1f0f735 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -0,0 +1,71 @@ +/* + * Users.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import org.jibble.pircbot.User +import java.util.* + +class Users : AbstractCommand() { + override val command = "users" + override val help = listOf( + Utils.bold("To list the users present on the channel:"), + Utils.helpIndent("/msg %s $command") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + val users: Array<User> = bot.getUsers(bot.channel) + val nicks = ArrayList<String>() + users.forEach { user -> + if (bot.isOp(user.nick)) { + nicks.add('@'.toString() + user.nick) + } else { + nicks.add(user.nick) + } + } + + bot.send(sender, nicks.sorted().joinToString(" "), isPrivate) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt new file mode 100644 index 0000000..9e1138f --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -0,0 +1,130 @@ +/* + * Links.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntryLink + +class Comment : AbstractCommand() { + override val command = COMMAND + override val help = listOf( + Utils.bold("To add a comment:"), + Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", + Utils.bold("To edit a comment, use its label: "), + Utils.helpIndent("${Constants.LINK_CMD}1.1:This is an edited comment"), + Utils.bold("To delete a comment, use its label and a minus sign: "), + Utils.helpIndent("${Constants.LINK_CMD}1.1:-") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + companion object { + const val COMMAND = "comment" + } + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + val cmds = args.substring(1).split("[.:]".toRegex(), 3) + val index = cmds[0].toInt() - 1 + + if (index < UrlMgr.entriesCount) { + val entry: EntryLink = UrlMgr.getEntry(index) + val cindex = cmds[1].toInt() - 1 + if (cindex < entry.commentsCount) { + val cmd = cmds[2].trim() + + // L1.1: + if (cmd.isEmpty()) { + val comment = entry.getComment(cindex) + bot.send(bot.channel, EntriesUtils.buildComment(index, cindex, comment)) + } else if ("-" == cmd) { // L11:- + entry.deleteComment(cindex) + bot.send( + bot.channel, + "Comment ${Constants.LINK_CMD}${index + 1}.${cindex + 1} removed." + ) + UrlMgr.saveEntries(bot, false) + } else if (cmd[0] == '?') { // L1.1:?<author> + if (isOp) { + if (cmd.length > 1) { + val comment = entry.getComment(cindex) + comment.nick = cmd.substring(1) + bot.send(bot.channel, EntriesUtils.buildComment(index, cindex, comment)) + UrlMgr.saveEntries(bot, false) + } + } else { + bot.send(sender, "Please ask a channel op to change the author of this comment for you.") + } + } else { + entry.setComment(cindex, cmd, sender) + val comment = entry.getComment(cindex) + bot.send(sender, EntriesUtils.buildComment(index, cindex, comment)) + UrlMgr.saveEntries(bot, false) + } + } + } + } + + override fun helpResponse( + bot: Mobibot, + command: String, + sender: String, + isOp: Boolean, + isPrivate: Boolean + ): Boolean { + if (super.helpResponse(bot, command, sender, isOp, isPrivate)) { + if (isOp) { + bot.send(sender, Utils.bold("To change a comment's author:"), isPrivate) + bot.send(sender, Utils.helpIndent("/msg ${bot.nick} ${Constants.LINK_CMD}1.1:?<nick>"), isPrivate) + } + return true + } + return false + } + + override fun matches(message: String): Boolean { + return message.matches("^${Constants.LINK_CMD}[0-9]+\\.[0-9]+:.*".toRegex()) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt new file mode 100644 index 0000000..6d05a0e --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -0,0 +1,147 @@ +/* + * Links.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntryLink + +class Posting : AbstractCommand() { + override val command = "posting" + override val help = listOf( + Utils.bold("Post a URL, by saying it on a line on its own:"), + Utils.helpIndent("<url> [<title>] ${Tags.COMMAND}}: <+tag> [...]]"), + "I will reply with a label, for example:" + Utils.bold("${Constants.LINK_CMD}1"), + Utils.bold("To add a title, use its label and a pipe:"), + Utils.helpIndent("${Constants.LINK_CMD}1:|This is the title"), + Utils.bold("To add a comment:"), + Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", + Utils.bold("To edit a comment, see: "), + Utils.helpIndent("/msg %s ${Constants.HELP_CMD} ${Comment.COMMAND}") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + val cmds = args.substring(1).split(":", limit = 2) + val index = cmds[0].toInt() - 1 + + if (index < UrlMgr.entriesCount) { + val cmd = cmds[1].trim() + if (cmd.isEmpty()) { + val entry: EntryLink = UrlMgr.getEntry(index) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + if (entry.hasTags()) { + bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) + } + if (entry.hasComments()) { + val comments = entry.comments + for (i in comments.indices) { + bot.send(bot.channel, EntriesUtils.buildComment(index, i, comments[i])) + } + } + } else { + // L1:- + if ("-" == cmd) { + val entry: EntryLink = UrlMgr.getEntry(index) + if (entry.login == login || isOp) { + bot.deletePin(entry) + if (bot.isTwitterAutoPost) { + bot.twitterRemoveEntry(index) + } + UrlMgr.removeEntry(index) + bot.send(bot.channel, "Entry ${Constants.LINK_CMD}${index + 1} removed.") + UrlMgr.saveEntries(bot, false) + } else { + bot.send(sender, "Please ask a channel op to remove this entry for you.") + } + } else if (cmd[0] == '|') { // L1:|<title> + if (cmd.length > 1) { + val entry: EntryLink = UrlMgr.getEntry(index) + entry.title = cmd.substring(1).trim() + bot.updatePin(entry.link, entry) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + UrlMgr.saveEntries(bot, false) + } + } else if (cmd[0] == '=') { // L1:=<url> + val entry: EntryLink = UrlMgr.getEntry(index) + if (entry.login == login || isOp) { + val link = cmd.substring(1) + if (link.matches(UrlMgr.LINK_MATCH.toRegex())) { + val oldLink = entry.link + entry.link = link + bot.updatePin(oldLink, entry) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + UrlMgr.saveEntries(bot, false) + } + } else { + bot.send(sender, "Please ask channel op to change this link for you.") + } + } else if (cmd[0] == '?') { // L1:?<author> + if (isOp) { + if (cmd.length > 1) { + val entry: EntryLink = UrlMgr.getEntry(index) + entry.nick = cmd.substring(1) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + UrlMgr.saveEntries(bot, false) + } + } else { + bot.send(sender, "Please ask a channel op to change the author of this link for you.") + } + } else { + val entry: EntryLink = UrlMgr.getEntry(index) + val cindex = entry.addComment(cmd, sender) + val comment = entry.getComment(cindex) + bot.send(sender, EntriesUtils.buildComment(index, cindex, comment)) + UrlMgr.saveEntries(bot, false) + } + } + } + } + + override fun matches(message: String): Boolean { + return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex()) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt new file mode 100644 index 0000000..204f41f --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -0,0 +1,92 @@ +/* + * Links.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntryLink + +class Tags : AbstractCommand() { + override val command = COMMAND + override val help = listOf( + Utils.bold("To categorize or tag a URL, use its label and a T:"), + Utils.helpIndent("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + companion object { + const val COMMAND = "tags" + } + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + val cmds = args.substring(1).split("T:", limit = 2) + val index = cmds[0].toInt() - 1 + + if (index < UrlMgr.entriesCount) { + val cmd = cmds[1].trim() + val entry: EntryLink = UrlMgr.getEntry(index) + if (cmd.isNotEmpty()) { + if (entry.login == login || isOp) { + entry.setTags(cmd) + bot.updatePin(entry.link, entry) + bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) + UrlMgr.saveEntries(bot, false) + } else { + bot.send(sender, "Please ask a channel op to change the tags for you.") + } + } else { + if (entry.hasTags()) { + bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) + } else { + bot.send(sender, "The entry has no tags. Why don't add some?") + } + } + } + } + + override fun matches(message: String): Boolean { + return message.matches("^${Constants.LINK_CMD}[0-9]+T:.*".toRegex()) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt new file mode 100644 index 0000000..c1a3b37 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -0,0 +1,243 @@ +/* + * Links.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.TwitterTimer +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.Ignore +import net.thauvin.erik.mobibot.entries.EntriesMgr +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntryLink +import org.jsoup.Jsoup +import java.io.IOException +import java.util.* + +class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { + private val tagsKeywords = ArrayList<String>() + override val command = Constants.LINK_CMD + override val help = emptyList<String>() + override val isOp = false + override val isPublic = false + override val isVisible = false + + init { + tagsKeywords.addAll(keywords.split(", +?| +")) + Companion.defaultTags = defaultTags + } + + companion object { + const val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*" + + // Entries array + private val entries = ArrayList<EntryLink>(0) + + // History/backlogs array + private val history = ArrayList<String>(0) + private lateinit var defaultTags: String + + @JvmStatic + val entriesCount + get() = entries.size + + @JvmStatic + var today: String = Utils.today() + private set + + @JvmStatic + fun addHistory(index: Int, entry: String) { + history.add(index, entry) + } + + /** + * Saves the entries. + * + * @param isDayBackup Set the `true` if the daily backup file should also be created. + */ + @JvmStatic + fun saveEntries(bot: Mobibot, isDayBackup: Boolean) { + EntriesMgr.saveEntries(bot, entries, history, isDayBackup) + } + + @JvmStatic + fun removeEntry(index: Int) { + entries.removeAt(index) + } + + @JvmStatic + fun getEntry(index: Int): EntryLink { + return entries[index] + } + + @JvmStatic + fun getHistory(): List<String> { + return history + } + + @JvmStatic + fun startup(current: String, backlogs: String, channel: String) { + today = EntriesMgr.loadEntries(current, channel, entries) + if (Utils.today() != today) { + this.entries.clear() + today = Utils.today() + } + EntriesMgr.loadBacklogs(backlogs, history) + } + } + + @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD") + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + val cmds = args.split(" ".toRegex(), 2) + + if (Ignore.isNotIgnored(sender) && (cmds.size == 1 + || !cmds[1].contains(bot.nick) + || !cmds[1].endsWith(" (${Ignore.IGNORE}"))) { + val link = cmds[0].trim() + var isBackup = false + val dupIndex: Int = findDupEntry(link) + if (dupIndex == -1) { + if (Utils.today() != today) { + isBackup = true + saveEntries(bot, true) + entries.clear() + today = Utils.today() + } + val tags: StringBuilder = StringBuilder(defaultTags) + var title = Constants.NO_TITLE + if (cmds.size == 2) { + val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) + if (data.size == 1) { + title = data[0].trim() + } else { + if (data[0].isNotBlank()) { + title = data[0].trim() + } + tags.append(' ').append(data[1].trim()) + } + } + if (Constants.NO_TITLE == title) { + try { + val html = Jsoup.connect(link).userAgent("Mozilla").get() + val htmlTitle = html.title() + if (htmlTitle.isNotBlank()) { + val split = htmlTitle.split("( \\| )".toRegex(), 2) + title = if (split.size == 2) { + split[0] + } else { + htmlTitle + } + } + } catch (ignore: IOException) { + // Do nothing + } + } + if (tagsKeywords.isNotEmpty()) { + for (match in tagsKeywords) { + val m = match.trim() + if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { + tags.append(' ').append(m) + } + } + } + entries.add(EntryLink(link, title, sender, login, bot.channel, tags.toString())) + val index: Int = entries.size - 1 + val entry: EntryLink = entries[index] + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + + // Add Entry to pinboard. + bot.addPin(entry) + + // Queue link for posting to twitter + if (bot.isTwitterAutoPost) { + bot.twitterAddEntry(index) + Mobibot.timer.schedule( + TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L + ) + } + saveEntries(bot, isBackup) + if (Constants.NO_TITLE == entry.title) { + bot.send(sender, Utils.bold("Please specify a title, by typing:"), true) + bot.send( + sender, + Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title"), + true + ) + } + } else { + val entry: EntryLink = entries[dupIndex] + bot.send( + sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry) + ) + } + } + } + + override fun helpResponse( + bot: Mobibot, + command: String, + sender: String, + isOp: Boolean, + isPrivate: Boolean + ): Boolean = false + + /** + * Returns the index of the specified duplicate entry, if any. + * + * @param link The link. + * @return The index or -1 if none. + */ + private fun findDupEntry(link: String): Int { + synchronized(entries) { + for (i in entries.indices) { + if (link == entries[i].link) { + return i + } + } + } + return -1 + } + + override fun matches(message: String): Boolean { + return message.matches(LINK_MATCH.toRegex()) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt new file mode 100644 index 0000000..e45885d --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -0,0 +1,130 @@ +/* + * Links.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.entriesCount +import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.getEntry +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntryLink + +class View : AbstractCommand() { + private val maxEntries = 8 + override val command = VIEW_CMD + override val help = listOf( + Utils.bold("To list or search the current URL posts:"), + Utils.helpIndent("%s: $command [<start>] [<query>]") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + companion object { + const val VIEW_CMD = "view" + } + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (entriesCount != 0) { + val max = entriesCount + var lcArgs = args.toLowerCase(Constants.LOCALE) + var i = 0 + if (lcArgs.isEmpty() && max > maxEntries) { + i = max - maxEntries + } + if (lcArgs.matches("^\\d+(| .*)".toRegex())) { + val split = lcArgs.split(" ", limit = 2) + try { + i = split[0].toInt() + if (i > 0) { + i-- + } + lcArgs = if (split.size == 2) { + split[1].trim() + } else { + "" + } + if (i > max) { + i = 0 + } + } catch (ignore: NumberFormatException) { + // Do nothing + } + } + var entry: EntryLink + var sent = 0 + while (i < max) { + entry = getEntry(i) + if (lcArgs.isNotEmpty()) { + if (entry.link.toLowerCase().contains(lcArgs) + || entry.title.toLowerCase().contains(lcArgs) + || entry.nick.toLowerCase().contains(lcArgs)) { + if (sent > maxEntries) { + bot.send( + sender, "To view more, try: " + + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), + isPrivate + ) + break + } + bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) + sent++ + } + } else { + if (sent > maxEntries) { + bot.send( + sender, + "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}"), + isPrivate + ) + break + } + bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) + sent++ + } + i++ + } + } else { + bot.send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java similarity index 75% rename from src/main/java/net/thauvin/erik/mobibot/tell/Tell.java rename to src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index 9692505..e8cee58 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -30,12 +30,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot.tell; +package net.thauvin.erik.mobibot.commands.tell; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Commands; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.commands.links.View; import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -112,16 +112,16 @@ public class Tell { } /** - * Responds with help. + * Responds with Constants. * * @param sender The sender. */ public void helpResponse(final String sender) { bot.send(sender, "To send a message to someone when they join the channel:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>")); bot.send(sender, "To view queued and sent messages:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD)); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + View.VIEW_CMD)); bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); } @@ -141,19 +141,20 @@ public class Tell { * @param sender The sender's nick. * @param cmds The commands string. */ - @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.") + @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", + justification = "Working on it.") public void response(final String sender, final String cmds) { final String arrow = " --> "; if (StringUtils.isBlank(cmds)) { helpResponse(sender); - } else if (cmds.startsWith(Commands.VIEW_CMD)) { - if (bot.isOp(sender) && (Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) { + } else if (cmds.startsWith(View.VIEW_CMD)) { + if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) { if (!messages.isEmpty()) { for (final TellMessage message : messages) { bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) - + " [ID: " + message.getId() + ", " - + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - true); + + " [ID: " + message.getId() + ", " + + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', + true); } } else { bot.send(sender, "There are no messages in the queue.", true); @@ -170,20 +171,20 @@ public class Tell { if (message.isReceived()) { bot.send(sender, - Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " - + message.getId() + ", DELIVERED]", - true); + Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " + + message.getId() + ", DELIVERED]", + true); } else { bot.send(sender, - Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " - + message.getId() + ", QUEUED]", - true); + Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " + + message.getId() + ", QUEUED]", + true); } - bot.send(sender, bot.helpIndent(message.getMessage(), false), true); + bot.send(sender, Utils.helpIndent(message.getMessage()), true); } } @@ -192,10 +193,10 @@ public class Tell { } else { bot.send(sender, "To delete one or all delivered messages:"); bot.send(sender, - bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" - + TELL_ALL_KEYWORD + '>')); + Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + + TELL_ALL_KEYWORD + '>')); bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) - + Utils.plural(maxDays, " day.", " days.")); + + Utils.plural(maxDays, " day.", " days.")); } } } else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) { @@ -259,7 +260,7 @@ public class Tell { save(); bot.send(sender, "Message [ID " + message.getId() + "] was queued for " - + Utils.bold(message.getRecipient()), true); + + Utils.bold(message.getRecipient()), true); } else { bot.send(sender, "Sorry, the messages queue is currently full.", true); } @@ -289,43 +290,42 @@ public class Tell { */ public void send(final String nickname, final boolean isMessage) { if (!nickname.equals(bot.getNick()) && isEnabled()) { - messages.stream().filter(message -> message.isMatch(nickname)).forEach( - message -> { - if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { - if (message.getSender().equals(nickname)) { - if (!isMessage) { - bot.send(nickname, Utils.bold("You") + " wanted me to remind you: " - + Utils.reverseColor(message.getMessage()), - true); - - message.setIsReceived(); - message.setIsNotified(); - - save(); - } - } else { - bot.send(nickname, message.getSender() + " wanted me to tell you: " - + Utils.reverseColor(message.getMessage()), - true); + messages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> { + if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { + if (message.getSender().equals(nickname)) { + if (!isMessage) { + bot.send(nickname, Utils.bold("You") + " wanted me to remind you: " + + Utils.reverseColor(message.getMessage()), + true); message.setIsReceived(); + message.setIsNotified(); save(); } - } else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() - && !message.isNotified()) { - bot.send(nickname, - "Your message " - + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " - + Utils.bold(message.getRecipient()) + " on " - + Utils.utcDateTime(message.getReceived()), - true); + } else { + bot.send(nickname, message.getSender() + " wanted me to tell you: " + + Utils.reverseColor(message.getMessage()), + true); - message.setIsNotified(); + message.setIsReceived(); save(); } - }); + } else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() + && !message.isNotified()) { + bot.send(nickname, + "Your message " + + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + + Utils.bold(message.getRecipient()) + " on " + + Utils.utcDateTime(message.getReceived()), + true); + + message.setIsNotified(); + + save(); + } + }); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java similarity index 99% rename from src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java rename to src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java index 9d39e22..89dafab 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot.tell; +package net.thauvin.erik.mobibot.commands.tell; import java.io.Serializable; import java.time.Clock; diff --git a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java similarity index 99% rename from src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java rename to src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java index ad6cb0a..c03af95 100644 --- a/src/main/java/net/thauvin/erik/mobibot/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot.tell; +package net.thauvin.erik.mobibot.commands.tell; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java index 636fbe4..f0485db 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.entries; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Commands; import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Utils; @@ -61,8 +60,8 @@ public final class EntriesUtils { * @return The entry's comment. */ public static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) { - return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " - + comment.getComment()); + return (Constants.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " + + comment.getComment()); } /** @@ -87,8 +86,8 @@ public final class EntriesUtils { */ @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.") public static String buildLink(final int index, final EntryLink entry, final boolean isView) { - final StringBuilder buff = new StringBuilder().append(Commands.LINK_CMD).append(index + 1) - .append(": ").append('[').append(entry.getNick()).append(']'); + final StringBuilder buff = new StringBuilder().append(Constants.LINK_CMD).append(index + 1) + .append(": ").append('[').append(entry.getNick()).append(']'); if (isView && entry.hasComments()) { buff.append("[+").append(entry.getCommentsCount()).append(']'); @@ -115,6 +114,6 @@ public final class EntriesUtils { * @return The entry's tags. */ public static String buildTags(final int entryIndex, final EntryLink entry) { - return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", ")); + return (Constants.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", ")); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 2d5409d..671db96 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -95,7 +95,7 @@ public abstract class AbstractModule { } /** - * Responds with the module's help. + * Responds with the module's Constants. * * @param bot The bot's instance. * @param sender The sender. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 6d4a304..8201c8b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -35,10 +35,13 @@ package net.thauvin.erik.mobibot.modules; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import org.apache.commons.lang3.StringUtils; import java.text.DecimalFormat; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The Calc module. * @@ -88,7 +91,6 @@ public class Calc extends AbstractModule { final boolean isPrivate) { if (StringUtils.isNotBlank(args)) { bot.send(calc(args)); - } else { helpResponse(bot, sender, args, isPrivate); } @@ -99,7 +101,7 @@ public class Calc extends AbstractModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To solve a mathematical calculation:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>")); + bot.send(sender, bold("To solve a mathematical calculation:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>")); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index a43f241..c66f968 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -56,6 +56,8 @@ import java.util.Locale; import java.util.Map; import java.util.TreeMap; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The CurrentConverter module. * @@ -228,14 +230,14 @@ public final class CurrencyConverter extends ThreadedModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To convert from one currency to another:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); + bot.send(sender, bold("To convert from one currency to another:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); if (args.endsWith(CURRENCY_CMD)) { - bot.send(sender, "For a listing of currency rates:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); - bot.send(sender, "For a listing of supported currencies:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); + bot.send(sender, bold("For a listing of currency rates:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); + bot.send(sender, bold("For a listing of supported currencies:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 62b750c..4388724 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -37,6 +37,8 @@ import net.thauvin.erik.mobibot.Utils; import java.security.SecureRandom; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The Dice module. * @@ -72,15 +74,15 @@ public final class Dice extends AbstractModule { final int playerTotal = i + y; bot.send(bot.getChannel(), - sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " - + Utils.bold(playerTotal)); + sender + " rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " + + bold(playerTotal)); i = r.nextInt(6) + 1; y = r.nextInt(6) + 1; final int total = i + y; bot.action( - "rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total)); + "rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " + bold(total)); if (playerTotal < total) { bot.action("wins."); @@ -96,7 +98,7 @@ public final class Dice extends AbstractModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To roll the dice:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD)); + bot.send(sender, bold("To roll the dice:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + DICE_CMD)); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index e180fb5..f40b5af 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -52,6 +52,8 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The GoogleSearch module. * @@ -79,14 +81,74 @@ public final class GoogleSearch extends ThreadedModule { properties.put(GOOGLE_CSE_KEY_PROP, ""); } + /** + * Performs a search on Google. + * + * @param query The search query. + * @param apiKey The Google API key. + * @param cseKey The Google CSE key. + * @return The {@link Message} array containing the search results. + * @throws ModuleException If an error occurs while searching. + */ + @SuppressFBWarnings({ "URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION" }) + @SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops")) + static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey) + throws ModuleException { + if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) { + throw new ModuleException(StringUtils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing."); + } + + if (StringUtils.isNotBlank(query)) { + final ArrayList<Message> results = new ArrayList<>(); + try { + final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); + + final URL url = + new URL("https://www.googleapis.com/customsearch/v1?key=" + + apiKey + + "&cx=" + + cseKey + + "&q=" + + q + + "&filter=1&num=5&alt=json"); + final URLConnection conn = url.openConnection(); + + final StringBuilder sb = new StringBuilder(); + try (final BufferedReader reader = + new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + + final JSONObject json = new JSONObject(sb.toString()); + final JSONArray ja = json.getJSONArray("items"); + + for (int i = 0; i < ja.length(); i++) { + final JSONObject j = ja.getJSONObject(i); + results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); + results.add( + new NoticeMessage(TAB_INDENT + j.getString("link"), Colors.DARK_GREEN)); + } + } + } catch (IOException e) { + throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e); + } + + return results; + } else { + throw new ModuleException("Invalid query."); + } + } + /** * {@inheritDoc} */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (isEnabled()) { - bot.send(sender, "To search Google:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); + bot.send(sender, bold("To search Google:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); } else { bot.send(sender, "The Google search module is disabled."); } @@ -100,7 +162,7 @@ public final class GoogleSearch extends ThreadedModule { if (StringUtils.isNotBlank(query)) { try { final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), - properties.get(GOOGLE_CSE_KEY_PROP)); + properties.get(GOOGLE_CSE_KEY_PROP)); for (final Message msg : results) { bot.send(sender, msg); } @@ -112,64 +174,4 @@ public final class GoogleSearch extends ThreadedModule { helpResponse(bot, sender, query, true); } } - - /** - * Performs a search on Google. - * - * @param query The search query. - * @param apiKey The Google API key. - * @param cseKey The Google CSE key. - * @return The {@link Message} array containing the search results. - * @throws ModuleException If an error occurs while searching. - */ - @SuppressFBWarnings({"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"}) - @SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops")) - static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey) - throws ModuleException { - if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) { - throw new ModuleException(StringUtils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing."); - } - - if (StringUtils.isNotBlank(query)) { - final ArrayList<Message> results = new ArrayList<>(); - try { - final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); - - final URL url = - new URL("https://www.googleapis.com/customsearch/v1?key=" - + apiKey - + "&cx=" - + cseKey - + "&q=" - + q - + "&filter=1&num=5&alt=json"); - final URLConnection conn = url.openConnection(); - - final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = - new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - } - - final JSONObject json = new JSONObject(sb.toString()); - final JSONArray ja = json.getJSONArray("items"); - - for (int i = 0; i < ja.length(); i++) { - final JSONObject j = ja.getJSONObject(i); - results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); - results.add( - new NoticeMessage(TAB_INDENT + j.getString("link"), Colors.DARK_GREEN)); - } - } - } catch (IOException e) { - throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e); - } - - return results; - } else { - throw new ModuleException("Invalid query."); - } - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 2210baf..199e4e7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -44,6 +44,8 @@ import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The Joke module. * @@ -126,7 +128,7 @@ public final class Joke extends ThreadedModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To retrieve a random joke:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD)); + bot.send(sender, bold("To retrieve a random joke:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + JOKE_CMD)); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index a3b808f..676ebbe 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -34,12 +34,15 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import org.apache.commons.net.whois.WhoisClient; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The Lookup module. * @@ -195,7 +198,7 @@ public final class Lookup extends AbstractModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To perform a DNS lookup query:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>")); + bot.send(sender, bold("To perform a DNS lookup query:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>")); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 214a1ed..4e689a8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -33,11 +33,14 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import java.security.SecureRandom; import java.util.Arrays; import java.util.List; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The Ping module. * @@ -94,7 +97,7 @@ public class Ping extends AbstractModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To ping the bot:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + PING_CMD)); + bot.send(sender, bold("To ping the bot:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + PING_CMD)); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 903bf90..a3de8e5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -33,7 +33,10 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpIndent +import net.thauvin.erik.mobibot.Utils.red import kotlin.random.Random @@ -71,7 +74,7 @@ class RockPaperScissors : AbstractModule() { companion object { // For testing. - fun winLoseOrDraw(player: String, bot: String ): String { + fun winLoseOrDraw(player: String, bot: String): String { val hand = Hands.valueOf(player.toUpperCase()) val botHand = Hands.valueOf(bot.toUpperCase()) @@ -88,26 +91,26 @@ class RockPaperScissors : AbstractModule() { val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] when { hand == botHand -> { - bot.action("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)} ~ The game is tied ~") + bot.action("${green(hand.name)} vs. ${green(botHand.name)} ~ The game is tied ~") } hand.beats(botHand) -> { bot.action( - "${Utils.green(hand.name)} ${Utils.bold(hand.action)} ${Utils.red(botHand.name)} ~ You win ~" + "${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)} ~ You win ~" ) } else -> { bot.action( - "${Utils.green(botHand.name)} ${Utils.bold(botHand.action)} ${Utils.red(botHand.name)} ~ You lose ~" + "${green(botHand.name)} ${bold(botHand.action)} ${red(botHand.name)} ~ You lose ~" ) } } } override fun helpResponse(bot: Mobibot, sender: String, args: String?, isPrivate: Boolean) { - bot.send(sender, "To play Rock Paper Scissors:") + bot.send(sender, bold("To play Rock Paper Scissors:")) bot.send( sender, - bot.helpIndent( + helpIndent( "${bot.nick}: ${Hands.ROCK.name.toLowerCase()} or ${Hands.PAPER.name.toLowerCase()}" + " or ${Hands.SCISSORS.name.toLowerCase()}" ) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 42c5057..72e1517 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -52,6 +52,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The StockQuote module. * @@ -82,9 +84,10 @@ public final class StockQuote extends ThreadedModule { properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); } - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?") + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", + justification = "false positive?") private static JSONObject getJsonResponse(final Response response, final String debugMessage) - throws IOException, ModuleException { + throws IOException, ModuleException { if (response.isSuccessful()) { if (response.body() != null) { final JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string()); @@ -132,7 +135,7 @@ public final class StockQuote extends ThreadedModule { * @return The {@link Message} array containing the stock quote. * @throws ModuleException If an errors occurs. */ - @SuppressWarnings({"PMD.CloseResource"}) + @SuppressWarnings({ "PMD.CloseResource" }) static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { if (StringUtils.isBlank(apiKey)) { throw new ModuleException(StringUtils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); @@ -146,7 +149,7 @@ public final class StockQuote extends ThreadedModule { try { // Search for symbol/keywords Request request = new Request.Builder().url( - ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build(); + ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build(); Response response = client.newCall(request).execute(); JSONObject json = getJsonResponse(response, debugMessage); @@ -161,8 +164,8 @@ public final class StockQuote extends ThreadedModule { // Get quote for symbol request = new Request.Builder().url( - ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" - + apiKey).build(); + ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" + + apiKey).build(); response = client.newCall(request).execute(); json = getJsonResponse(response, debugMessage); @@ -175,20 +178,21 @@ public final class StockQuote extends ThreadedModule { } messages.add(new PublicMessage( - "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils - .unescapeXml(symbolInfo.getString("2. name") + ']'))); + "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils.unescapeXml( + symbolInfo.getString("2. name") + ']'))); messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); - messages.add( - new PublicMessage(" Previous: " + Utils.unescapeXml(quote.getString("08. previous close")))); + messages.add(new PublicMessage( + " Previous: " + + Utils.unescapeXml(quote.getString("08. previous close")))); messages.add(new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); messages.add(new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); messages.add(new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); messages.add(new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); messages.add(new NoticeMessage( - " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); + " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); messages.add(new NoticeMessage( - " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils - .unescapeXml(quote.getString("10. change percent")) + ']')); + " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils.unescapeXml( + quote.getString("10. change percent")) + ']')); } catch (IOException | NullPointerException e) { throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); } @@ -203,8 +207,8 @@ public final class StockQuote extends ThreadedModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To retrieve a stock quote:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>")); + bot.send(sender, bold("To retrieve a stock quote:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>")); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 1b7af6e..f3ed606 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; import twitter4j.DirectMessage; @@ -40,6 +41,8 @@ import twitter4j.Status; import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The Twitter module. * @@ -115,8 +118,8 @@ public final class Twitter extends ThreadedModule { @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { if (isEnabled()) { - bot.send(sender, "To post to Twitter:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>")); + bot.send(sender, bold("To post to Twitter:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>")); } else { bot.send(sender, "The Twitter posting facility is disabled."); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 115e6ef..9d6087e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -37,6 +37,8 @@ import net.thauvin.erik.mobibot.Utils; import java.security.SecureRandom; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The War module. * @@ -80,14 +82,14 @@ public final class War extends AbstractModule { y = r.nextInt(WAR_DECK.length); bot.send(bot.getChannel(), - sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + sender + " drew the " + bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + bot.action("drew the " + bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); if (i != y) { break; } - bot.send("This means " + Utils.bold("WAR") + '!'); + bot.send("This means " + bold("WAR") + '!'); } if (i < y) { @@ -102,7 +104,7 @@ public final class War extends AbstractModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To play war:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WAR_CMD)); + bot.send(sender, bold("To play war:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WAR_CMD)); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 5f1a376..53822e1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -52,6 +52,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The <code>Weather2</code> module. * @@ -177,7 +179,7 @@ public class Weather2 extends ThreadedModule { Colors.GREEN)); } else { final HttpUrl url = Objects.requireNonNull(HttpUrl.parse( - "https://openweathermap.org/find")) + "https://openweathermap.org/find")) .newBuilder() .addQueryParameter("q", city + ',' + country) @@ -199,17 +201,22 @@ public class Weather2 extends ThreadedModule { return messages; } + private static String wind(final Double w) { + final double kmh = w * 1.60934; + return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; + } + /** * {@inheritDoc} */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To display weather information:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); + bot.send(sender, bold("To display weather information:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); bot.send(sender, "For example:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); bot.send(sender, - "The default ISO 3166 country code is " + Utils.bold("US") + "The default ISO 3166 country code is " + bold("US") + ". Zip codes are supported in most countries."); } @@ -236,9 +243,4 @@ public class Weather2 extends ThreadedModule { helpResponse(bot, sender, args, true); } } - - private static String wind(final Double w) { - final double kmh = w * 1.60934; - return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 7bbc09d..a19f33e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -35,6 +35,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.PublicMessage; @@ -47,6 +48,8 @@ import java.util.Collections; import java.util.Map; import java.util.TreeMap; +import static net.thauvin.erik.mobibot.Utils.bold; + /** * The WorldTime module. * @@ -232,11 +235,11 @@ public final class WorldTime extends AbstractModule { */ @Override public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To display a country's current date/time:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); + bot.send(sender, bold("To display a country's current date/time:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); - bot.send(sender, "For a listing of the supported countries:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD)); + bot.send(sender, bold("For a listing of the supported countries:")); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD)); } /** diff --git a/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.java similarity index 98% rename from src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java rename to src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.java index 04927a8..e57c52a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/tell/TellMessageTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.java @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot.tell; +package net.thauvin.erik.mobibot.commands.tell; import org.testng.annotations.Test; diff --git a/version.properties b/version.properties index aa42206..30605d4 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Mar 23 23:04:40 PDT 2020 -version.buildmeta=714 +#Sat Mar 28 10:22:37 PDT 2020 +version.buildmeta=045 version.major=0 -version.minor=7 -version.patch=3 -version.prerelease=beta +version.minor=8 +version.patch=0 +version.prerelease=alpha version.project=mobibot -version.semver=0.7.3-beta+714 +version.semver=0.8.0-alpha+045 From f7e26516196ee63bf161f18d1e44d47f7c125269 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 13:15:28 -0700 Subject: [PATCH 352/842] Reworked versions command. --- .../net/thauvin/erik/mobibot/Mobibot.java | 21 ++-- .../net/thauvin/erik/mobibot/commands/Info.kt | 4 - .../erik/mobibot/commands/MobibotVersion.kt | 64 ------------ .../erik/mobibot/commands/Versions.java | 98 +++++++++++++++++++ 4 files changed, 104 insertions(+), 83 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Versions.java diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index e695d4d..f68e272 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -38,13 +38,13 @@ import net.thauvin.erik.mobibot.commands.Cycle; import net.thauvin.erik.mobibot.commands.Ignore; import net.thauvin.erik.mobibot.commands.Info; import net.thauvin.erik.mobibot.commands.Me; -import net.thauvin.erik.mobibot.commands.MobibotVersion; import net.thauvin.erik.mobibot.commands.Modules; import net.thauvin.erik.mobibot.commands.Msg; import net.thauvin.erik.mobibot.commands.Nick; import net.thauvin.erik.mobibot.commands.Recap; import net.thauvin.erik.mobibot.commands.Say; import net.thauvin.erik.mobibot.commands.Users; +import net.thauvin.erik.mobibot.commands.Versions; import net.thauvin.erik.mobibot.commands.links.Comment; import net.thauvin.erik.mobibot.commands.links.Posting; import net.thauvin.erik.mobibot.commands.links.Tags; @@ -117,19 +117,10 @@ public class Mobibot extends PircBot { // Info strings @SuppressWarnings("indentation") public static final List<String> INFO = - List.of(ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)", - "https://www.mobitopia.org/mobibot/"); - // Version strings - @SuppressWarnings("indentation") - public static final List<String> MOBIBOT_VERSIONS = - List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', - "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " - + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty( - "java.runtime.version") + ')', - "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") - + ", " - + System.getProperty("java.vm.info") + ')'); + List.of( + ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + + " (" + Utils.green("https://www.mobitopia.org/mobibot/") + ')', + "Written by Erik C. Thauvin (" + Utils.green("https://erik.thauvin.net/") + ')'); // Timer public static final Timer timer = new Timer(true); // Default port @@ -253,13 +244,13 @@ public class Mobibot extends PircBot { commands.add(ignoreCommand); commands.add(new Info()); commands.add(new Me()); - commands.add(new MobibotVersion()); commands.add(new Modules()); commands.add(new Msg()); commands.add(new Nick()); commands.add(new Recap()); commands.add(new Say()); commands.add(new Users()); + commands.add(new Versions()); // Load the links commands commands.add(new Comment()); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt index 7fe62e4..b1ad2ac 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -57,11 +57,7 @@ class Info : AbstractCommand() { isPrivate: Boolean ) { for (info in Mobibot.INFO) { - if (info.startsWith("https://")) { - bot.send(sender, info, Colors.DARK_GREEN, isPrivate) - } else { bot.send(sender, info, isPrivate) - } } val info = StringBuilder("Uptime: ") diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt b/src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt deleted file mode 100644 index 79c6224..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/commands/MobibotVersion.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * AbstractCommandImpl.kt - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils - -class MobibotVersion : AbstractCommand() { - override val command = "version" - override val help = listOf( - Utils.bold("To view the version data (bot, java, etc.):"), - Utils.helpIndent("/msg %s $command") - ) - override val isOp = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse( - bot: Mobibot, - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - for (v in Mobibot.MOBIBOT_VERSIONS) { - bot.send(sender, v, isPrivate) - } - } else { - bot.helpDefault(sender, isOp) - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java new file mode 100644 index 0000000..064f5e9 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -0,0 +1,98 @@ +/* + * Version.java + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands; + +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.ReleaseInfo; +import net.thauvin.erik.mobibot.Utils; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class Versions extends AbstractCommand { + private final List<String> versions = + List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty( + "java.runtime.version") + ')', + "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + + ", " + + System.getProperty("java.vm.info") + ')'); + + @NotNull + @Override + public String getCommand() { + return "versions"; + } + + + @NotNull + @Override + public List<String> getHelp() { + return List.of(Utils.bold("To view the versions data (bot, java, etc.):"), + Utils.helpIndent("/msg %s $command")); + } + + @Override + public boolean isOp() { + return true; + } + + @Override + public boolean isPublic() { + return false; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public void commandResponse(@NotNull final Mobibot bot, + @NotNull final String sender, + @NotNull final String login, + @NotNull final String args, + final boolean isOp, + final boolean isPrivate) { + if (isOp) { + for (final String v : versions) { + bot.send(sender, v, isPrivate); + } + } else { + bot.helpDefault(sender, isOp); + } + + } +} From 9b2dc8587f3a072748974ca61f47ff978e615995 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 13:17:07 -0700 Subject: [PATCH 353/842] detekt & spotbugs cleanup. --- config/spotbugs/excludeFilter.xml | 5 +++-- detekt-baseline.xml | 13 ++++++++----- .../net/thauvin/erik/mobibot/commands/Ignore.kt | 4 ++-- .../thauvin/erik/mobibot/commands/links/UrlMgr.kt | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/config/spotbugs/excludeFilter.xml b/config/spotbugs/excludeFilter.xml index 49bdd81..21512f0 100644 --- a/config/spotbugs/excludeFilter.xml +++ b/config/spotbugs/excludeFilter.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <FindBugsFilter - xmlns="https://github.com/spotbugs/filter/3.0.0" + xmlns="https://github.com/spotbugs/filter/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd"> + xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/4.0.0/spotbugs/etc/findbugsfilter.xsd"> <Match> <Or> <Package name="net.thauvin.erik.mobibot.*"/> @@ -22,6 +22,7 @@ <Class name="net.thauvin.erik.mobibot.Pinboard"/> <Class name="net.thauvin.erik.mobibot.FeedReader"/> <Class name="net.thauvin.erik.mobibot.commands.tell.Tell"/> + <Class name="net.thauvin.erik.mobibot.commands.Versions"/> <Package name="net.thauvin.erik.mobibot.modules.*"/> <Package name="net.thauvin.erik.mobibot.entries.*"/> </Or> diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 2f5da4b..51a20bd 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -2,21 +2,24 @@ <SmellBaseline> <Blacklist></Blacklist> <Whitelist> - <ID>ComplexMethod:Links.kt$Links$commandResponse</ID> + <ID>ComplexCondition:UrlMgr.kt$UrlMgr$Ignore.isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(bot.nick) || !cmds[1].endsWith(" (${Ignore.IGNORE_CMD}"))</ID> + <ID>ComplexMethod:Posting.kt$Posting$commandResponse</ID> <ID>ComplexMethod:UrlMgr.kt$UrlMgr$commandResponse</ID> <ID>ComplexMethod:View.kt$View$commandResponse</ID> - <ID>LongMethod:Links.kt$Links$commandResponse</ID> + <ID>LongMethod:Posting.kt$Posting$commandResponse</ID> <ID>LongMethod:UrlMgr.kt$UrlMgr$commandResponse</ID> <ID>LongMethod:View.kt$View$commandResponse</ID> <ID>LongParameterList:AbstractCommand.kt$AbstractCommand$( bot: Mobibot, sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>LoopWithTooManyJumpStatements:View.kt$View$while (i < max) { entry = getEntry(i) if (lcArgs.isNotEmpty()) { if (entry.link.toLowerCase().contains(lcArgs) || entry.title.toLowerCase().contains(lcArgs) || entry.nick.toLowerCase().contains(lcArgs)) { if (sent > maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } } else { if (sent > maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } i++ }</ID> - <ID>MagicNumber:Comments.kt$Comments$3</ID> + <ID>MagicNumber:Comment.kt$Comment$3</ID> <ID>MagicNumber:Cycle.kt$Cycle$10</ID> <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> + <ID>MagicNumber:UrlMgr.kt$UrlMgr$1000L</ID> + <ID>MagicNumber:UrlMgr.kt$UrlMgr$60L</ID> <ID>MagicNumber:View.kt$View$8</ID> - <ID>NestedBlockDepth:Comments.kt$Comments$commandResponse</ID> + <ID>NestedBlockDepth:Comment.kt$Comment$commandResponse</ID> <ID>NestedBlockDepth:Ignore.kt$Ignore$commandResponse</ID> - <ID>NestedBlockDepth:Links.kt$Links$commandResponse</ID> + <ID>NestedBlockDepth:Posting.kt$Posting$commandResponse</ID> <ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID> <ID>NestedBlockDepth:View.kt$View$commandResponse</ID> </Whitelist> diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index b67fc18..db3ab27 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -45,7 +45,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { } } - override val command = IGNORE + override val command = IGNORE_CMD override val help = listOf( Utils.bold("To check your ignore status:"), Utils.helpIndent("%s: $command"), @@ -57,7 +57,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { override val isVisible = true companion object { - const val IGNORE = "ignore" + const val IGNORE_CMD = "ignore" private val ignored = HashSet<String>() @JvmStatic diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index c1a3b37..bbd8c7a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -131,7 +131,7 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { if (Ignore.isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(bot.nick) - || !cmds[1].endsWith(" (${Ignore.IGNORE}"))) { + || !cmds[1].endsWith(" (${Ignore.IGNORE_CMD}"))) { val link = cmds[0].trim() var isBackup = false val dupIndex: Int = findDupEntry(link) From abbb322b5d74ac43fba153ce2291c4cb49da737b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 15:00:56 -0700 Subject: [PATCH 354/842] Added setCommandsList method. --- .../net/thauvin/erik/mobibot/Mobibot.java | 27 ++++++++++++------- .../thauvin/erik/mobibot/commands/Modules.kt | 4 +-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index f68e272..db20fd4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -650,16 +650,10 @@ public class Mobibot extends PircBot { Collections.sort(opsCommandsNames); } - // Print 6 commands per line - final int chunk = 6; - for (int i = 0; i < commandsNames.size(); i += chunk) { - send(sender, Utils.helpIndent( - String.join(" ", commandsNames.subList(i, Math.min(commandsNames.size(), i + chunk))))); - } - + sendCommandsList(sender, commandsNames, false); if (isOp) { - send(sender, Utils.bold("The op commands are:")); - send(sender, Utils.helpIndent(String.join(" ", opsCommandsNames))); + send(sender, Utils.bold("The op commands are:"), false); + sendCommandsList(sender, opsCommandsNames, false); } } @@ -1015,6 +1009,20 @@ public class Mobibot extends PircBot { send(who, Utils.colorize(message, color), false); } + /** + * Send a formatted commands/modules, etc. list. + * + * @param nick The nick to send the list to. + * @param list The list to format. + */ + public final void sendCommandsList(final String nick, final List<String> list, final boolean isPrivate) { + final int chunk = 8; // 8 commands per line. + for (int i = 0; i < list.size(); i += chunk) { + send(nick, Utils.helpIndent( + String.join(" ", list.subList(i, Math.min(list.size(), i + chunk)))), isPrivate); + } + } + /** * Sets the backlogs URL. * @@ -1152,6 +1160,7 @@ public class Mobibot extends PircBot { twitterEntries.remove(index); } + /** * Post all the links on twitter on shutdown. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index de494a4..2bbce85 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -58,8 +58,8 @@ class Modules : AbstractCommand() { if (modulesNames.isEmpty()) { bot.send(sender, "There are no enabled modules.", true) } else { - bot.send(sender, Utils.bold("The enabled modules are: ")) - bot.send(sender, Utils.helpIndent(modulesNames.joinToString(", "))) + bot.send(sender, Utils.bold("The enabled modules are: "), false) + bot.sendCommandsList(sender, modulesNames, false ) } } else { bot.helpDefault(sender, isOp) From 78260381cf05439b975ebeb6e463468faf3c62b4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 15:01:29 -0700 Subject: [PATCH 355/842] Cleaned up send() in commands. --- .../thauvin/erik/mobibot/commands/AbstractCommand.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Users.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Versions.java | 2 +- .../thauvin/erik/mobibot/commands/links/Comment.kt | 4 ++-- .../thauvin/erik/mobibot/commands/links/UrlMgr.kt | 5 ++--- .../net/thauvin/erik/mobibot/commands/links/View.kt | 12 +++++------- 9 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index aeaac2d..9d1a0cb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -53,7 +53,7 @@ abstract class AbstractCommand { open fun helpResponse(bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { if (!this.isOp || this.isOp == isOp) { for (h in help) { - bot.send(sender, String.format(h, bot.nick), isPrivate) + bot.send(sender, String.format(h, bot.nick)) } return true } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt index b1ad2ac..9bc4715 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -83,6 +83,6 @@ class Info : AbstractCommand() { append(']') } - bot.send(sender, info.toString(), isPrivate) + bot.send(sender, info.toString()) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index 2bbce85..ae9492a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -56,7 +56,7 @@ class Modules : AbstractCommand() { if (isOp) { val modulesNames = bot.modulesNames if (modulesNames.isEmpty()) { - bot.send(sender, "There are no enabled modules.", true) + bot.send(sender, "There are no enabled modules.", isPrivate) } else { bot.send(sender, Utils.bold("The enabled modules are: "), false) bot.sendCommandsList(sender, modulesNames, false ) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt index 5c01b83..8c4cc2f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt @@ -56,7 +56,7 @@ class Msg : AbstractCommand() { if (isOp) { val msg = args.split(" ", limit = 2) if (args.length > 2) { - bot.send(msg[0], msg[1], true) + bot.send(msg[0], msg[1], isPrivate) } else { helpResponse(bot, command, sender, isOp, isPrivate) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index 1f0f735..acfed1d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -66,6 +66,6 @@ class Users : AbstractCommand() { } } - bot.send(sender, nicks.sorted().joinToString(" "), isPrivate) + bot.send(sender, nicks.sorted().joinToString(" ")) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 064f5e9..cdc15a2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -88,7 +88,7 @@ public class Versions extends AbstractCommand { final boolean isPrivate) { if (isOp) { for (final String v : versions) { - bot.send(sender, v, isPrivate); + bot.send(sender, v); } } else { bot.helpDefault(sender, isOp); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 9e1138f..6f8a192 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -116,8 +116,8 @@ class Comment : AbstractCommand() { ): Boolean { if (super.helpResponse(bot, command, sender, isOp, isPrivate)) { if (isOp) { - bot.send(sender, Utils.bold("To change a comment's author:"), isPrivate) - bot.send(sender, Utils.helpIndent("/msg ${bot.nick} ${Constants.LINK_CMD}1.1:?<nick>"), isPrivate) + bot.send(sender, Utils.bold("To change a comment's author:")) + bot.send(sender, Utils.helpIndent("/msg ${bot.nick} ${Constants.LINK_CMD}1.1:?<nick>")) } return true } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index bbd8c7a..02afc30 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -196,11 +196,10 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { } saveEntries(bot, isBackup) if (Constants.NO_TITLE == entry.title) { - bot.send(sender, Utils.bold("Please specify a title, by typing:"), true) + bot.send(sender, Utils.bold("Please specify a title, by typing:")) bot.send( sender, - Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title"), - true + Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title") ) } } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index e45885d..daec0f2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -101,30 +101,28 @@ class View : AbstractCommand() { if (sent > maxEntries) { bot.send( sender, "To view more, try: " - + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), - isPrivate + + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs") ) break } - bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) + bot.send(sender, EntriesUtils.buildLink(i, entry, true)) sent++ } } else { if (sent > maxEntries) { bot.send( sender, - "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}"), - isPrivate + "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}") ) break } - bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) + bot.send(sender, EntriesUtils.buildLink(i, entry, true)) sent++ } i++ } } else { - bot.send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate) + bot.send(sender, "There is currently nothing to view. Why don't you post something?") } } } From 887ab8c9596875262faee68e431571bff37e0af0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 15:27:49 -0700 Subject: [PATCH 356/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 1 - src/main/java/net/thauvin/erik/mobibot/commands/Info.kt | 1 - src/main/java/net/thauvin/erik/mobibot/commands/Versions.java | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 056d216..a7ccaa4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -323,5 +323,4 @@ public final class Utils { public static String utcDateTime(final LocalDateTime date) { return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); } - } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt index 9bc4715..261e35e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -35,7 +35,6 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.links.UrlMgr -import org.jibble.pircbot.Colors import java.lang.management.ManagementFactory class Info : AbstractCommand() { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index cdc15a2..077c985 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -91,7 +91,7 @@ public class Versions extends AbstractCommand { bot.send(sender, v); } } else { - bot.helpDefault(sender, isOp); + bot.helpDefault(sender, false); } } From f16508dae45c49a6ebcf0e556f855b7244db2325 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 17:27:44 -0700 Subject: [PATCH 357/842] Removed unecessary breaks. --- .../erik/mobibot/commands/links/View.kt | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index daec0f2..628ace6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -90,36 +90,29 @@ class View : AbstractCommand() { // Do nothing } } + var entry: EntryLink var sent = 0 - while (i < max) { + while (i < max && sent < maxEntries) { entry = getEntry(i) if (lcArgs.isNotEmpty()) { if (entry.link.toLowerCase().contains(lcArgs) || entry.title.toLowerCase().contains(lcArgs) || entry.nick.toLowerCase().contains(lcArgs)) { - if (sent > maxEntries) { - bot.send( - sender, "To view more, try: " - + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs") - ) - break - } bot.send(sender, EntriesUtils.buildLink(i, entry, true)) sent++ } } else { - if (sent > maxEntries) { - bot.send( - sender, - "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}") - ) - break - } bot.send(sender, EntriesUtils.buildLink(i, entry, true)) sent++ } i++ + if (sent == maxEntries) { + bot.send( + sender, + "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs") + ) + } } } else { bot.send(sender, "There is currently nothing to view. Why don't you post something?") From 6bf2d6fdb13fc07c259b2537d592bc33c89d0754 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 17:29:35 -0700 Subject: [PATCH 358/842] Updated to jdk 9 & 14, maybe. --- .circleci/config.yml | 12 ++++++------ .travis.yml | 12 +++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a20eb37..2926e9b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,19 +30,19 @@ defaults_gradle: &defaults_gradle path: build/reports/ jobs: - build_gradle_jdk12: + build_gradle_jdk14: <<: *defaults docker: - - image: openjdk:12-jdk + - image: cimg/openjdk:14.0 <<: *defaults_gradle - build_gradle_jdk8: + build_gradle_jdk9: <<: *defaults docker: - - image: circleci/openjdk:8-jdk + - image: circleci/openjdk:9 <<: *defaults_gradle @@ -50,6 +50,6 @@ workflows: version: 2 gradle: jobs: - - build_gradle_jdk8 - - build_gradle_jdk12 + - build_gradle_jdk9 + - build_gradle_jdk14 diff --git a/.travis.yml b/.travis.yml index f8946bd..73393f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,12 @@ env: global: - CI=true +matrix: + include: + - jdk: openjdk9 + - env: JDK='OpenJDK 14' + install: . ./install-jdk.sh -F 14 -C + install: - git fetch --unshallow --tags @@ -12,15 +18,11 @@ addons: sonarcloud: organization: "ethauvin-github" -jdk: - - oraclejdk8 - - openjdk12 - before_install: - chmod +x gradlew after_success: - | - if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then + if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk9 ]; then ./gradlew sonarqube fi From 4aeb17ecef7cf138eec0dec75e26d99126ee7131 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 28 Mar 2020 17:39:52 -0700 Subject: [PATCH 359/842] Trying openjdk14 and oraclejdk9. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- .travis.yml | 10 ++++------ gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 7b66ece..e5aec1b 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+090" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 49a5e9b..77358ff 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+090" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index b92cc97..2ff7f88 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+090" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/.travis.yml b/.travis.yml index 73393f2..10d46d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,6 @@ env: global: - CI=true -matrix: - include: - - jdk: openjdk9 - - env: JDK='OpenJDK 14' - install: . ./install-jdk.sh -F 14 -C - install: - git fetch --unshallow --tags @@ -18,6 +12,10 @@ addons: sonarcloud: organization: "ethauvin-github" +jdk: + - oraclejdk9 + - openjdk14 + before_install: - chmod +x gradlew diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b133221..a4b4429 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-rc-3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 02fae4d8e49b947ecc6893d8139681830d28015b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Mar 2020 00:42:02 -0700 Subject: [PATCH 360/842] Added matches(). --- .../erik/mobibot/entries/EntryLink.java | 173 ++++++++++-------- 1 file changed, 93 insertions(+), 80 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 0365ae4..6e95c0c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -36,6 +36,7 @@ import com.rometools.rome.feed.synd.SyndCategory; import com.rometools.rome.feed.synd.SyndCategoryImpl; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Constants; +import org.apache.commons.lang3.StringUtils; import java.io.Serializable; import java.util.Calendar; @@ -161,16 +162,6 @@ public class EntryLink implements Serializable { return channel; } - /** - * Sets the channel. - * - * @param channel The channel. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setChannel(final String channel) { - this.channel = channel; - } - /** * Returns a comment. * @@ -217,15 +208,6 @@ public class EntryLink implements Serializable { return link; } - /** - * Sets the comment's link. - * - * @param link The new link. - */ - public final void setLink(final String link) { - this.link = link; - } - /** * Returns the comment's author login. * @@ -235,16 +217,6 @@ public class EntryLink implements Serializable { return login; } - /** - * Sets the comment's author login. - * - * @param login The new login. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setLogin(final String login) { - this.login = login; - } - /** * Returns the comment's author nickname. * @@ -254,15 +226,6 @@ public class EntryLink implements Serializable { return nick; } - /** - * Sets the comment's author nickname. - * - * @param nick The new nickname. - */ - public final void setNick(final String nick) { - this.nick = nick; - } - /** * Returns the tags formatted for pinboard.in * @@ -288,6 +251,96 @@ public class EntryLink implements Serializable { return tags; } + /** + * Returns the comment's title. + * + * @return The title. + */ + public final String getTitle() { + return title; + } + + /** + * Returns true if the entry has comments. + * + * @return true if there are comments, false otherwise. + */ + public final boolean hasComments() { + return !comments.isEmpty(); + } + + /** + * Returns true if the entry has tags. + * + * @return true if there are tags, false otherwise. + */ + public final boolean hasTags() { + return !tags.isEmpty(); + } + + /** + * Returns true if a string is contained in the link, title, or nick. + * + * @param match The string to match. + * @return {@code true} if matched, {@code false} otherwise. + */ + public Boolean matches(final String match) { + return (StringUtils.containsIgnoreCase(link, match) + || StringUtils.containsIgnoreCase(title, match) + || StringUtils.containsIgnoreCase(nick, match)); + } + + /** + * Sets the channel. + * + * @param channel The channel. + */ + @SuppressWarnings("UnusedDeclaration") + public final void setChannel(final String channel) { + this.channel = channel; + } + + /** + * /** Sets a comment. + * + * @param index The comment's index. + * @param comment The actual comment. + * @param nick The nickname of the author of the comment. + */ + public final void setComment(final int index, final String comment, final String nick) { + if (index < comments.size()) { + comments.set(index, new EntryComment(comment, nick)); + } + } + + /** + * Sets the comment's link. + * + * @param link The new link. + */ + public final void setLink(final String link) { + this.link = link; + } + + /** + * Sets the comment's author login. + * + * @param login The new login. + */ + @SuppressWarnings("UnusedDeclaration") + public final void setLogin(final String login) { + this.login = login; + } + + /** + * Sets the comment's author nickname. + * + * @param nick The new nickname. + */ + public final void setNick(final String nick) { + this.nick = nick; + } + /** * Sets the tags. * @@ -342,15 +395,6 @@ public class EntryLink implements Serializable { this.tags.addAll(tags); } - /** - * Returns the comment's title. - * - * @return The title. - */ - public final String getTitle() { - return title; - } - /** * Sets the comment's title. * @@ -360,37 +404,6 @@ public class EntryLink implements Serializable { this.title = title; } - /** - * Returns true if the entry has comments. - * - * @return true if there are comments, false otherwise. - */ - public final boolean hasComments() { - return (!comments.isEmpty()); - } - - /** - * Returns true if the entry has tags. - * - * @return true if there are tags, false otherwise. - */ - public final boolean hasTags() { - return (!tags.isEmpty()); - } - - /** - * /** Sets a comment. - * - * @param index The comment's index. - * @param comment The actual comment. - * @param nick The nickname of the author of the comment. - */ - public final void setComment(final int index, final String comment, final String nick) { - if (index < comments.size()) { - comments.set(index, new EntryComment(comment, nick)); - } - } - /** * Returns a string representation of the object. * @@ -400,7 +413,7 @@ public class EntryLink implements Serializable { public final String toString() { return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date - + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' - + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; + + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' + + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; } } From fbe36d50b7ada558ca0f8ff0145755e94d803fe1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 29 Mar 2020 00:44:10 -0700 Subject: [PATCH 361/842] Removed complexity (detekt) --- detekt-baseline.xml | 14 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 1 - .../thauvin/erik/mobibot/commands/Ignore.kt | 96 +++++++---- .../erik/mobibot/commands/links/Comment.kt | 74 +++++---- .../erik/mobibot/commands/links/Posting.kt | 149 +++++++++-------- .../erik/mobibot/commands/links/UrlMgr.kt | 154 +++++++++--------- .../erik/mobibot/commands/links/View.kt | 97 ++++++----- version.properties | 6 +- 10 files changed, 328 insertions(+), 271 deletions(-) diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 51a20bd..4e8f359 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -2,25 +2,15 @@ <SmellBaseline> <Blacklist></Blacklist> <Whitelist> - <ID>ComplexCondition:UrlMgr.kt$UrlMgr$Ignore.isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(bot.nick) || !cmds[1].endsWith(" (${Ignore.IGNORE_CMD}"))</ID> - <ID>ComplexMethod:Posting.kt$Posting$commandResponse</ID> - <ID>ComplexMethod:UrlMgr.kt$UrlMgr$commandResponse</ID> - <ID>ComplexMethod:View.kt$View$commandResponse</ID> - <ID>LongMethod:Posting.kt$Posting$commandResponse</ID> - <ID>LongMethod:UrlMgr.kt$UrlMgr$commandResponse</ID> - <ID>LongMethod:View.kt$View$commandResponse</ID> <ID>LongParameterList:AbstractCommand.kt$AbstractCommand$( bot: Mobibot, sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> - <ID>LoopWithTooManyJumpStatements:View.kt$View$while (i < max) { entry = getEntry(i) if (lcArgs.isNotEmpty()) { if (entry.link.toLowerCase().contains(lcArgs) || entry.title.toLowerCase().contains(lcArgs) || entry.nick.toLowerCase().contains(lcArgs)) { if (sent > maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } } else { if (sent > maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } i++ }</ID> + <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> + <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID> <ID>MagicNumber:Cycle.kt$Cycle$10</ID> <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> <ID>MagicNumber:UrlMgr.kt$UrlMgr$1000L</ID> <ID>MagicNumber:UrlMgr.kt$UrlMgr$60L</ID> <ID>MagicNumber:View.kt$View$8</ID> - <ID>NestedBlockDepth:Comment.kt$Comment$commandResponse</ID> - <ID>NestedBlockDepth:Ignore.kt$Ignore$commandResponse</ID> - <ID>NestedBlockDepth:Posting.kt$Posting$commandResponse</ID> <ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID> - <ID>NestedBlockDepth:View.kt$View$commandResponse</ID> </Whitelist> </SmellBaseline> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 3421c5d..00d806e 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1585416159853L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1585467573570L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "045"; - public static final String VERSION = "0.8.0-alpha+045"; + public static final String BUILDMETA = "105"; + public static final String VERSION = "0.8.0-alpha+105"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index db20fd4..0afd2b0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -212,7 +212,7 @@ public class Mobibot extends PircBot { UrlMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, ircChannel); if (logger.isDebugEnabled()) { - logger.debug("Last feed: {}", UrlMgr.getToday()); + logger.debug("Last feed: {}", UrlMgr.getStartDate()); } } catch (Exception e) { if (logger.isErrorEnabled()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 9d1a0cb..4eeaaa8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -63,5 +63,4 @@ abstract class AbstractCommand { open fun matches(message: String): Boolean { return false } - } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index db3ab27..d24ccd3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -37,7 +37,7 @@ import net.thauvin.erik.mobibot.Utils import java.util.* class Ignore(defaultIgnore: String) : AbstractCommand() { - private val keyword = "me" + private val me = "me" init { if (defaultIgnore.isNotBlank()) { @@ -47,11 +47,20 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { override val command = IGNORE_CMD override val help = listOf( + Utils.bold("To ignore a link posted to the channel:"), + Utils.helpIndent("https://www.foo.bar %s"), Utils.bold("To check your ignore status:"), Utils.helpIndent("%s: $command"), Utils.bold("To toggle your ignore status:"), - Utils.helpIndent("%s: $command $keyword") + Utils.helpIndent("%s: $command $me") ) + private val helpOp = listOf( + Utils.bold("To ignore a link posted to the channel:"), + Utils.helpIndent("https://www.foo.bar %s"), + Utils.bold("To add/remove nicks from the ignored list:"), + Utils.helpIndent("/msg %s $command <nick>|$me [<nick> ...]") + ) + override val isOp = false override val isPublic = true override val isVisible = true @@ -74,38 +83,69 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { isOp: Boolean, isPrivate: Boolean ) { - if (isOp) { + if (!isOp) { val nick = sender.toLowerCase() - val isMe = args.toLowerCase().startsWith(keyword) - if (isMe) { - if (ignored.remove(nick)) { - bot.send(sender, "You are no longer ignored.") - } else { - ignored.add(nick) - bot.send(sender, "You are now ignored.") - } + val isMe = args.toLowerCase().startsWith(me) + ignoreNick(bot, nick, isMe) + } else { + ignoreOp(bot, sender, args) + } + } + + override fun helpResponse( + bot: Mobibot, + command: String, + sender: String, + isOp: Boolean, + isPrivate: Boolean + ): Boolean { + return if (isOp) { + for (h in helpOp) { + bot.send(sender, String.format(h, bot.nick)) + } + true + } else { + super.helpResponse(bot, command, sender, isOp, isPrivate) + } + } + + private fun ignoreNick(bot: Mobibot, sender: String, isMe: Boolean) { + if (isMe) { + if (ignored.remove(sender)) { + bot.send(sender, "You are no longer ignored.") } else { - if (ignored.contains(nick)) { - bot.send(sender, "You are currently ignored.") - } else { - bot.send(sender, "You are not currently ignored.") - } + ignored.add(sender) + bot.send(sender, "You are now ignored.") } } else { - if (args.isNotEmpty()) { - val nicks = args.toLowerCase().split(" ") - for (nick in nicks) { - val ignore = if (keyword == nick) { - sender.toLowerCase() - } else { - nick - } - if (!ignored.remove(ignore)) { - ignored.add(ignore) - } + if (ignored.contains(sender)) { + bot.send(sender, "You are currently ignored.") + } else { + bot.send(sender, "You are not currently ignored.") + } + } + } + + private fun ignoreOp(bot: Mobibot, sender: String, args: String) { + if (args.isNotEmpty()) { + val nicks = args.toLowerCase().split(" ") + for (nick in nicks) { + val ignore = if (me == nick) { + nick.toLowerCase() + } else { + nick + } + if (!ignored.remove(ignore)) { + ignored.add(ignore) } } - bot.send(sender, "The following nicks are ignored: ${ignored.joinToString(", ")}") + } + + if (ignored.size > 0) { + bot.send(sender, Utils.bold("The following nicks are ignored:")) + bot.sendCommandsList(sender, ignored.toList(), false) + } else { + bot.send(sender, "No one is currently ${Utils.bold("ignored")}.") } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 6f8a192..435de45 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -71,37 +71,13 @@ class Comment : AbstractCommand() { if (index < UrlMgr.entriesCount) { val entry: EntryLink = UrlMgr.getEntry(index) - val cindex = cmds[1].toInt() - 1 - if (cindex < entry.commentsCount) { - val cmd = cmds[2].trim() - - // L1.1: - if (cmd.isEmpty()) { - val comment = entry.getComment(cindex) - bot.send(bot.channel, EntriesUtils.buildComment(index, cindex, comment)) - } else if ("-" == cmd) { // L11:- - entry.deleteComment(cindex) - bot.send( - bot.channel, - "Comment ${Constants.LINK_CMD}${index + 1}.${cindex + 1} removed." - ) - UrlMgr.saveEntries(bot, false) - } else if (cmd[0] == '?') { // L1.1:?<author> - if (isOp) { - if (cmd.length > 1) { - val comment = entry.getComment(cindex) - comment.nick = cmd.substring(1) - bot.send(bot.channel, EntriesUtils.buildComment(index, cindex, comment)) - UrlMgr.saveEntries(bot, false) - } - } else { - bot.send(sender, "Please ask a channel op to change the author of this comment for you.") - } - } else { - entry.setComment(cindex, cmd, sender) - val comment = entry.getComment(cindex) - bot.send(sender, EntriesUtils.buildComment(index, cindex, comment)) - UrlMgr.saveEntries(bot, false) + val commentIndex = cmds[1].toInt() - 1 + if (commentIndex < entry.commentsCount) { + when (val cmd = cmds[2].trim()) { + "" -> showComment(bot, entry, index, commentIndex) // L1.1: + "-" -> deleteComment(bot, entry, index, commentIndex) // L11:- + "?" -> changeAuthor(bot, cmd, sender, isOp, entry, index, commentIndex) // L1.1:?<author> + else -> setComment(bot, cmd, sender, entry, index, commentIndex) } } } @@ -127,4 +103,40 @@ class Comment : AbstractCommand() { override fun matches(message: String): Boolean { return message.matches("^${Constants.LINK_CMD}[0-9]+\\.[0-9]+:.*".toRegex()) } + + private fun changeAuthor( + bot: Mobibot, + cmd: String, + sender: String, + isOp: Boolean, + entry: EntryLink, + index: Int, + commentIndex: Int + ) { + if (isOp && cmd.length > 1) { + val comment = entry.getComment(commentIndex) + comment.nick = cmd.substring(1) + bot.send(bot.channel, EntriesUtils.buildComment(index, commentIndex, comment)) + UrlMgr.saveEntries(bot, false) + } else { + bot.send(sender, "Please ask a channel op to change the author of this comment for you.") + } + } + + private fun deleteComment(bot: Mobibot, entry: EntryLink, index: Int, commentIndex: Int) { + entry.deleteComment(commentIndex) + bot.send(bot.channel, "Comment ${Constants.LINK_CMD}${index + 1}.${commentIndex + 1} removed.") + UrlMgr.saveEntries(bot, false) + } + + private fun setComment(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int) { + entry.setComment(commentIndex, cmd, sender) + val comment = entry.getComment(commentIndex) + bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment)) + UrlMgr.saveEntries(bot, false) + } + + private fun showComment(bot: Mobibot, entry: EntryLink, index: Int, commentIndex: Int) { + bot.send(bot.channel, EntriesUtils.buildComment(index, commentIndex, entry.getComment(commentIndex))) + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 6d05a0e..7b9fed5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -69,74 +69,17 @@ class Posting : AbstractCommand() { val index = cmds[0].toInt() - 1 if (index < UrlMgr.entriesCount) { - val cmd = cmds[1].trim() - if (cmd.isEmpty()) { - val entry: EntryLink = UrlMgr.getEntry(index) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) - if (entry.hasTags()) { - bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) - } - if (entry.hasComments()) { - val comments = entry.comments - for (i in comments.indices) { - bot.send(bot.channel, EntriesUtils.buildComment(index, i, comments[i])) + when (val cmd = cmds[1].trim()) { + "" -> showEntry(bot, index) + "-" -> removeEntry(bot, sender, login, isOp, index) // L1:- + else -> { + when (cmd[0]) { + '|' -> changeTitle(bot, cmd, index) // L1:|<title> + '=' -> changeUrl(bot, cmd, login, isOp, index) // L1:=<url> + '?' -> changeAuthor(bot, cmd, sender, isOp, index) // L1:?<author> + else -> addComment(bot, cmd, sender, index) } } - } else { - // L1:- - if ("-" == cmd) { - val entry: EntryLink = UrlMgr.getEntry(index) - if (entry.login == login || isOp) { - bot.deletePin(entry) - if (bot.isTwitterAutoPost) { - bot.twitterRemoveEntry(index) - } - UrlMgr.removeEntry(index) - bot.send(bot.channel, "Entry ${Constants.LINK_CMD}${index + 1} removed.") - UrlMgr.saveEntries(bot, false) - } else { - bot.send(sender, "Please ask a channel op to remove this entry for you.") - } - } else if (cmd[0] == '|') { // L1:|<title> - if (cmd.length > 1) { - val entry: EntryLink = UrlMgr.getEntry(index) - entry.title = cmd.substring(1).trim() - bot.updatePin(entry.link, entry) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) - UrlMgr.saveEntries(bot, false) - } - } else if (cmd[0] == '=') { // L1:=<url> - val entry: EntryLink = UrlMgr.getEntry(index) - if (entry.login == login || isOp) { - val link = cmd.substring(1) - if (link.matches(UrlMgr.LINK_MATCH.toRegex())) { - val oldLink = entry.link - entry.link = link - bot.updatePin(oldLink, entry) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) - UrlMgr.saveEntries(bot, false) - } - } else { - bot.send(sender, "Please ask channel op to change this link for you.") - } - } else if (cmd[0] == '?') { // L1:?<author> - if (isOp) { - if (cmd.length > 1) { - val entry: EntryLink = UrlMgr.getEntry(index) - entry.nick = cmd.substring(1) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) - UrlMgr.saveEntries(bot, false) - } - } else { - bot.send(sender, "Please ask a channel op to change the author of this link for you.") - } - } else { - val entry: EntryLink = UrlMgr.getEntry(index) - val cindex = entry.addComment(cmd, sender) - val comment = entry.getComment(cindex) - bot.send(sender, EntriesUtils.buildComment(index, cindex, comment)) - UrlMgr.saveEntries(bot, false) - } } } } @@ -144,4 +87,78 @@ class Posting : AbstractCommand() { override fun matches(message: String): Boolean { return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex()) } + + private fun addComment(bot: Mobibot, cmd: String, sender: String, index: Int) { + val entry: EntryLink = UrlMgr.getEntry(index) + val commentIndex = entry.addComment(cmd, sender) + val comment = entry.getComment(commentIndex) + bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment)) + UrlMgr.saveEntries(bot, false) + } + + private fun changeTitle(bot: Mobibot, cmd: String, index: Int) { + if (cmd.length > 1) { + val entry: EntryLink = UrlMgr.getEntry(index) + entry.title = cmd.substring(1).trim() + bot.updatePin(entry.link, entry) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + UrlMgr.saveEntries(bot, false) + } + } + + private fun changeUrl(bot: Mobibot, cmd: String, login: String, isOp: Boolean, index: Int) { + val entry: EntryLink = UrlMgr.getEntry(index) + if (entry.login == login || isOp) { + val link = cmd.substring(1) + if (link.matches(UrlMgr.LINK_MATCH.toRegex())) { + val oldLink = entry.link + entry.link = link + bot.updatePin(oldLink, entry) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + UrlMgr.saveEntries(bot, false) + } + } + } + + private fun changeAuthor(bot: Mobibot, cmd: String, sender: String, isOp: Boolean, index: Int) { + if (isOp) { + if (cmd.length > 1) { + val entry: EntryLink = UrlMgr.getEntry(index) + entry.nick = cmd.substring(1) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + UrlMgr.saveEntries(bot, false) + } + } else { + bot.send(sender, "Please ask a channel op to change the author of this link for you.") + } + } + + private fun removeEntry(bot: Mobibot, sender: String, login: String, isOp: Boolean, index: Int) { + val entry: EntryLink = UrlMgr.getEntry(index) + if (entry.login == login || isOp) { + bot.deletePin(entry) + if (bot.isTwitterAutoPost) { + bot.twitterRemoveEntry(index) + } + UrlMgr.removeEntry(index) + bot.send(bot.channel, "Entry ${Constants.LINK_CMD}${index + 1} removed.") + UrlMgr.saveEntries(bot, false) + } else { + bot.send(sender, "Please ask a channel op to remove this entry for you.") + } + } + + private fun showEntry(bot: Mobibot, index: Int) { + val entry: EntryLink = UrlMgr.getEntry(index) + bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + if (entry.hasTags()) { + bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) + } + if (entry.hasComments()) { + val comments = entry.comments + for (i in comments.indices) { + bot.send(bot.channel, EntriesUtils.buildComment(index, i, comments[i])) + } + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index 02afc30..89519ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.commands.links -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.TwitterTimer @@ -44,7 +43,6 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink import org.jsoup.Jsoup import java.io.IOException -import java.util.* class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { private val tagsKeywords = ArrayList<String>() @@ -74,7 +72,7 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { get() = entries.size @JvmStatic - var today: String = Utils.today() + var startDate: String = Utils.today() private set @JvmStatic @@ -109,16 +107,15 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { @JvmStatic fun startup(current: String, backlogs: String, channel: String) { - today = EntriesMgr.loadEntries(current, channel, entries) - if (Utils.today() != today) { + startDate = EntriesMgr.loadEntries(current, channel, entries) + if (Utils.today() != startDate) { this.entries.clear() - today = Utils.today() + startDate = Utils.today() } EntriesMgr.loadBacklogs(backlogs, history) } } - @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD") override fun commandResponse( bot: Mobibot, sender: String, @@ -129,56 +126,22 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { ) { val cmds = args.split(" ".toRegex(), 2) - if (Ignore.isNotIgnored(sender) && (cmds.size == 1 - || !cmds[1].contains(bot.nick) - || !cmds[1].endsWith(" (${Ignore.IGNORE_CMD}"))) { + if (Ignore.isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(bot.nick))) { val link = cmds[0].trim() - var isBackup = false - val dupIndex: Int = findDupEntry(link) - if (dupIndex == -1) { - if (Utils.today() != today) { - isBackup = true - saveEntries(bot, true) - entries.clear() - today = Utils.today() - } + if (!isDupEntry(bot, sender, link)) { + val isBackup = saveDayBackup(bot) val tags: StringBuilder = StringBuilder(defaultTags) var title = Constants.NO_TITLE if (cmds.size == 2) { val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) - if (data.size == 1) { - title = data[0].trim() - } else { - if (data[0].isNotBlank()) { - title = data[0].trim() - } + title = data[0].trim() + if (data.size > 1) { tags.append(' ').append(data[1].trim()) } } - if (Constants.NO_TITLE == title) { - try { - val html = Jsoup.connect(link).userAgent("Mozilla").get() - val htmlTitle = html.title() - if (htmlTitle.isNotBlank()) { - val split = htmlTitle.split("( \\| )".toRegex(), 2) - title = if (split.size == 2) { - split[0] - } else { - htmlTitle - } - } - } catch (ignore: IOException) { - // Do nothing - } - } - if (tagsKeywords.isNotEmpty()) { - for (match in tagsKeywords) { - val m = match.trim() - if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { - tags.append(' ').append(m) - } - } - } + tags.append(matchTagKeywords(title)) + title = fetchTitle(link, title) + entries.add(EntryLink(link, title, sender, login, bot.channel, tags.toString())) val index: Int = entries.size - 1 val entry: EntryLink = entries[index] @@ -188,13 +151,10 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { bot.addPin(entry) // Queue link for posting to twitter - if (bot.isTwitterAutoPost) { - bot.twitterAddEntry(index) - Mobibot.timer.schedule( - TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L - ) - } + twitterPost(bot, index) + saveEntries(bot, isBackup) + if (Constants.NO_TITLE == entry.title) { bot.send(sender, Utils.bold("Please specify a title, by typing:")) bot.send( @@ -202,11 +162,6 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title") ) } - } else { - val entry: EntryLink = entries[dupIndex] - bot.send( - sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry) - ) } } } @@ -219,24 +174,69 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { isPrivate: Boolean ): Boolean = false - /** - * Returns the index of the specified duplicate entry, if any. - * - * @param link The link. - * @return The index or -1 if none. - */ - private fun findDupEntry(link: String): Int { - synchronized(entries) { - for (i in entries.indices) { - if (link == entries[i].link) { - return i - } - } - } - return -1 - } - override fun matches(message: String): Boolean { return message.matches(LINK_MATCH.toRegex()) } + + private fun fetchTitle(link: String, title: String): String { + if (Constants.NO_TITLE == title) { + try { + val html = Jsoup.connect(link).userAgent("Mozilla").get() + val htmlTitle = html.title() + val split = htmlTitle.split("( \\| )".toRegex(), 2) + return if (split.size == 2) { + split[0] + } else { + htmlTitle + } + } catch (ignore: IOException) { + // Do nothing + } + } + return title + } + + private fun isDupEntry(bot: Mobibot, sender: String, link: String): Boolean { + synchronized(entries) { + for (i in entries.indices) { + if (link == entries[i].link) { + val entry: EntryLink = entries[i] + bot.send(sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry)) + return true + } + } + } + return false + } + + private fun matchTagKeywords(title: String): String { + val matches = ArrayList<String>() + if (tagsKeywords.isNotEmpty()) { + for (match in tagsKeywords) { + val m = match.trim() + if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { + matches.add(m) + } + } + } + return matches.joinToString(" ") + } + + private fun saveDayBackup(bot: Mobibot): Boolean { + if (Utils.today() != startDate) { + saveEntries(bot, true) + entries.clear() + startDate = Utils.today() + return true + } + + return false + } + + private fun twitterPost(bot: Mobibot, index: Int) { + if (bot.isTwitterAutoPost) { + bot.twitterAddEntry(index) + Mobibot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index 628ace6..1bf601a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -65,57 +65,56 @@ class View : AbstractCommand() { isPrivate: Boolean ) { if (entriesCount != 0) { - val max = entriesCount - var lcArgs = args.toLowerCase(Constants.LOCALE) - var i = 0 - if (lcArgs.isEmpty() && max > maxEntries) { - i = max - maxEntries - } - if (lcArgs.matches("^\\d+(| .*)".toRegex())) { - val split = lcArgs.split(" ", limit = 2) - try { - i = split[0].toInt() - if (i > 0) { - i-- - } - lcArgs = if (split.size == 2) { - split[1].trim() - } else { - "" - } - if (i > max) { - i = 0 - } - } catch (ignore: NumberFormatException) { - // Do nothing - } - } - - var entry: EntryLink - var sent = 0 - while (i < max && sent < maxEntries) { - entry = getEntry(i) - if (lcArgs.isNotEmpty()) { - if (entry.link.toLowerCase().contains(lcArgs) - || entry.title.toLowerCase().contains(lcArgs) - || entry.nick.toLowerCase().contains(lcArgs)) { - bot.send(sender, EntriesUtils.buildLink(i, entry, true)) - sent++ - } - } else { - bot.send(sender, EntriesUtils.buildLink(i, entry, true)) - sent++ - } - i++ - if (sent == maxEntries) { - bot.send( - sender, - "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs") - ) - } - } + showPosts(bot, args, sender) } else { bot.send(sender, "There is currently nothing to view. Why don't you post something?") } } + + private fun showPosts(bot: Mobibot, args: String, sender: String) { + val max = entriesCount + var lcArgs = args.toLowerCase(Constants.LOCALE) + var i = 0 + if (lcArgs.isEmpty() && max > maxEntries) { + i = max - maxEntries + } + if (lcArgs.matches("^\\d+(| .*)".toRegex())) { + val split = lcArgs.split(" ", limit = 2) + try { + i = split[0].toInt() - 1 + lcArgs = if (split.size == 2) { + split[1].trim() + } else { + "" + } + if (i > max) { + i = 0 + } + } catch (ignore: NumberFormatException) { + // Do nothing + } + } + + var entry: EntryLink + var sent = 0 + while (i < max && sent < maxEntries) { + entry = getEntry(i) + if (lcArgs.isNotBlank()) { + if (entry.matches(lcArgs)) { + bot.send(sender, EntriesUtils.buildLink(i, entry, true)) + sent++ + } + } else { + bot.send(sender, EntriesUtils.buildLink(i, entry, true)) + sent++ + } + i++ + if (sent == maxEntries && i < max) { + bot.send( + sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs") + ) + } + } + + } } diff --git a/version.properties b/version.properties index 30605d4..3f02b34 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Mar 28 10:22:37 PDT 2020 -version.buildmeta=045 +#Sun Mar 29 00:39:32 PDT 2020 +version.buildmeta=105 version.major=0 version.minor=8 version.patch=0 version.prerelease=alpha version.project=mobibot -version.semver=0.8.0-alpha+045 +version.semver=0.8.0-alpha+105 From b7a0072db56830fb5036c0ea8a210610d41c03f5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:27:00 -0700 Subject: [PATCH 362/842] Updated dependencies. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 6 +++--- .idea/modules/mobibot.test.iml | 6 +++--- build.gradle | 9 +++++---- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index e5aec1b..771bd41 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+090" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+157" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 77358ff..6f0fcd0 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+090" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+157" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -54,7 +54,7 @@ <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 2ff7f88..cfdf045 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+090" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+157" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -55,7 +55,7 @@ <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> diff --git a/build.gradle b/build.gradle index d9102a2..8d75ae5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,9 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.28.0' - id 'com.github.spotbugs' version '4.0.4' + id 'com.github.spotbugs' version '4.0.5' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.7.0' + id 'io.gitlab.arturbosch.detekt' version '1.7.2' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -46,11 +46,12 @@ dependencies { implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" - implementation 'org.apache.commons:commons-lang3:3.9' + implementation 'org.apache.commons:commons-lang3:3.10' + //implementation 'org.apache.commons:commons-text:1.8' implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.6' - implementation 'com.squareup.okhttp3:okhttp:4.4.1' + implementation 'com.squareup.okhttp3:okhttp:4.5.0-RC1' implementation 'com.rometools:rome:1.12.2' From 363a7cda40544953d9b0db4c23f4383404aec552 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:31:58 -0700 Subject: [PATCH 363/842] Fixed convert2Diamond. --- src/main/java/net/thauvin/erik/mobibot/Pinboard.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index a0f3d95..b9f538d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -80,9 +80,8 @@ class Pinboard { * * @param entry The entry to add. */ - @SuppressWarnings("Convert2Diamond") final void addPost(final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { @Override protected Boolean doInBackground() { return pinboard.addPin(entry.getLink(), @@ -101,11 +100,10 @@ class Pinboard { * * @param entry The entry to delete. */ - @SuppressWarnings("Convert2Diamond") final void deletePost(final EntryLink entry) { final String link = entry.getLink(); - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { @Override protected Boolean doInBackground() { return pinboard.deletePin(link); @@ -141,9 +139,8 @@ class Pinboard { * @param oldUrl The old post URL. * @param entry The entry to add. */ - @SuppressWarnings("Convert2Diamond") final void updatePost(final String oldUrl, final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { + final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { @Override protected Boolean doInBackground() { if (!oldUrl.equals(entry.getLink())) { From 89eac6b0254e8d116370def8513e7403339ba22b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:38:43 -0700 Subject: [PATCH 364/842] Fixed isPrivate() and bold() usage. --- .../net/thauvin/erik/mobibot/FeedReader.java | 10 +- .../net/thauvin/erik/mobibot/Mobibot.java | 129 +++++++++--------- .../java/net/thauvin/erik/mobibot/Utils.java | 14 +- .../erik/mobibot/commands/AbstractCommand.kt | 7 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 8 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 40 +++--- .../net/thauvin/erik/mobibot/commands/Info.kt | 6 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 6 +- .../thauvin/erik/mobibot/commands/Modules.kt | 10 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 6 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 6 +- .../thauvin/erik/mobibot/commands/Recap.kt | 7 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 8 +- .../thauvin/erik/mobibot/commands/Users.kt | 6 +- .../erik/mobibot/commands/Versions.java | 8 +- .../erik/mobibot/commands/links/Comment.kt | 48 ++++--- .../erik/mobibot/commands/links/Posting.kt | 34 ++--- .../erik/mobibot/commands/links/Tags.kt | 10 +- .../erik/mobibot/commands/links/UrlMgr.kt | 13 +- .../erik/mobibot/commands/links/View.kt | 12 +- .../erik/mobibot/commands/tell/Tell.java | 63 +++++---- .../erik/mobibot/modules/AbstractModule.java | 7 +- .../thauvin/erik/mobibot/modules/Calc.java | 12 +- .../thauvin/erik/mobibot/modules/Dice.java | 8 +- .../erik/mobibot/modules/GoogleSearch.java | 16 +-- .../thauvin/erik/mobibot/modules/Joke.java | 14 +- .../thauvin/erik/mobibot/modules/Lookup.java | 10 +- .../thauvin/erik/mobibot/modules/Ping.java | 8 +- .../erik/mobibot/modules/ThreadedModule.java | 10 +- .../thauvin/erik/mobibot/modules/Twitter.java | 17 ++- .../net/thauvin/erik/mobibot/modules/War.java | 11 +- 31 files changed, 292 insertions(+), 272 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 6f52e3a..99c68bd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -89,20 +89,20 @@ class FeedReader implements Runnable { SyndEntry item; final List<SyndEntry> items = feed.getEntries(); if (items.isEmpty()) { - bot.send(sender, "There is currently nothing to view. Why don't you post something?"); + bot.send(sender, "There is currently nothing to view. Why don't you post something?", false); } else { for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { item = items.get(i); - bot.send(sender, item.getTitle()); - bot.send(sender, TAB_INDENT + Utils.green(item.getLink())); + bot.send(sender, item.getTitle(), false); + bot.send(sender, TAB_INDENT + Utils.green(item.getLink()), false); } } } catch (MalformedURLException e) { bot.getLogger().debug("Invalid feed URL.", e); - bot.send(sender, "The feed URL is invalid."); + bot.send(sender, "The feed URL is invalid.", false); } catch (Exception e) { bot.getLogger().debug("Unable to fetch the feed.", e); - bot.send(sender, "An error has occurred while fetching the feed: " + e.getMessage()); + bot.send(sender, "An error has occurred while fetching the feed: " + e.getMessage(), false); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 0afd2b0..63a6e4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -482,13 +482,13 @@ public class Mobibot extends PircBot { /** * Responds with the title and links from the RSS feed. * - * @param sender The nick of the person who sent the private message. + * @param sender The nick of the person who sent the message. */ private void feedResponse(final String sender) { if (isNotBlank(feedUrl)) { new Thread(new FeedReader(this, sender, feedUrl)).start(); } else { - send(sender, "There is no weblog setup for this channel."); + send(sender, "There is no weblog setup for this channel.", false); } } @@ -597,14 +597,15 @@ public class Mobibot extends PircBot { /** * Responds with the commands help, if any. * - * @param sender The nick of the person requesting Constants. - * @param topic The help topic. + * @param sender The nick of the person requesting Constants. + * @param topic The help topic. + * @param isPrivate The private flag. * @return {@code true} if the topic was found, {@code false} otherwise. */ - private boolean helpCommands(final String sender, final String topic) { + private boolean helpCommands(final String sender, final String topic, final boolean isPrivate) { for (final AbstractCommand command : commands) { if (command.isVisible() && command.getCommand().startsWith(topic)) { - return command.helpResponse(this, topic, sender, isOp(sender), true); + return command.helpResponse(this, topic, sender, isOp(sender), isPrivate); } } return false; @@ -613,14 +614,18 @@ public class Mobibot extends PircBot { /** * Responds with the default Constants. * - * @param sender The nick of the person requesting Constants. - * @param isOp The channel operator flag. + * @param sender The nick of the person requesting Constants. + * @param isOp The channel operator flag. + * @param isPrivate The private flag. */ - public void helpDefault(final String sender, final boolean isOp) { - send(sender, Utils.bold("Type a URL on " + ircChannel + " to post it.")); - send(sender, "For more information on a specific command, type:"); - send(sender, Utils.helpIndent(getNick() + ": " + Constants.HELP_CMD + " <command>")); - send(sender, Utils.bold("The commands are:")); + public void helpDefault(final String sender, final boolean isOp, final boolean isPrivate) { + send(sender, "Type a URL on " + ircChannel + " to post it.", isPrivate); + send(sender, "For more information on a specific command, type:", isPrivate); + send(sender, + Utils.helpIndent( + ((isPrivate) ? "/msg " + getNick() : getNick() + ':') + " " + Constants.HELP_CMD + " <command>"), + isPrivate); + send(sender, "The commands are:", isPrivate); if (commandsNames.isEmpty()) { // Feed command @@ -650,26 +655,29 @@ public class Mobibot extends PircBot { Collections.sort(opsCommandsNames); } - sendCommandsList(sender, commandsNames, false); + sendCommandsList(sender, commandsNames, 8, isPrivate, true); if (isOp) { - send(sender, Utils.bold("The op commands are:"), false); - sendCommandsList(sender, opsCommandsNames, false); + send(sender, "The op commands are:", isPrivate); + sendCommandsList(sender, opsCommandsNames, 8, isPrivate, true); } } /** * Responds with the modules help, if any. * - * @param sender The nick of the person requesting Constants. - * @param topic The help topic. + * @param sender The nick of the person requesting Constants. + * @param topic The help topic. + * @param isPrivate The private flag. * @return {@code true} if the topic was found, {@code false} otherwise. */ - private boolean helpModules(final String sender, final String topic) { + private boolean helpModules(final String sender, final String topic, final boolean isPrivate) { for (final AbstractModule module : modules) { - for (final String cmd : module.getCommands()) { - if (topic.equals(cmd)) { - module.helpResponse(this, sender, topic, true); - return true; + if (module.isEnabled()) { + for (final String cmd : module.getCommands()) { + if (topic.equals(cmd)) { + module.helpResponse(this, sender, isPrivate); + return true; + } } } } @@ -679,24 +687,25 @@ public class Mobibot extends PircBot { /** * Responds with the bot's Constants. * - * @param sender The nick of the person who sent the private message. - * @param topic The help topic, if any. + * @param sender The nick of the person who sent the private message. + * @param topic The help topic, if any. + * @param isPrivate The private flag. */ - private void helpResponse(final String sender, final String topic) { + private void helpResponse(final String sender, final String topic, final boolean isPrivate) { final boolean isOp = isOp(sender); if (StringUtils.isBlank(topic)) { - helpDefault(sender, isOp); + helpDefault(sender, isOp, isPrivate); } else { final String lcTopic = topic.toLowerCase(Constants.LOCALE).trim(); if (lcTopic.equals(getChannelName())) { - send(sender, Utils.bold("To list the last 5 posts from the channel's weblog:")); - send(sender, Utils.helpIndent(getNick() + ": " + getChannelName())); + send(sender, "To list the last 5 posts from the channel's weblog:", isPrivate); + send(sender, Utils.helpIndent(getNick() + ": " + getChannelName()), isPrivate); } else if (Tell.TELL_CMD.equals(lcTopic) && tell.isEnabled()) { - tell.helpResponse(sender); + tell.helpResponse(sender, isPrivate); } else { // Command, Modules or Default - if (!helpCommands(sender, topic) && !helpModules(sender, lcTopic)) { - helpDefault(sender, isOp); + if (!helpCommands(sender, topic, isPrivate) && !helpModules(sender, lcTopic, isPrivate)) { + helpDefault(sender, isOp, isPrivate); } } } @@ -782,11 +791,11 @@ public class Mobibot extends PircBot { } if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help - helpResponse(sender, args); + helpResponse(sender, args, false); } else if (cmd.equalsIgnoreCase(getChannelName())) { // mobibot: <channel> feedResponse(sender); } else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) { // mobibot: tell - tell.response(sender, args); + tell.response(sender, args, false); } else { boolean skip = false; // Commands @@ -850,12 +859,12 @@ public class Mobibot extends PircBot { final boolean isOp = isOp(sender); if (cmd.startsWith(Constants.HELP_CMD)) { - helpResponse(sender, args); + helpResponse(sender, args, true); } else if (isOp && "kill".equals(cmd)) { sendRawLine("QUIT : Poof!"); System.exit(0); } else if (isOp && Constants.DIE_CMD.equals(cmd)) { - send(ircChannel, sender + " has just signed my death sentence."); + send(sender + " has just signed my death sentence."); timer.cancel(); twitterShutdown(); twitterNotification("killed by " + sender + " on " + ircChannel); @@ -870,10 +879,10 @@ public class Mobibot extends PircBot { UrlMgr.addHistory(0, args); send(sender, UrlMgr.getHistory().toString(), true); } else { - send(sender, "The specified log could not be found."); + send(sender, "The specified log could not be found.", true); } } else if (Tell.TELL_CMD.equals(cmd) && tell.isEnabled()) { - tell.response(sender, args); + tell.response(sender, args, true); } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { if (logger.isDebugEnabled()) { Configurator.setLevel(logger.getName(), loggerLevel); @@ -898,7 +907,7 @@ public class Mobibot extends PircBot { } } } - helpDefault(sender, isOp); + helpDefault(sender, isOp, true); } } @@ -961,20 +970,10 @@ public class Mobibot extends PircBot { * @param notice The notice message. */ public final void send(final String notice) { - send(getChannel(), notice); + send(getChannel(), notice, false); } - /** - * Sends a message. - * - * @param who The channel or nick of the person who sent the command. - * @param message The actual message. - */ - public final void send(final String who, final String message) { - send(who, message, false); - } - /** * Sends a message. * @@ -997,29 +996,23 @@ public class Mobibot extends PircBot { send(who, Utils.colorize(message, color), isPrivate); } - /** - * Sends a message. - * - * @param who The channel or nick of the person who sent the command. - * @param message The actual message. - * @param color The message's color. - */ - @SuppressWarnings("unused") - public final void send(final String who, final String message, final String color) { - send(who, Utils.colorize(message, color), false); - } - /** * Send a formatted commands/modules, etc. list. * - * @param nick The nick to send the list to. - * @param list The list to format. + * @param nick The nick to send the list to. + * @param list The list to format. + * @param size The number of items per line. + * @param isPrivate The private flag. + * @param isBold The bold flag */ - public final void sendCommandsList(final String nick, final List<String> list, final boolean isPrivate) { - final int chunk = 8; // 8 commands per line. - for (int i = 0; i < list.size(); i += chunk) { + public final void sendCommandsList(final String nick, + final List<String> list, + final int size, + final boolean isPrivate, + final boolean isBold) { + for (int i = 0; i < list.size(); i += size) { send(nick, Utils.helpIndent( - String.join(" ", list.subList(i, Math.min(list.size(), i + chunk)))), isPrivate); + String.join(" ", list.subList(i, Math.min(list.size(), i + size))), isBold), isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index a7ccaa4..67c81c8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -164,9 +164,21 @@ public final class Utils { * * @param help The help string. * @return The indented help string. + * @see #helpIndent(String, boolean) */ public static String helpIndent(final String help) { - return " " + help; + return helpIndent(help, true); + } + + /** + * Returns indented help string. + * + * @param help The help string. + * @param isBold The bold flag. + * @return The indented help string. + */ + public static String helpIndent(final String help, final boolean isBold) { + return " " + (isBold ? bold(help) : help); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 4eeaaa8..4b42bc3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -53,7 +53,12 @@ abstract class AbstractCommand { open fun helpResponse(bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { if (!this.isOp || this.isOp == isOp) { for (h in help) { - bot.send(sender, String.format(h, bot.nick)) + if (isPrivate) { + bot.send(sender, String.format(h, "/msg ${bot.nick}"), true) + } else if (isPublic) { + bot.send(sender, String.format(h, "${bot.nick}:"), false) + } + } return true } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index fa18a1b..73c2d7c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -39,8 +39,8 @@ class Cycle : AbstractCommand() { private val wait = 10 override val command = "cycle" override val help = listOf( - Utils.bold("To have the bot leave the channel and come back:"), - Utils.helpIndent("/msg %s $command") + "To have the bot leave the channel and come back:", + Utils.helpIndent("%s $command") ) override val isOp = true override val isPublic = false @@ -56,13 +56,13 @@ class Cycle : AbstractCommand() { isPrivate: Boolean ) { if (isOp) { - bot.send(bot.channel, "$sender has just asked me to leave. I'll be back!") + bot.send("$sender has just asked me to leave. I'll be back!") bot.sleep(wait) bot.partChannel(bot.channel) bot.sleep(wait) bot.joinChannel(bot.channel) } else { - bot.helpDefault(sender, isOp) + bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index d24ccd3..6ad42ab 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -47,18 +47,18 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { override val command = IGNORE_CMD override val help = listOf( - Utils.bold("To ignore a link posted to the channel:"), + "To ignore a link posted to the channel:", Utils.helpIndent("https://www.foo.bar %s"), - Utils.bold("To check your ignore status:"), - Utils.helpIndent("%s: $command"), - Utils.bold("To toggle your ignore status:"), - Utils.helpIndent("%s: $command $me") + "To check your ignore status:", + Utils.helpIndent("%s $command"), + "To toggle your ignore status:", + Utils.helpIndent("%s $command $me") ) private val helpOp = listOf( - Utils.bold("To ignore a link posted to the channel:"), + "To ignore a link posted to the channel:", Utils.helpIndent("https://www.foo.bar %s"), - Utils.bold("To add/remove nicks from the ignored list:"), - Utils.helpIndent("/msg %s $command <nick>|$me [<nick> ...]") + "To add/remove nicks from the ignored list:", + Utils.helpIndent("%s $command <nick>|$me [<nick> ...]") ) override val isOp = false @@ -86,9 +86,9 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { if (!isOp) { val nick = sender.toLowerCase() val isMe = args.toLowerCase().startsWith(me) - ignoreNick(bot, nick, isMe) + ignoreNick(bot, nick, isMe, isPrivate) } else { - ignoreOp(bot, sender, args) + ignoreOp(bot, sender, args, isPrivate) } } @@ -101,7 +101,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { ): Boolean { return if (isOp) { for (h in helpOp) { - bot.send(sender, String.format(h, bot.nick)) + bot.send(sender, String.format(h, bot.nick), isPrivate) } true } else { @@ -109,24 +109,24 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { } } - private fun ignoreNick(bot: Mobibot, sender: String, isMe: Boolean) { + private fun ignoreNick(bot: Mobibot, sender: String, isMe: Boolean, isPrivate: Boolean) { if (isMe) { if (ignored.remove(sender)) { - bot.send(sender, "You are no longer ignored.") + bot.send(sender, "You are no longer ignored.", isPrivate) } else { ignored.add(sender) - bot.send(sender, "You are now ignored.") + bot.send(sender, "You are now ignored.", isPrivate) } } else { if (ignored.contains(sender)) { - bot.send(sender, "You are currently ignored.") + bot.send(sender, "You are currently ignored.", isPrivate) } else { - bot.send(sender, "You are not currently ignored.") + bot.send(sender, "You are not currently ignored.", isPrivate) } } } - private fun ignoreOp(bot: Mobibot, sender: String, args: String) { + private fun ignoreOp(bot: Mobibot, sender: String, args: String, isPrivate: Boolean) { if (args.isNotEmpty()) { val nicks = args.toLowerCase().split(" ") for (nick in nicks) { @@ -142,10 +142,10 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { } if (ignored.size > 0) { - bot.send(sender, Utils.bold("The following nicks are ignored:")) - bot.sendCommandsList(sender, ignored.toList(), false) + bot.send(sender, "The following nicks are ignored:", isPrivate) + bot.sendCommandsList(sender, ignored.toList(), 8, isPrivate, true) } else { - bot.send(sender, "No one is currently ${Utils.bold("ignored")}.") + bot.send(sender, "No one is currently ${Utils.bold("ignored")}.", isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt index 261e35e..8c1969c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -40,8 +40,8 @@ import java.lang.management.ManagementFactory class Info : AbstractCommand() { override val command = "info" override val help = listOf( - Utils.bold("To view information about the bot:"), - Utils.helpIndent("%s: $command") + "To view information about the bot:", + Utils.helpIndent("%s $command") ) override val isOp = false override val isPublic = true @@ -82,6 +82,6 @@ class Info : AbstractCommand() { append(']') } - bot.send(sender, info.toString()) + bot.send(sender, info.toString(), isPrivate) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt index a229009..75e4c1f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt @@ -38,8 +38,8 @@ import net.thauvin.erik.mobibot.Utils class Me : AbstractCommand() { override val command = "me" override val help = listOf( - Utils.bold("To have the bot perform an action:"), - Utils.helpIndent("/msg %s $command <action>") + "To have the bot perform an action:", + Utils.helpIndent("%s $command <action>") ) override val isOp = true override val isPublic = false @@ -56,7 +56,7 @@ class Me : AbstractCommand() { if (isOp) { bot.action(args) } else { - bot.helpDefault(sender, isOp) + bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index ae9492a..d14f437 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -38,8 +38,8 @@ import net.thauvin.erik.mobibot.Utils class Modules : AbstractCommand() { override val command = "modules" override val help = listOf( - Utils.bold("To view a list of enabled modules:"), - Utils.helpIndent("/msg %s $command") + "To view a list of enabled modules:", + Utils.helpIndent("%s $command") ) override val isOp = true override val isPublic = false @@ -58,11 +58,11 @@ class Modules : AbstractCommand() { if (modulesNames.isEmpty()) { bot.send(sender, "There are no enabled modules.", isPrivate) } else { - bot.send(sender, Utils.bold("The enabled modules are: "), false) - bot.sendCommandsList(sender, modulesNames, false ) + bot.send(sender, "The enabled modules are: ", isPrivate) + bot.sendCommandsList(sender, modulesNames, 7, isPrivate, false) } } else { - bot.helpDefault(sender, isOp) + bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt index 8c4cc2f..a65ef5c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt @@ -38,8 +38,8 @@ import net.thauvin.erik.mobibot.Utils class Msg : AbstractCommand() { override val command = "msg" override val help = listOf( - Utils.bold("To have the bot send a private message to someone:"), - Utils.helpIndent("/msg %s $command <nick> <text>") + "To have the bot send a private message to someone:", + Utils.helpIndent("%s $command <nick> <text>") ) override val isOp = true override val isPublic = true @@ -61,7 +61,7 @@ class Msg : AbstractCommand() { helpResponse(bot, command, sender, isOp, isPrivate) } } else { - bot.helpDefault(sender, isOp) + bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt index 41396ed..1ab8256 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt @@ -38,8 +38,8 @@ import net.thauvin.erik.mobibot.Utils class Nick : AbstractCommand() { override val command = "nick" override val help = listOf( - Utils.bold("To change the bot's nickname:"), - Utils.helpIndent("/msg %s $command <nick>") + "To change the bot's nickname:", + Utils.helpIndent("%s $command <nick>") ) override val isOp = true override val isPublic = true @@ -56,7 +56,7 @@ class Nick : AbstractCommand() { if (isOp) { bot.changeNick(args) } else { - bot.helpDefault(sender, isOp) + bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt index 2ffc255..c6c690a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -41,9 +41,8 @@ import java.util.* class Recap : AbstractCommand() { override val command = "recap" override val help = listOf( - Utils.bold("To list the last 10 public channel messages:"), - Utils.helpIndent("%s: $command"), - Utils.helpIndent("/msg %s $command") + "To list the last 10 public channel messages:", + Utils.helpIndent("%s $command") ) override val isOp = false override val isPublic = true @@ -89,7 +88,7 @@ class Recap : AbstractCommand() { bot.send(sender, r, isPrivate) } } else { - bot.send(sender, "Sorry, nothing to recap.", true) + bot.send(sender, "Sorry, nothing to recap.", isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt index c0b1e79..1ddb5d7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt @@ -38,8 +38,8 @@ import net.thauvin.erik.mobibot.Utils class Say : AbstractCommand() { override val command = "say" override val help = listOf( - Utils.bold("To have the bot say something on the channel:"), - Utils.helpIndent("/msg %s $command <text>") + "To have the bot say something on the channel:", + Utils.helpIndent("%s $command <text>") ) override val isOp = true override val isPublic = false @@ -55,9 +55,9 @@ class Say : AbstractCommand() { isPrivate: Boolean ) { if (isOp) { - bot.send(bot.channel, args, true) + bot.send(args) } else { - bot.helpDefault(sender, isOp) + bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index acfed1d..fad7358 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -40,8 +40,8 @@ import java.util.* class Users : AbstractCommand() { override val command = "users" override val help = listOf( - Utils.bold("To list the users present on the channel:"), - Utils.helpIndent("/msg %s $command") + "To list the users present on the channel:", + Utils.helpIndent("%s $command") ) override val isOp = false override val isPublic = true @@ -66,6 +66,6 @@ class Users : AbstractCommand() { } } - bot.send(sender, nicks.sorted().joinToString(" ")) + bot.send(sender, nicks.sorted().joinToString(" "), isPrivate) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 077c985..91f0044 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -60,8 +60,8 @@ public class Versions extends AbstractCommand { @NotNull @Override public List<String> getHelp() { - return List.of(Utils.bold("To view the versions data (bot, java, etc.):"), - Utils.helpIndent("/msg %s $command")); + return List.of("To view the versions data (bot, java, etc.):", + Utils.helpIndent("%s " + getCommand())); } @Override @@ -88,10 +88,10 @@ public class Versions extends AbstractCommand { final boolean isPrivate) { if (isOp) { for (final String v : versions) { - bot.send(sender, v); + bot.send(sender, v, isPrivate); } } else { - bot.helpDefault(sender, false); + bot.helpDefault(sender, false, isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 435de45..db996fc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -42,12 +42,12 @@ import net.thauvin.erik.mobibot.entries.EntryLink class Comment : AbstractCommand() { override val command = COMMAND override val help = listOf( - Utils.bold("To add a comment:"), + "To add a comment:", Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", - Utils.bold("To edit a comment, use its label: "), + "To edit a comment, use its label: ", Utils.helpIndent("${Constants.LINK_CMD}1.1:This is an edited comment"), - Utils.bold("To delete a comment, use its label and a minus sign: "), + "To delete a comment, use its label and a minus sign: ", Utils.helpIndent("${Constants.LINK_CMD}1.1:-") ) override val isOp = false @@ -75,9 +75,14 @@ class Comment : AbstractCommand() { if (commentIndex < entry.commentsCount) { when (val cmd = cmds[2].trim()) { "" -> showComment(bot, entry, index, commentIndex) // L1.1: - "-" -> deleteComment(bot, entry, index, commentIndex) // L11:- - "?" -> changeAuthor(bot, cmd, sender, isOp, entry, index, commentIndex) // L1.1:?<author> - else -> setComment(bot, cmd, sender, entry, index, commentIndex) + "-" -> deleteComment(bot, sender, isOp, entry, index, commentIndex) // L11:- + else -> { + if (cmd.startsWith('?')) { // L1.1:?<author> + changeAuthor(bot, cmd, sender, isOp, entry, index, commentIndex) + } else { // L1.1:<comment> + setComment(bot, cmd, sender, entry, index, commentIndex) + } + } } } } @@ -92,8 +97,8 @@ class Comment : AbstractCommand() { ): Boolean { if (super.helpResponse(bot, command, sender, isOp, isPrivate)) { if (isOp) { - bot.send(sender, Utils.bold("To change a comment's author:")) - bot.send(sender, Utils.helpIndent("/msg ${bot.nick} ${Constants.LINK_CMD}1.1:?<nick>")) + bot.send(sender, "To change a comment's author:", isPrivate) + bot.send(sender, Utils.helpIndent("/msg ${bot.nick} ${Constants.LINK_CMD}1.1:?<nick>"), isPrivate) } return true } @@ -116,27 +121,38 @@ class Comment : AbstractCommand() { if (isOp && cmd.length > 1) { val comment = entry.getComment(commentIndex) comment.nick = cmd.substring(1) - bot.send(bot.channel, EntriesUtils.buildComment(index, commentIndex, comment)) + bot.send(EntriesUtils.buildComment(index, commentIndex, comment)) UrlMgr.saveEntries(bot, false) } else { - bot.send(sender, "Please ask a channel op to change the author of this comment for you.") + bot.send(sender, "Please ask a channel op to change the author of this comment for you.", false) } } - private fun deleteComment(bot: Mobibot, entry: EntryLink, index: Int, commentIndex: Int) { - entry.deleteComment(commentIndex) - bot.send(bot.channel, "Comment ${Constants.LINK_CMD}${index + 1}.${commentIndex + 1} removed.") - UrlMgr.saveEntries(bot, false) + private fun deleteComment( + bot: Mobibot, + sender: String, + isOp: Boolean, + entry: EntryLink, + index: Int, + commentIndex: Int + ) { + if (isOp || sender == entry.getComment(commentIndex).nick) { + entry.deleteComment(commentIndex) + bot.send("Comment ${Constants.LINK_CMD}${index + 1}.${commentIndex + 1} removed.") + UrlMgr.saveEntries(bot, false) + } else { + bot.send(sender, "Please ask a channel op to delete this comment for you.", false) + } } private fun setComment(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int) { entry.setComment(commentIndex, cmd, sender) val comment = entry.getComment(commentIndex) - bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment)) + bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false) UrlMgr.saveEntries(bot, false) } private fun showComment(bot: Mobibot, entry: EntryLink, index: Int, commentIndex: Int) { - bot.send(bot.channel, EntriesUtils.buildComment(index, commentIndex, entry.getComment(commentIndex))) + bot.send(EntriesUtils.buildComment(index, commentIndex, entry.getComment(commentIndex))) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 7b9fed5..8a90987 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -42,16 +42,16 @@ import net.thauvin.erik.mobibot.entries.EntryLink class Posting : AbstractCommand() { override val command = "posting" override val help = listOf( - Utils.bold("Post a URL, by saying it on a line on its own:"), + "Post a URL, by saying it on a line on its own:", Utils.helpIndent("<url> [<title>] ${Tags.COMMAND}}: <+tag> [...]]"), - "I will reply with a label, for example:" + Utils.bold("${Constants.LINK_CMD}1"), - Utils.bold("To add a title, use its label and a pipe:"), + "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1", + "To add a title, use its label and a pipe:", Utils.helpIndent("${Constants.LINK_CMD}1:|This is the title"), - Utils.bold("To add a comment:"), + "To add a comment:", Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", - Utils.bold("To edit a comment, see: "), - Utils.helpIndent("/msg %s ${Constants.HELP_CMD} ${Comment.COMMAND}") + "To edit a comment, see: ", + Utils.helpIndent("%s ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOp = false override val isPublic = true @@ -77,7 +77,7 @@ class Posting : AbstractCommand() { '|' -> changeTitle(bot, cmd, index) // L1:|<title> '=' -> changeUrl(bot, cmd, login, isOp, index) // L1:=<url> '?' -> changeAuthor(bot, cmd, sender, isOp, index) // L1:?<author> - else -> addComment(bot, cmd, sender, index) + else -> addComment(bot, cmd, sender, index) // L1:<comment> } } } @@ -92,7 +92,7 @@ class Posting : AbstractCommand() { val entry: EntryLink = UrlMgr.getEntry(index) val commentIndex = entry.addComment(cmd, sender) val comment = entry.getComment(commentIndex) - bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment)) + bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false) UrlMgr.saveEntries(bot, false) } @@ -101,7 +101,7 @@ class Posting : AbstractCommand() { val entry: EntryLink = UrlMgr.getEntry(index) entry.title = cmd.substring(1).trim() bot.updatePin(entry.link, entry) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + bot.send(EntriesUtils.buildLink(index, entry)) UrlMgr.saveEntries(bot, false) } } @@ -114,7 +114,7 @@ class Posting : AbstractCommand() { val oldLink = entry.link entry.link = link bot.updatePin(oldLink, entry) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + bot.send(EntriesUtils.buildLink(index, entry)) UrlMgr.saveEntries(bot, false) } } @@ -125,11 +125,11 @@ class Posting : AbstractCommand() { if (cmd.length > 1) { val entry: EntryLink = UrlMgr.getEntry(index) entry.nick = cmd.substring(1) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + bot.send(EntriesUtils.buildLink(index, entry)) UrlMgr.saveEntries(bot, false) } } else { - bot.send(sender, "Please ask a channel op to change the author of this link for you.") + bot.send(sender, "Please ask a channel op to change the author of this link for you.", false) } } @@ -141,23 +141,23 @@ class Posting : AbstractCommand() { bot.twitterRemoveEntry(index) } UrlMgr.removeEntry(index) - bot.send(bot.channel, "Entry ${Constants.LINK_CMD}${index + 1} removed.") + bot.send("Entry ${Constants.LINK_CMD}${index + 1} removed.") UrlMgr.saveEntries(bot, false) } else { - bot.send(sender, "Please ask a channel op to remove this entry for you.") + bot.send(sender, "Please ask a channel op to remove this entry for you.", false) } } private fun showEntry(bot: Mobibot, index: Int) { val entry: EntryLink = UrlMgr.getEntry(index) - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + bot.send(EntriesUtils.buildLink(index, entry)) if (entry.hasTags()) { - bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) + bot.send(EntriesUtils.buildTags(index, entry)) } if (entry.hasComments()) { val comments = entry.comments for (i in comments.indices) { - bot.send(bot.channel, EntriesUtils.buildComment(index, i, comments[i])) + bot.send(EntriesUtils.buildComment(index, i, comments[i])) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index 204f41f..6978794 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -42,7 +42,7 @@ import net.thauvin.erik.mobibot.entries.EntryLink class Tags : AbstractCommand() { override val command = COMMAND override val help = listOf( - Utils.bold("To categorize or tag a URL, use its label and a T:"), + "To categorize or tag a URL, use its label and a T:", Utils.helpIndent("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") ) override val isOp = false @@ -71,16 +71,16 @@ class Tags : AbstractCommand() { if (entry.login == login || isOp) { entry.setTags(cmd) bot.updatePin(entry.link, entry) - bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) + bot.send(EntriesUtils.buildTags(index, entry)) UrlMgr.saveEntries(bot, false) } else { - bot.send(sender, "Please ask a channel op to change the tags for you.") + bot.send(sender, "Please ask a channel op to change the tags for you.",isPrivate) } } else { if (entry.hasTags()) { - bot.send(bot.channel, EntriesUtils.buildTags(index, entry)) + bot.send(EntriesUtils.buildTags(index, entry)) } else { - bot.send(sender, "The entry has no tags. Why don't add some?") + bot.send(sender, "The entry has no tags. Why don't add some?", isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index 89519ef..1daf17e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -128,7 +128,7 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { if (Ignore.isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(bot.nick))) { val link = cmds[0].trim() - if (!isDupEntry(bot, sender, link)) { + if (!isDupEntry(bot, sender, link, isPrivate)) { val isBackup = saveDayBackup(bot) val tags: StringBuilder = StringBuilder(defaultTags) var title = Constants.NO_TITLE @@ -145,7 +145,7 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { entries.add(EntryLink(link, title, sender, login, bot.channel, tags.toString())) val index: Int = entries.size - 1 val entry: EntryLink = entries[index] - bot.send(bot.channel, EntriesUtils.buildLink(index, entry)) + bot.send(EntriesUtils.buildLink(index, entry)) // Add Entry to pinboard. bot.addPin(entry) @@ -156,10 +156,11 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { saveEntries(bot, isBackup) if (Constants.NO_TITLE == entry.title) { - bot.send(sender, Utils.bold("Please specify a title, by typing:")) + bot.send(sender, "Please specify a title, by typing:", isPrivate) bot.send( sender, - Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title") + Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title"), + isPrivate ) } } @@ -196,12 +197,12 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { return title } - private fun isDupEntry(bot: Mobibot, sender: String, link: String): Boolean { + private fun isDupEntry(bot: Mobibot, sender: String, link: String, isPrivate: Boolean): Boolean { synchronized(entries) { for (i in entries.indices) { if (link == entries[i].link) { val entry: EntryLink = entries[i] - bot.send(sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry)) + bot.send(sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry), isPrivate) return true } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index 1bf601a..94ef295 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -45,8 +45,8 @@ class View : AbstractCommand() { private val maxEntries = 8 override val command = VIEW_CMD override val help = listOf( - Utils.bold("To list or search the current URL posts:"), - Utils.helpIndent("%s: $command [<start>] [<query>]") + "To list or search the current URL posts:", + Utils.helpIndent("%s $command [<start>] [<query>]") ) override val isOp = false override val isPublic = true @@ -67,7 +67,7 @@ class View : AbstractCommand() { if (entriesCount != 0) { showPosts(bot, args, sender) } else { - bot.send(sender, "There is currently nothing to view. Why don't you post something?") + bot.send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate) } } @@ -101,17 +101,17 @@ class View : AbstractCommand() { entry = getEntry(i) if (lcArgs.isNotBlank()) { if (entry.matches(lcArgs)) { - bot.send(sender, EntriesUtils.buildLink(i, entry, true)) + bot.send(sender, EntriesUtils.buildLink(i, entry, true), false) sent++ } } else { - bot.send(sender, EntriesUtils.buildLink(i, entry, true)) + bot.send(sender, EntriesUtils.buildLink(i, entry, true), false) sent++ } i++ if (sent == maxEntries && i < max) { bot.send( - sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs") + sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), false ) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index e8cee58..44938bf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -114,16 +114,19 @@ public class Tell { /** * Responds with Constants. * - * @param sender The sender. + * @param sender The sender. + * @param isPrivate The private flag. */ - public void helpResponse(final String sender) { - bot.send(sender, "To send a message to someone when they join the channel:"); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>")); + public void helpResponse(final String sender, final boolean isPrivate) { + bot.send(sender, "To send a message to someone when they join the channel:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>"), isPrivate); - bot.send(sender, "To view queued and sent messages:"); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + View.VIEW_CMD)); + bot.send(sender, "To view queued and sent messages:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + View.VIEW_CMD), isPrivate); - bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); + bot.send(sender, + "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), + isPrivate); } /** @@ -138,15 +141,16 @@ public class Tell { /** * Processes the commands. * - * @param sender The sender's nick. - * @param cmds The commands string. + * @param sender The sender's nick. + * @param cmds The commands string. + * @param isPrivate The private flag. */ @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.") - public void response(final String sender, final String cmds) { + public void response(final String sender, final String cmds, final boolean isPrivate) { final String arrow = " --> "; if (StringUtils.isBlank(cmds)) { - helpResponse(sender); + helpResponse(sender, isPrivate); } else if (cmds.startsWith(View.VIEW_CMD)) { if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) { if (!messages.isEmpty()) { @@ -154,10 +158,10 @@ public class Tell { bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - true); + isPrivate); } } else { - bot.send(sender, "There are no messages in the queue.", true); + bot.send(sender, "There are no messages in the queue.", isPrivate); } } else { boolean hasMessage = false; @@ -166,7 +170,7 @@ public class Tell { if (message.isMatch(sender)) { if (!hasMessage) { hasMessage = true; - bot.send(sender, "Here are your messages: ", true); + bot.send(sender, "Here are your messages: ", isPrivate); } if (message.isReceived()) { @@ -174,29 +178,30 @@ public class Tell { Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " + message.getId() + ", DELIVERED]", - true); + isPrivate); } else { bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " + message.getId() + ", QUEUED]", - true); + isPrivate); } - bot.send(sender, Utils.helpIndent(message.getMessage()), true); + bot.send(sender, Utils.helpIndent(message.getMessage()), isPrivate); } } if (!hasMessage) { - bot.send(sender, "You have no messages in the queue.", true); + bot.send(sender, "You have no messages in the queue.", isPrivate); } else { - bot.send(sender, "To delete one or all delivered messages:"); + bot.send(sender, "To delete one or all delivered messages:", isPrivate); bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" - + TELL_ALL_KEYWORD + '>')); - bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) - + Utils.plural(maxDays, " day.", " days.")); + + TELL_ALL_KEYWORD + '>'), isPrivate); + bot.send(sender, + "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), + isPrivate); } } } else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) { @@ -216,9 +221,9 @@ public class Tell { if (deleted) { save(); - bot.send(sender, "Delivered messages have been deleted.", true); + bot.send(sender, "Delivered messages have been deleted.", isPrivate); } else { - bot.send(sender, "No delivered messages were found.", true); + bot.send(sender, "No delivered messages were found.", isPrivate); } } else { @@ -231,7 +236,7 @@ public class Tell { messages.remove(message); save(); - bot.send(sender, "Your message was deleted from the queue.", true); + bot.send(sender, "Your message was deleted from the queue.", isPrivate); deleted = true; break; } @@ -239,14 +244,14 @@ public class Tell { if (!deleted) { if (found) { - bot.send(sender, "Only messages that you sent can be deleted.", true); + bot.send(sender, "Only messages that you sent can be deleted.", isPrivate); } else { - bot.send(sender, "The specified message [ID " + id + "] could not be found.", true); + bot.send(sender, "The specified message [ID " + id + "] could not be found.", isPrivate); } } } } else { - helpResponse(sender); + helpResponse(sender, isPrivate); } } else { final String[] split = cmds.split(" ", 2); @@ -265,7 +270,7 @@ public class Tell { bot.send(sender, "Sorry, the messages queue is currently full.", true); } } else { - helpResponse(sender); + helpResponse(sender, isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 671db96..12bee34 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -96,16 +96,13 @@ public abstract class AbstractModule { /** * Responds with the module's Constants. - * - * @param bot The bot's instance. + * @param bot The bot's instance. * @param sender The sender. - * @param args The help arguments. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ public abstract void helpResponse(final Mobibot bot, final String sender, - final String args, - @SuppressWarnings("unused") final boolean isPrivate); + final boolean isPrivate); /** * Returns <code>true</code> if the module is enabled. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 8201c8b..f9e1e2f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -40,8 +40,6 @@ import org.apache.commons.lang3.StringUtils; import java.text.DecimalFormat; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The Calc module. * @@ -74,7 +72,7 @@ public class Calc extends AbstractModule { try { final Expression calc = new ExpressionBuilder(query).build(); - return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()); + return query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate())); } catch (Exception e) { return "No idea. This is the kind of math I don't get."; } @@ -92,7 +90,7 @@ public class Calc extends AbstractModule { if (StringUtils.isNotBlank(args)) { bot.send(calc(args)); } else { - helpResponse(bot, sender, args, isPrivate); + helpResponse(bot, sender, isPrivate); } } @@ -100,8 +98,8 @@ public class Calc extends AbstractModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To solve a mathematical calculation:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>")); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To solve a mathematical calculation:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>"), isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 4388724..ec1d57c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -75,7 +75,7 @@ public final class Dice extends AbstractModule { bot.send(bot.getChannel(), sender + " rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " - + bold(playerTotal)); + + bold(playerTotal), isPrivate); i = r.nextInt(6) + 1; y = r.nextInt(6) + 1; @@ -97,8 +97,8 @@ public final class Dice extends AbstractModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To roll the dice:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + DICE_CMD)); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To roll the dice:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + DICE_CMD), isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index f40b5af..9e92783 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -52,8 +52,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The GoogleSearch module. * @@ -145,12 +143,12 @@ public final class GoogleSearch extends ThreadedModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { if (isEnabled()) { - bot.send(sender, bold("To search Google:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>")); + bot.send(sender, "To search Google:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>"), isPrivate); } else { - bot.send(sender, "The Google search module is disabled."); + bot.send(sender, "The Google search module is disabled.", isPrivate); } } @@ -158,7 +156,7 @@ public final class GoogleSearch extends ThreadedModule { * Searches Google. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String query) { + void run(final Mobibot bot, final String sender, final String cmd, final String query, final boolean isPrivate) { if (StringUtils.isNotBlank(query)) { try { final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), @@ -168,10 +166,10 @@ public final class GoogleSearch extends ThreadedModule { } } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); + bot.send(sender, e.getMessage(), isPrivate); } } else { - helpResponse(bot, sender, query, true); + helpResponse(bot, sender, isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 199e4e7..5f30ccd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -44,8 +44,6 @@ import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The Joke module. * @@ -107,19 +105,19 @@ public final class Joke extends ThreadedModule { final String cmd, final String args, final boolean isPrivate) { - new Thread(() -> run(bot, sender, cmd, args)).start(); + new Thread(() -> run(bot, sender, cmd, args, isPrivate)).start(); } /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String arg) { + void run(final Mobibot bot, final String sender, final String cmd, final String arg, final boolean isPrivate) { try { bot.send(Utils.cyan(randomJoke().getMessage())); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); + bot.send(sender, e.getMessage(), isPrivate); } } @@ -127,8 +125,8 @@ public final class Joke extends ThreadedModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To retrieve a random joke:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + JOKE_CMD)); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To retrieve a random joke:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + JOKE_CMD), isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 676ebbe..c1292f6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -41,8 +41,6 @@ import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The Lookup module. * @@ -189,7 +187,7 @@ public final class Lookup extends AbstractModule { } } } else { - helpResponse(bot, sender, args, true); + helpResponse(bot, sender, true); } } @@ -197,8 +195,8 @@ public final class Lookup extends AbstractModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To perform a DNS lookup query:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>")); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To perform a DNS lookup query:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>"), isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 4e689a8..6123898 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -39,8 +39,6 @@ import java.security.SecureRandom; import java.util.Arrays; import java.util.List; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The Ping module. * @@ -96,8 +94,8 @@ public class Ping extends AbstractModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To ping the bot:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + PING_CMD)); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To ping the bot:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + PING_CMD), isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java index 7119950..6bdc29f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java @@ -52,14 +52,18 @@ public abstract class ThreadedModule extends AbstractModule { final String args, final boolean isPrivate) { if (isEnabled() && args.length() > 0) { - new Thread(() -> run(bot, sender, cmd, args)).start(); + new Thread(() -> run(bot, sender, cmd, args, isPrivate)).start(); } else { - helpResponse(bot, sender, args, isPrivate); + helpResponse(bot, sender, isPrivate); } } /** * Runs the thread. */ - abstract void run(Mobibot bot, String sender, @SuppressWarnings("unused") String cmd, String args); + abstract void run(Mobibot bot, + String sender, + @SuppressWarnings("unused") String cmd, + String args, + boolean isPrivate); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index f3ed606..2d0c997 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -41,8 +41,6 @@ import twitter4j.Status; import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The Twitter module. * @@ -116,12 +114,12 @@ public final class Twitter extends ThreadedModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { if (isEnabled()) { - bot.send(sender, bold("To post to Twitter:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>")); + bot.send(sender, "To post to Twitter:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>"), isPrivate); } else { - bot.send(sender, "The Twitter posting facility is disabled."); + bot.send(sender, "The Twitter posting facility is " + Utils.bold("disabled") + '.', isPrivate); } } @@ -149,13 +147,14 @@ public final class Twitter extends ThreadedModule { * Posts to twitter. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String message) { + void run(final Mobibot bot, final String sender, final String cmd, final String message, final boolean isPrivate) { try { bot.send(sender, - post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMessage()); + post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMessage(), + isPrivate); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); + bot.send(sender, e.getMessage(), isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 9d6087e..a7b294b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -81,9 +81,8 @@ public final class War extends AbstractModule { i = r.nextInt(WAR_DECK.length); y = r.nextInt(WAR_DECK.length); - bot.send(bot.getChannel(), - sender + " drew the " + bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); - bot.action("drew the " + bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]); + bot.send(sender + " drew the " + bold(WAR_DECK[i]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); + bot.action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); if (i != y) { break; @@ -103,8 +102,8 @@ public final class War extends AbstractModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To play war:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WAR_CMD)); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To play war:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WAR_CMD), isPrivate); } } From 0fda50b4e175cad0a1bced8fd0d20f9d69c3d0b4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:39:55 -0700 Subject: [PATCH 365/842] Reworked. --- .../mobibot/modules/CurrencyConverter.java | 188 +++++++++--------- 1 file changed, 97 insertions(+), 91 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index c66f968..c9cfba1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -38,7 +38,6 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; import org.apache.commons.lang3.StringUtils; import org.jdom2.Document; @@ -51,13 +50,11 @@ import javax.xml.XMLConstants; import java.io.IOException; import java.net.URL; import java.text.NumberFormat; +import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.TreeMap; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The CurrentConverter module. * @@ -67,13 +64,12 @@ import static net.thauvin.erik.mobibot.Utils.bold; */ @SuppressWarnings("PMD.UseConcurrentHashMap") public final class CurrencyConverter extends ThreadedModule { - /** - * The rates keyword. - */ - static final String CURRENCY_RATES_KEYWORD = "rates"; - // Currency command private static final String CURRENCY_CMD = "currency"; + // Rates keyword + private static final String CURRENCY_RATES_KEYWORD = "rates"; + // Empty rate table. + private static final String EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty."; // Exchange rates private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); // Exchange rates table URL @@ -96,9 +92,54 @@ public final class CurrencyConverter extends ThreadedModule { * * @param query The query. * @return The {@link Message} contained the converted currency. - * @throws ModuleException If an error occurs while converting. */ - static Message convertCurrency(final String query) throws ModuleException { + static Message convertCurrency(final String query) { + final String[] cmds = query.split(" "); + + if (cmds.length == 4) { + if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) { + return new PublicMessage("You're kidding, right?"); + } else { + final String to = cmds[1].toUpperCase(Constants.LOCALE); + final String from = cmds[3].toUpperCase(Constants.LOCALE); + + if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) { + try { + final double amt = Double.parseDouble(cmds[0].replace(",", "")); + final double doubleFrom = Double.parseDouble(EXCHANGE_RATES.get(to)); + final double doubleTo = Double.parseDouble(EXCHANGE_RATES.get(from)); + + return new PublicMessage( + NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1) + + ' ' + + cmds[1].toUpperCase(Constants.LOCALE) + + " = " + + NumberFormat.getCurrencyInstance(Constants.LOCALE) + .format((amt * doubleTo) / doubleFrom) + .substring(1) + + ' ' + + cmds[3].toUpperCase(Constants.LOCALE)); + } catch (NumberFormatException e) { + return new ErrorMessage("Let's try with some real numbers next time, okay?"); + } + } else { + return new ErrorMessage("Sounds like monopoly money to me!"); + } + } + } + return new ErrorMessage("Invalid query. Let's try again."); + } + + static List<String> currencyRates() { + final List<String> rates = new ArrayList<>(33); + for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { + rates.add(" " + rate.getKey() + ": " + StringUtils.leftPad(rate.getValue(), 8)); + } + + return rates; + } + + static void loadRates() throws ModuleException { if (EXCHANGE_RATES.isEmpty()) { try { final SAXBuilder builder = new SAXBuilder(); @@ -116,71 +157,20 @@ public final class CurrencyConverter extends ThreadedModule { pubDate = cubeTime.getAttribute("time").getValue(); final List<Element> cubes = cubeTime.getChildren(); - Element cube; - for (final Element rawCube : cubes) { - cube = rawCube; + for (final Element cube : cubes) { EXCHANGE_RATES.put( cube.getAttribute("currency").getValue(), cube.getAttribute("rate").getValue()); } EXCHANGE_RATES.put("EUR", "1"); - } catch (JDOMException e) { - throw new ModuleException(query, "An error has occurred while parsing the exchange rates table.", e); - } catch (IOException e) { - throw new ModuleException( - query, "An error has occurred while fetching the exchange rates table.", e); + } catch (JDOMException | IOException e) { + throw new ModuleException(e.getMessage(), + "An error has occurred while parsing the exchange rates table.", + e); } } - - if (EXCHANGE_RATES.isEmpty()) { - return new ErrorMessage("Sorry, but the exchange rate table is empty."); - } else { - final String[] cmds = query.split(" "); - - if (cmds.length == 4) { - if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) { - return new ErrorMessage("You're kidding, right?"); - } else { - try { - final double amt = Double.parseDouble(cmds[0].replace(",", "")); - final double from = - Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase(Constants.LOCALE))); - final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase(Constants.LOCALE))); - - return new PublicMessage( - NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1) - + ' ' - + cmds[1].toUpperCase(Constants.LOCALE) - + " = " - + NumberFormat.getCurrencyInstance(Locale.US) - .format((amt * to) / from) - .substring(1) - + ' ' - + cmds[3].toUpperCase(Constants.LOCALE)); - } catch (Exception e) { - throw new ModuleException("convertCurrency(" + query + ')', - "The supported currencies are: " + EXCHANGE_RATES.keySet(), e); - } - } - } else if (CURRENCY_RATES_KEYWORD.equals(query)) { - - final StringBuilder buff = new StringBuilder().append('[').append(pubDate).append("]: "); - - int i = 0; - for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { - if (i > 0) { - buff.append(", "); - } - buff.append(rate.getKey()).append(": ").append(rate.getValue()); - i++; - } - - return new NoticeMessage(buff.toString()); - } - } - return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet()); } /** @@ -206,38 +196,54 @@ public final class CurrencyConverter extends ThreadedModule { */ @SuppressFBWarnings("REDOS") @Override - void run(final Mobibot bot, final String sender, final String cmd, final String query) { - if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(query)) { - if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { - try { - final Message msg = convertCurrency(query); - if (msg.isError()) { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false); - } - bot.send(sender, msg); - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage()); - } - } else { - helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true); + void run(final Mobibot bot, final String sender, final String cmd, final String query, final boolean isPrivate) { + if (EXCHANGE_RATES.isEmpty()) { + try { + loadRates(); + } catch (ModuleException e) { + bot.getLogger().warn(e.getDebugMessage(), e); } } + + if (EXCHANGE_RATES.isEmpty()) { + bot.send(sender, EMPTY_RATE_TABLE, true); + } else if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { + final Message msg = convertCurrency(query); + bot.send(sender, msg); + if (msg.isError()) { + helpResponse(bot, sender, isPrivate); + } + } else if (query.contains(CURRENCY_RATES_KEYWORD)) { + bot.send(sender, "The currency rates for " + Utils.bold(pubDate) + " are:", isPrivate); + bot.sendCommandsList(sender, currencyRates(), 3, isPrivate, false); + } else { + helpResponse(bot, sender, isPrivate); + } } /** * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To convert from one currency to another:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]")); - - if (args.endsWith(CURRENCY_CMD)) { - bot.send(sender, bold("For a listing of currency rates:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD); - bot.send(sender, bold("For a listing of supported currencies:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD)); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + if (EXCHANGE_RATES.isEmpty()) { + try { + loadRates(); + } catch (ModuleException e) { + bot.getLogger().debug(e.getDebugMessage(), e); + } + } + if (EXCHANGE_RATES.isEmpty()) { + bot.send(sender, EMPTY_RATE_TABLE, isPrivate); + } else { + bot.send(sender, "To convert from one currency to another:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " 100 USD to EUR"), isPrivate); + bot.send(sender, "For a listing of current rates:", isPrivate); + bot.send(sender, + Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD, + isPrivate); + bot.send(sender, "The supported currencies are: ", isPrivate); + bot.sendCommandsList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false); } } } From 07ab869388023c2544008379bec5742ca4a2f145 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:40:07 -0700 Subject: [PATCH 366/842] Reworked. --- .../erik/mobibot/modules/RockPaperScissors.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index a3de8e5..061f08e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -100,20 +100,21 @@ class RockPaperScissors : AbstractModule() { } else -> { bot.action( - "${green(botHand.name)} ${bold(botHand.action)} ${red(botHand.name)} ~ You lose ~" + "${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)} ~ You lose ~" ) } } } - override fun helpResponse(bot: Mobibot, sender: String, args: String?, isPrivate: Boolean) { - bot.send(sender, bold("To play Rock Paper Scissors:")) + override fun helpResponse(bot: Mobibot, sender: String, isPrivate: Boolean) { + bot.send(sender, "To play Rock Paper Scissors:", isPrivate) bot.send( sender, helpIndent( - "${bot.nick}: ${Hands.ROCK.name.toLowerCase()} or ${Hands.PAPER.name.toLowerCase()}" - + " or ${Hands.SCISSORS.name.toLowerCase()}" - ) + "${bot.nick}: ${Hands.ROCK.name.toLowerCase()} | ${Hands.PAPER.name.toLowerCase()}" + + " | ${Hands.SCISSORS.name.toLowerCase()}" + ), + isPrivate ) } } From ced0aa97f88925c2063ab4e74d4a92d301579194 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:40:50 -0700 Subject: [PATCH 367/842] Fixed isPrivate() usage. --- .../thauvin/erik/mobibot/modules/StockQuote.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 72e1517..925c719 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -52,8 +52,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The StockQuote module. * @@ -206,16 +204,16 @@ public final class StockQuote extends ThreadedModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To retrieve a stock quote:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>")); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To retrieve a stock quote:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>"), isPrivate); } /** - * Returns the specified stock quote from Alpha Advantage. + * Returns the specified stock quote from Alpha Avantage. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String symbol) { + void run(final Mobibot bot, final String sender, final String cmd, final String symbol, final boolean isPrivate) { if (StringUtils.isNotBlank(symbol)) { try { final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); @@ -227,7 +225,7 @@ public final class StockQuote extends ThreadedModule { bot.send(e.getMessage()); } } else { - helpResponse(bot, sender, symbol, true); + helpResponse(bot, sender, isPrivate); } } } From 968823828a157cbbe6fa72dc2068ecd81f4e3c90 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:41:48 -0700 Subject: [PATCH 368/842] Cleanup. --- .../erik/mobibot/modules/Weather2.java | 38 +++++++++-------- .../erik/mobibot/modules/WorldTime.java | 41 +++++++++++-------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 53822e1..1463e36 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -92,7 +92,7 @@ public class Weather2 extends ThreadedModule { @SuppressWarnings("AvoidEscapedUnicodeCharacters") private static String getTemps(final Double d) { final double c = (d - 32) * 5 / 9; - return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; + return Math.round(d) + " °F, " + Math.round(c) + " °C"; } /** @@ -138,7 +138,8 @@ public class Weather2 extends ThreadedModule { cwd = owm.currentWeatherByCityName(city, getCountry(country)); } if (cwd.hasCityName()) { - messages.add(new PublicMessage("City: " + cwd.getCityName() + " [" + country + "]")); + messages.add(new PublicMessage( + "City: " + cwd.getCityName() + " [" + StringUtils.upperCase(country) + "]")); final Main main = cwd.getMainData(); if (main != null) { @@ -159,15 +160,13 @@ public class Weather2 extends ThreadedModule { } if (cwd.hasWeatherList()) { - final StringBuilder condition = new StringBuilder("Condition: "); + final StringBuilder condition = new StringBuilder("Condition:"); final List<Weather> list = cwd.getWeatherList(); if (list != null) { for (final Weather w : list) { - if (condition.indexOf(",") == -1) { - condition.append(StringUtils.capitalize(w.getDescription())); - } else { - condition.append(", ").append(w.getDescription()); - } + condition.append(' ') + .append(StringUtils.capitalize(w.getDescription())) + .append('.'); } messages.add(new NoticeMessage(condition.toString())); } @@ -182,7 +181,8 @@ public class Weather2 extends ThreadedModule { "https://openweathermap.org/find")) .newBuilder() .addQueryParameter("q", - city + ',' + country) + city + ',' + + StringUtils.upperCase(country)) .build(); messages.add(new NoticeMessage(url.toString(), Colors.GREEN)); } @@ -210,26 +210,28 @@ public class Weather2 extends ThreadedModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To display weather information:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]")); - bot.send(sender, "For example:"); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To display weather information:", isPrivate); + bot.send(sender, + Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]"), + isPrivate); + bot.send(sender, "For example:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr"), isPrivate); bot.send(sender, "The default ISO 3166 country code is " + bold("US") - + ". Zip codes are supported in most countries."); + + ". Zip codes are supported in most countries.", isPrivate); } /** * Fetches the weather data from a specific city. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String args) { + void run(final Mobibot bot, final String sender, final String cmd, final String args, final boolean isPrivate) { if (StringUtils.isNotBlank(args)) { try { final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); if (messages.get(0).isError()) { - helpResponse(bot, sender, args, true); + helpResponse(bot, sender, isPrivate); } else { for (final Message msg : messages) { bot.send(sender, msg); @@ -240,7 +242,7 @@ public class Weather2 extends ThreadedModule { bot.send(e.getMessage()); } } else { - helpResponse(bot, sender, args, true); + helpResponse(bot, sender, isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index a19f33e..bac4eb0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -44,12 +44,11 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; +import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.TreeMap; -import static net.thauvin.erik.mobibot.Utils.bold; - /** * The WorldTime module. * @@ -195,14 +194,16 @@ public final class WorldTime extends AbstractModule { if (tz != null) { if (BEATS_KEYWORD.equals(tz)) { - response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD); + response = ("The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD)); } else { response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( - DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '")) - + tz.substring(tz.indexOf('/') + 1).replace('_', ' '); + DateTimeFormatter + .ofPattern("'The time is " + Utils.bold("'HH:mm'") + " on " + Utils.bold( + "'EEEE, d MMMM yyyy'") + " in '")) + + Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' ')); } } else { - return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet()); + return new ErrorMessage("Unsupported country/zone. Please try again."); } return new PublicMessage(response); @@ -217,15 +218,19 @@ public final class WorldTime extends AbstractModule { final String cmd, final String args, final boolean isPrivate) { - final Message msg = worldTime(args); - - if (isPrivate) { - bot.send(sender, msg.getMessage(), true); + if (args.length() == 0) { + bot.send(sender, "The supported countries/zones are: ", isPrivate); + bot.sendCommandsList(sender, new ArrayList<>(COUNTRIES_MAP.keySet()), 17, false, false); } else { - if (msg.isError()) { - bot.send(sender, msg.getMessage()); + final Message msg = worldTime(args); + if (isPrivate) { + bot.send(sender, msg.getMessage(), true); } else { - bot.send(msg.getMessage()); + if (msg.isError()) { + bot.send(sender, msg.getMessage(), false); + } else { + bot.send(msg.getMessage()); + } } } } @@ -234,12 +239,12 @@ public final class WorldTime extends AbstractModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, bold("To display a country's current date/time:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]"); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + bot.send(sender, "To display a country's current date/time:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]", isPrivate); - bot.send(sender, bold("For a listing of the supported countries:")); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD)); + bot.send(sender, "For a listing of the supported countries:", isPrivate); + bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD), isPrivate); } /** From 1035f5ba05a6d2b348d07c7f0e09ea1dda3155b9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:42:07 -0700 Subject: [PATCH 369/842] Updated tests. --- detekt-baseline.xml | 4 +++ .../erik/mobibot/modules/CalcTest.java | 7 ++-- .../modules/CurrencyConverterTest.java | 36 ++++++++++--------- .../erik/mobibot/modules/WordTimeTest.java | 3 +- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 4e8f359..8af6eed 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -4,13 +4,17 @@ <Whitelist> <ID>LongParameterList:AbstractCommand.kt$AbstractCommand$( bot: Mobibot, sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> + <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID> <ID>MagicNumber:Cycle.kt$Cycle$10</ID> + <ID>MagicNumber:Ignore.kt$Ignore$8</ID> + <ID>MagicNumber:Modules.kt$Modules$7</ID> <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> <ID>MagicNumber:UrlMgr.kt$UrlMgr$1000L</ID> <ID>MagicNumber:UrlMgr.kt$UrlMgr$60L</ID> <ID>MagicNumber:View.kt$View$8</ID> + <ID>NestedBlockDepth:Comment.kt$Comment$commandResponse</ID> <ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID> </Whitelist> </SmellBaseline> diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java index 0d5de03..c108282 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Utils; import org.testng.annotations.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -46,9 +47,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class CalcTest { @Test public void testCalc() { - assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = 2"); - assertThat(Calc.calc("1 -3")).as("calc(1 -3)").isEqualTo("1-3 = -2"); - assertThat(Calc.calc("pi+π+e+φ")).as("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = 10.62"); + assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)); + assertThat(Calc.calc("1 -3")).as("calc(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)); + assertThat(Calc.calc("pi+π+e+φ")).as("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")); assertThat(Calc.calc("one + one")).as("calc(one + one)").startsWith("No idea."); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index a437408..9291c4f 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.msg.ErrorMessage; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -46,26 +46,28 @@ import static org.assertj.core.api.Assertions.assertThat; * @since 1.0 */ public class CurrencyConverterTest { - @Test - public void testCurrencyConvertererImpl() { - AbstractModuleTest.testAbstractModule(new CurrencyConverter()); - } - - @Test(expectedExceptions = ModuleException.class) - public void testException() throws ModuleException { - CurrencyConverter.convertCurrency("100 BLA to USD"); + @BeforeClass + public void before() throws ModuleException { + CurrencyConverter.loadRates(); } @Test @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") - public void testConvertCurrency() throws ModuleException { + public void testConvertCurrency() { assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMessage()) - .as("100 USD to EUR").startsWith("100.00 USD = "); - assertThat(CurrencyConverter.convertCurrency("100 USD to USD")) - .as("100 USD to USD").isInstanceOf(ErrorMessage.class); - assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).isNotice()) - .as(CurrencyConverter.CURRENCY_RATES_KEYWORD + " is notice").isTrue(); - assertThat(CurrencyConverter.convertCurrency(CurrencyConverter.CURRENCY_RATES_KEYWORD).getMessage()) - .as(CurrencyConverter.CURRENCY_RATES_KEYWORD).contains("USD: "); + .as("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR"); + assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getMessage()) + .as("100 USD to USD").contains("You're kidding, right?"); + assertThat(CurrencyConverter.convertCurrency("100 USD").getMessage()) + .as("100 USD").contains("Invalid query."); + assertThat(CurrencyConverter.currencyRates().size()) + .as("currencyRates().size() == 33").isEqualTo(33); + assertThat(CurrencyConverter.currencyRates()) + .as("currencyRates().get(EUR)").contains(" EUR: 1"); + } + + @Test + public void testCurrencyConvertererImpl() { + AbstractModuleTest.testAbstractModule(new CurrencyConverter()); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java index b7cfc43..7f623ff 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import net.thauvin.erik.mobibot.Utils; import org.testng.annotations.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -46,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class WordTimeTest { @Test public void testWorldTime() { - assertThat(WorldTime.worldTime("PST").getMessage()).as("PST").endsWith("Los Angeles"); + assertThat(WorldTime.worldTime("PST").getMessage()).as("PST").endsWith(Utils.bold("Los Angeles")); assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue(); assertThat(WorldTime.worldTime("BEATS").getMessage()).as("BEATS").contains("@"); } From 08260a632b4325b425aad2f85f21253541f8174a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 15:42:34 -0700 Subject: [PATCH 370/842] Fixed generic exception catching. --- .../thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java index c03af95..fd98c72 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java @@ -100,9 +100,7 @@ final class TellMessagesMgr { } } catch (FileNotFoundException ignore) { // Do nothing - } catch (IOException e) { - logger.error("An IO error occurred loading the messages queue.", e); - } catch (Exception e) { + } catch (IOException | ClassNotFoundException e) { logger.error("An error occurred loading the messages queue.", e); } From b0b881e22bb69615ae6c69ce98a9d7cae14bcbc1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 17:13:49 -0700 Subject: [PATCH 371/842] Added AddLog command. --- .../net/thauvin/erik/mobibot/Constants.java | 4 -- .../net/thauvin/erik/mobibot/Mobibot.java | 41 +++++------- .../thauvin/erik/mobibot/commands/AddLog.kt | 67 +++++++++++++++++++ 3 files changed, 83 insertions(+), 29 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index 411c688..9943398 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -42,10 +42,6 @@ import java.util.Locale; * @since 1.0 */ public final class Constants { - /** - * The add (back)log command. - */ - public static final String ADDLOG_CMD = "addlog"; /** * The connect/read timeout in ms. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 63a6e4d..cf460ea 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.commands.AbstractCommand; +import net.thauvin.erik.mobibot.commands.AddLog; import net.thauvin.erik.mobibot.commands.Cycle; import net.thauvin.erik.mobibot.commands.Ignore; import net.thauvin.erik.mobibot.commands.Info; @@ -85,7 +86,6 @@ import org.apache.logging.log4j.core.config.Configurator; import org.jibble.pircbot.PircBot; import org.jibble.pircbot.User; -import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -240,6 +240,7 @@ public class Mobibot extends PircBot { ignoreCommand = new Ignore(p.getProperty("ignore", "")); // Load the commands + commands.add(new AddLog()); commands.add(new Cycle()); commands.add(ignoreCommand); commands.add(new Info()); @@ -858,38 +859,28 @@ public class Mobibot extends PircBot { final boolean isOp = isOp(sender); - if (cmd.startsWith(Constants.HELP_CMD)) { + if (cmd.startsWith(Constants.HELP_CMD)) { // help helpResponse(sender, args, true); - } else if (isOp && "kill".equals(cmd)) { + } else if (isOp && "kill".equals(cmd)) { // kill sendRawLine("QUIT : Poof!"); System.exit(0); - } else if (isOp && Constants.DIE_CMD.equals(cmd)) { - send(sender + " has just signed my death sentence."); - timer.cancel(); - twitterShutdown(); - twitterNotification("killed by " + sender + " on " + ircChannel); - - sleep(3); - quitServer("The Bot Is Out There!"); - System.exit(0); - } else if (isOp && (cmds.length > 1) && Constants.ADDLOG_CMD.equals(cmd)) { - // e.g 2014-04-01 - final File backlog = new File(logsDir + args + EntriesMgr.XML_EXT); - if (backlog.exists()) { - UrlMgr.addHistory(0, args); - send(sender, UrlMgr.getHistory().toString(), true); - } else { - send(sender, "The specified log could not be found.", true); - } - } else if (Tell.TELL_CMD.equals(cmd) && tell.isEnabled()) { - tell.response(sender, args, true); - } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { + } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug if (logger.isDebugEnabled()) { Configurator.setLevel(logger.getName(), loggerLevel); } else { Configurator.setLevel(logger.getName(), Level.DEBUG); } send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); + } else if (isOp && Constants.DIE_CMD.equals(cmd)) { // die + send(sender + " has just signed my death sentence."); + timer.cancel(); + twitterShutdown(); + twitterNotification("killed by " + sender + " on " + ircChannel); + sleep(3); + quitServer("The Bot Is Out There!"); + System.exit(0); + } else if (Tell.TELL_CMD.equals(cmd) && tell.isEnabled()) { // tell + tell.response(sender, args, true); } else { for (final AbstractCommand command : commands) { if (command.getCommand().startsWith(cmd)) { @@ -1054,7 +1045,7 @@ public class Mobibot extends PircBot { */ final void setPinboardAuth(final String apiToken) { if (isNotBlank(apiToken)) { - pinboard = new Pinboard(this, apiToken, ircServer); + pinboard = new Pinboard(this, apiToken); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt new file mode 100644 index 0000000..0c01306 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -0,0 +1,67 @@ +/* + * AddLog.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.addHistory +import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.getHistory +import net.thauvin.erik.mobibot.entries.EntriesMgr +import java.io.File + +class AddLog : AbstractCommand() { + override val command = "addlog" + override val help = emptyList<String>() + override val isOp = true + override val isPublic = false + override val isVisible = false + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp && args.isNotBlank()) { + // e.g: 2014-04-01 + val backlog = File("${bot.logsDir}$args${EntriesMgr.XML_EXT}") + if (backlog.exists()) { + addHistory(0, args) + bot.send(sender, getHistory().toString(), isPrivate) + } else { + bot.send(sender, "The specified log could not be found.", isPrivate) + } + } + } +} From 9da263847547964672e7a108341fb42e35f84bce Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 17:14:06 -0700 Subject: [PATCH 372/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/Pinboard.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index b9f538d..58dac14 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -60,11 +60,10 @@ class Pinboard { * * @param bot The bot's instance. * @param apiToken The API end point. - * @param ircServer The IRC server. */ - Pinboard(final Mobibot bot, final String apiToken, final String ircServer) { + Pinboard(final Mobibot bot, final String apiToken) { pinboard = new PinboardPoster(apiToken); - this.ircServer = ircServer; + ircServer = bot.getIrcServer(); if (bot.getLogger().isDebugEnabled()) { final ConsoleHandler consoleHandler = new ConsoleHandler(); From 30b7a80c494f67bfe44f8807362d824e66e53b65 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 18:00:25 -0700 Subject: [PATCH 373/842] Added response on no matches. --- src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index 94ef295..a99a3f6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -115,6 +115,8 @@ class View : AbstractCommand() { ) } } - + if (sent == 0) { + bot.send(sender, "No matches. Please try again.", false) + } } } From cfdbfca0a2ba7a3ad07949a5523f076f3c45588d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 18:00:57 -0700 Subject: [PATCH 374/842] Using StringUtils upper/lowerCase(). --- src/main/java/net/thauvin/erik/mobibot/Mobibot.java | 13 +++++++------ .../java/net/thauvin/erik/mobibot/Pinboard.java | 2 +- .../net/thauvin/erik/mobibot/commands/links/View.kt | 3 +-- .../net/thauvin/erik/mobibot/entries/EntryLink.java | 5 ++--- .../erik/mobibot/modules/CurrencyConverter.java | 10 ++++++---- .../net/thauvin/erik/mobibot/modules/WorldTime.java | 5 ++--- .../erik/mobibot/modules/LocalProperties.java | 4 ++-- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index cf460ea..4907b96 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -102,6 +102,7 @@ import java.util.Set; import java.util.Timer; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.lowerCase; /** * Implements the #mobitopia bot. @@ -360,7 +361,7 @@ public class Mobibot extends PircBot { System.exit(1); } - final String nickname = p.getProperty("nick", Mobibot.class.getName().toLowerCase(Constants.LOCALE)); + final String nickname = p.getProperty("nick", lowerCase(Mobibot.class.getName())); final String channel = p.getProperty("channel"); final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); @@ -567,8 +568,8 @@ public class Mobibot extends PircBot { for (final char c : getNick().toCharArray()) { if (Character.isLetter(c)) { - buff.append('[').append(String.valueOf(c).toLowerCase(Constants.LOCALE)).append( - String.valueOf(c).toUpperCase(Constants.LOCALE)).append(']'); + buff.append('[').append(lowerCase(String.valueOf(c))).append(StringUtils.upperCase(String.valueOf(c))) + .append(']'); } else { buff.append(c); } @@ -697,7 +698,7 @@ public class Mobibot extends PircBot { if (StringUtils.isBlank(topic)) { helpDefault(sender, isOp, isPrivate); } else { - final String lcTopic = topic.toLowerCase(Constants.LOCALE).trim(); + final String lcTopic = lowerCase(topic).trim(); if (lcTopic.equals(getChannelName())) { send(sender, "To list the last 5 posts from the channel's weblog:", isPrivate); send(sender, Utils.helpIndent(getNick() + ": " + getChannelName()), isPrivate); @@ -783,7 +784,7 @@ public class Mobibot extends PircBot { isCommand = true; final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); - final String cmd = cmds[0].toLowerCase(Constants.LOCALE); + final String cmd = lowerCase(cmds[0]); String args = ""; @@ -850,7 +851,7 @@ public class Mobibot extends PircBot { } final String[] cmds = message.split(" ", 2); - final String cmd = cmds[0].toLowerCase(Constants.LOCALE); + final String cmd = lowerCase(cmds[0]); String args = ""; if (cmds.length > 1) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 58dac14..61e58d8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -35,7 +35,7 @@ package net.thauvin.erik.mobibot; import net.thauvin.erik.mobibot.entries.EntryLink; import net.thauvin.erik.pinboard.PinboardPoster; -import javax.swing.SwingWorker; +import javax.swing.*; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index a99a3f6..c670a60 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.commands.links -import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.AbstractCommand @@ -73,7 +72,7 @@ class View : AbstractCommand() { private fun showPosts(bot: Mobibot, args: String, sender: String) { val max = entriesCount - var lcArgs = args.toLowerCase(Constants.LOCALE) + var lcArgs = args.toLowerCase() var i = 0 if (lcArgs.isEmpty() && max > maxEntries) { i = max - maxEntries diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 6e95c0c..af6986a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -35,7 +35,6 @@ package net.thauvin.erik.mobibot.entries; import com.rometools.rome.feed.synd.SyndCategory; import com.rometools.rome.feed.synd.SyndCategoryImpl; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Constants; import org.apache.commons.lang3.StringUtils; import java.io.Serializable; @@ -361,7 +360,7 @@ public class EntryLink implements Serializable { if (part.length() >= 2) { tag = new SyndCategoryImpl(); - tag.setName(part.substring(1).toLowerCase(Constants.LOCALE)); + tag.setName(StringUtils.lowerCase(part.substring(1))); mod = part.charAt(0); @@ -375,7 +374,7 @@ public class EntryLink implements Serializable { this.tags.add(tag); } } else { - tag.setName(part.trim().toLowerCase(Constants.LOCALE)); + tag.setName(StringUtils.lowerCase(part.trim())); if (!this.tags.contains(tag)) { this.tags.add(tag); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index c9cfba1..1fd2c7c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -55,6 +55,8 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import static org.apache.commons.lang3.StringUtils.upperCase; + /** * The CurrentConverter module. * @@ -100,8 +102,8 @@ public final class CurrencyConverter extends ThreadedModule { if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) { return new PublicMessage("You're kidding, right?"); } else { - final String to = cmds[1].toUpperCase(Constants.LOCALE); - final String from = cmds[3].toUpperCase(Constants.LOCALE); + final String to = upperCase(cmds[1]); + final String from = upperCase(cmds[3]); if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) { try { @@ -112,13 +114,13 @@ public final class CurrencyConverter extends ThreadedModule { return new PublicMessage( NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1) + ' ' - + cmds[1].toUpperCase(Constants.LOCALE) + + upperCase(cmds[1]) + " = " + NumberFormat.getCurrencyInstance(Constants.LOCALE) .format((amt * doubleTo) / doubleFrom) .substring(1) + ' ' - + cmds[3].toUpperCase(Constants.LOCALE)); + + upperCase(cmds[3])); } catch (NumberFormatException e) { return new ErrorMessage("Let's try with some real numbers next time, okay?"); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index bac4eb0..74dbc4a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -33,12 +33,12 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.PublicMessage; +import org.apache.commons.lang3.StringUtils; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -188,8 +188,7 @@ public final class WorldTime extends AbstractModule { */ @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") static Message worldTime(final String query) { - final String tz = (COUNTRIES_MAP.get((query.substring(query.indexOf(' ') + 1).trim() - .toUpperCase(Constants.LOCALE)))); + final String tz = (COUNTRIES_MAP.get((StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim())))); final String response; if (tz != null) { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java index 1a62f34..acd9dab 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules; -import net.thauvin.erik.mobibot.Constants; +import org.apache.commons.lang3.StringUtils; import org.testng.annotations.BeforeSuite; import java.io.IOException; @@ -65,7 +65,7 @@ class LocalProperties { } private static String keyToEnv(final String key) { - return key.replace('-', '_').toUpperCase(Constants.LOCALE); + return StringUtils.upperCase(key.replace('-', '_')); } @BeforeSuite(alwaysRun = true) From d9b1cbceffd3ff380c207018d85ce5d2c04d1232 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 19:19:31 -0700 Subject: [PATCH 375/842] Cleanup. --- .../net/thauvin/erik/mobibot/FeedReader.java | 5 +- .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- .../net/thauvin/erik/mobibot/Pinboard.java | 6 +- .../erik/mobibot/commands/tell/Tell.java | 258 ++++++++++-------- .../erik/mobibot/modules/GoogleSearch.java | 6 +- 5 files changed, 144 insertions(+), 133 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 99c68bd..9188758 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -52,9 +52,6 @@ class FeedReader implements Runnable { // Maximum number of feed items to display private static final int MAX_ITEMS = 5; - // Tab indent (4 spaces) - private static final String TAB_INDENT = " "; - // Bot private final Mobibot bot; @@ -94,7 +91,7 @@ class FeedReader implements Runnable { for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { item = items.get(i); bot.send(sender, item.getTitle(), false); - bot.send(sender, TAB_INDENT + Utils.green(item.getLink()), false); + bot.send(sender, Utils.helpIndent(Utils.green(item.getLink()), false), false); } } } catch (MalformedURLException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 4907b96..0ed1aa7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -141,7 +141,7 @@ public class Mobibot extends PircBot { // Tell object public final Tell tell; // Commands - private final List<AbstractCommand> commands = new ArrayList<>(); + private final List<AbstractCommand> commands = new ArrayList<>(20); // Commands Names private final List<String> commandsNames = new ArrayList<>(); // Main channel diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java index 61e58d8..0291ff3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java @@ -35,7 +35,7 @@ package net.thauvin.erik.mobibot; import net.thauvin.erik.mobibot.entries.EntryLink; import net.thauvin.erik.pinboard.PinboardPoster; -import javax.swing.*; +import javax.swing.SwingWorker; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -58,8 +58,8 @@ class Pinboard { /** * Creates a new {@link Pinboard} instance. * - * @param bot The bot's instance. - * @param apiToken The API end point. + * @param bot The bot's instance. + * @param apiToken The API end point. */ Pinboard(final Mobibot bot, final String apiToken) { pinboard = new PinboardPoster(apiToken); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index 44938bf..5d7ba31 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.commands.tell; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.commands.links.View; @@ -53,7 +52,8 @@ public class Tell { * The tell command. */ public static final String TELL_CMD = "tell"; - + // Arrow + private static final String ARROW = " --> "; // Default maximum number of days to keep messages private static final int DEFAULT_TELL_MAX_DAYS = 7; // Default message max queue size @@ -64,7 +64,6 @@ public class Tell { private static final String TELL_ALL_KEYWORD = "all"; //T he delete command. private static final String TELL_DEL_KEYWORD = "del"; - // Bot instance private final Mobibot bot; // Maximum number of days to keep messages @@ -111,6 +110,58 @@ public class Tell { return TellMessagesMgr.clean(messages, maxDays); } + // Delete message. + private void deleteMessage(final String sender, final String cmds, final boolean isPrivate) { + final String[] split = cmds.split(" "); + + if (split.length == 2) { + final String id = split[1]; + boolean deleted = false; + + if (TELL_ALL_KEYWORD.equalsIgnoreCase(id)) { + for (final TellMessage message : messages) { + if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) { + messages.remove(message); + deleted = true; + } + } + + if (deleted) { + save(); + bot.send(sender, "Delivered messages have been deleted.", isPrivate); + } else { + bot.send(sender, "No delivered messages were found.", isPrivate); + } + + } else { + boolean found = false; + + for (final TellMessage message : messages) { + found = message.isMatchId(id); + + if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender))) { + messages.remove(message); + + save(); + bot.send(sender, "Your message was deleted from the queue.", isPrivate); + deleted = true; + break; + } + } + + if (!deleted) { + if (found) { + bot.send(sender, "Only messages that you sent can be deleted.", isPrivate); + } else { + bot.send(sender, "The specified message [ID " + id + "] could not be found.", isPrivate); + } + } + } + } else { + helpResponse(sender, isPrivate); + } + } + /** * Responds with Constants. * @@ -138,6 +189,28 @@ public class Tell { return maxSize > 0 && maxDays > 0; } + // New message. + private void newMessage(final String sender, final String cmds, final boolean isPrivate) { + final String[] split = cmds.split(" ", 2); + + if (split.length == 2 && (StringUtils.isNotBlank(split[1]) && split[1].contains(" "))) { + if (messages.size() < maxSize) { + final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); + + messages.add(message); + + save(); + + bot.send(sender, "Message [ID " + message.getId() + "] was queued for " + + Utils.bold(message.getRecipient()), true); + } else { + bot.send(sender, "Sorry, the messages queue is currently full.", true); + } + } else { + helpResponse(sender, isPrivate); + } + } + /** * Processes the commands. * @@ -145,133 +218,19 @@ public class Tell { * @param cmds The commands string. * @param isPrivate The private flag. */ - @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", - justification = "Working on it.") public void response(final String sender, final String cmds, final boolean isPrivate) { - final String arrow = " --> "; if (StringUtils.isBlank(cmds)) { helpResponse(sender, isPrivate); } else if (cmds.startsWith(View.VIEW_CMD)) { if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) { - if (!messages.isEmpty()) { - for (final TellMessage message : messages) { - bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) - + " [ID: " + message.getId() + ", " - + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - isPrivate); - } - } else { - bot.send(sender, "There are no messages in the queue.", isPrivate); - } + viewAll(sender, isPrivate); } else { - boolean hasMessage = false; - - for (final TellMessage message : messages) { - if (message.isMatch(sender)) { - if (!hasMessage) { - hasMessage = true; - bot.send(sender, "Here are your messages: ", isPrivate); - } - - if (message.isReceived()) { - bot.send(sender, - Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " - + message.getId() + ", DELIVERED]", - isPrivate); - - } else { - bot.send(sender, - Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " - + message.getId() + ", QUEUED]", - isPrivate); - } - - bot.send(sender, Utils.helpIndent(message.getMessage()), isPrivate); - } - } - - if (!hasMessage) { - bot.send(sender, "You have no messages in the queue.", isPrivate); - } else { - bot.send(sender, "To delete one or all delivered messages:", isPrivate); - bot.send(sender, - Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" - + TELL_ALL_KEYWORD + '>'), isPrivate); - bot.send(sender, - "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), - isPrivate); - } + viewMessages(sender, isPrivate); } } else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) { - final String[] split = cmds.split(" "); - - if (split.length == 2) { - final String id = split[1]; - boolean deleted = false; - - if (TELL_ALL_KEYWORD.equalsIgnoreCase(id)) { - for (final TellMessage message : messages) { - if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) { - messages.remove(message); - deleted = true; - } - } - - if (deleted) { - save(); - bot.send(sender, "Delivered messages have been deleted.", isPrivate); - } else { - bot.send(sender, "No delivered messages were found.", isPrivate); - } - - } else { - boolean found = false; - - for (final TellMessage message : messages) { - found = message.isMatchId(id); - - if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender))) { - messages.remove(message); - - save(); - bot.send(sender, "Your message was deleted from the queue.", isPrivate); - deleted = true; - break; - } - } - - if (!deleted) { - if (found) { - bot.send(sender, "Only messages that you sent can be deleted.", isPrivate); - } else { - bot.send(sender, "The specified message [ID " + id + "] could not be found.", isPrivate); - } - } - } - } else { - helpResponse(sender, isPrivate); - } + deleteMessage(sender, cmds, isPrivate); } else { - final String[] split = cmds.split(" ", 2); - - if (split.length == 2 && (StringUtils.isNotBlank(split[1]) && split[1].contains(" "))) { - if (messages.size() < maxSize) { - final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); - - messages.add(message); - - save(); - - bot.send(sender, "Message [ID " + message.getId() + "] was queued for " - + Utils.bold(message.getRecipient()), true); - } else { - bot.send(sender, "Sorry, the messages queue is currently full.", true); - } - } else { - helpResponse(sender, isPrivate); - } + newMessage(sender, cmds, isPrivate); } if (clean()) { @@ -351,4 +310,61 @@ public class Tell { public int size() { return messages.size(); } + + // View all messages. + private void viewAll(final String sender, final boolean isPrivate) { + if (!messages.isEmpty()) { + for (final TellMessage message : messages) { + bot.send(sender, Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) + + " [ID: " + message.getId() + ", " + + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', + isPrivate); + } + } else { + bot.send(sender, "There are no messages in the queue.", isPrivate); + } + } + + // View messages. + private void viewMessages(final String sender, final boolean isPrivate) { + boolean hasMessage = false; + + for (final TellMessage message : messages) { + if (message.isMatch(sender)) { + if (!hasMessage) { + hasMessage = true; + bot.send(sender, "Here are your messages: ", isPrivate); + } + + if (message.isReceived()) { + bot.send(sender, + Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " + + Utils.bold(message.getId()) + ", DELIVERED]", + isPrivate); + + } else { + bot.send(sender, + Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " + + Utils.bold(message.getId()) + ", QUEUED]", + isPrivate); + } + + bot.send(sender, Utils.helpIndent(message.getMessage()), isPrivate); + } + } + + if (!hasMessage) { + bot.send(sender, "You have no messages in the queue.", isPrivate); + } else { + bot.send(sender, "To delete one or all delivered messages:", isPrivate); + bot.send(sender, + Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + + TELL_ALL_KEYWORD + '>'), isPrivate); + bot.send(sender, + "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), + isPrivate); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 9e92783..e8b6597 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -66,8 +66,6 @@ public final class GoogleSearch extends ThreadedModule { static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; // Google command private static final String GOOGLE_CMD = "google"; - // Tab indent (4 spaces) - private static final String TAB_INDENT = " "; /** * Creates a new {@link GoogleSearch} instance. @@ -126,7 +124,7 @@ public final class GoogleSearch extends ThreadedModule { final JSONObject j = ja.getJSONObject(i); results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); results.add( - new NoticeMessage(TAB_INDENT + j.getString("link"), Colors.DARK_GREEN)); + new NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN)); } } } catch (IOException e) { @@ -135,7 +133,7 @@ public final class GoogleSearch extends ThreadedModule { return results; } else { - throw new ModuleException("Invalid query."); + throw new ModuleException("Invalid query. Please try again."); } } From 0ec1e751069d41c2fe8b9f5d418a826a5209d603 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 21:58:54 -0700 Subject: [PATCH 376/842] Made Tell an actual command. --- .../net/thauvin/erik/mobibot/Mobibot.java | 18 +-- .../erik/mobibot/commands/tell/Tell.java | 120 ++++++++++-------- 2 files changed, 73 insertions(+), 65 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 0ed1aa7..a7d83c1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -258,6 +258,9 @@ public class Mobibot extends PircBot { commands.add(new Comment()); commands.add(new Posting()); commands.add(new Tags()); + // Get the tell command settings + tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); + commands.add(tell); commands.add(new UrlMgr(p.getProperty("tags", ""), p.getProperty("tags-keywords", ""))); commands.add(new View()); @@ -290,9 +293,6 @@ public class Mobibot extends PircBot { } }); - // Get the tell command settings - tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); - // Save the entries UrlMgr.saveEntries(this, true); } @@ -624,8 +624,7 @@ public class Mobibot extends PircBot { send(sender, "Type a URL on " + ircChannel + " to post it.", isPrivate); send(sender, "For more information on a specific command, type:", isPrivate); send(sender, - Utils.helpIndent( - ((isPrivate) ? "/msg " + getNick() : getNick() + ':') + " " + Constants.HELP_CMD + " <command>"), + Utils.helpIndent(Utils.botCommand(getNick(), isPrivate) + Constants.HELP_CMD + " <command>"), isPrivate); send(sender, "The commands are:", isPrivate); @@ -702,8 +701,6 @@ public class Mobibot extends PircBot { if (lcTopic.equals(getChannelName())) { send(sender, "To list the last 5 posts from the channel's weblog:", isPrivate); send(sender, Utils.helpIndent(getNick() + ": " + getChannelName()), isPrivate); - } else if (Tell.TELL_CMD.equals(lcTopic) && tell.isEnabled()) { - tell.helpResponse(sender, isPrivate); } else { // Command, Modules or Default if (!helpCommands(sender, topic, isPrivate) && !helpModules(sender, lcTopic, isPrivate)) { @@ -796,8 +793,6 @@ public class Mobibot extends PircBot { helpResponse(sender, args, false); } else if (cmd.equalsIgnoreCase(getChannelName())) { // mobibot: <channel> feedResponse(sender); - } else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled()) { // mobibot: tell - tell.response(sender, args, false); } else { boolean skip = false; // Commands @@ -820,8 +815,7 @@ public class Mobibot extends PircBot { } } } - } else { - // Commands + } else { // Commands for (final AbstractCommand command : commands) { if (command.matches(message)) { command.commandResponse(this, sender, login, message, isOp(sender), false); @@ -880,8 +874,6 @@ public class Mobibot extends PircBot { sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); - } else if (Tell.TELL_CMD.equals(cmd) && tell.isEnabled()) { // tell - tell.response(sender, args, true); } else { for (final AbstractCommand command : commands) { if (command.getCommand().startsWith(cmd)) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index 5d7ba31..c42fbf2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -34,8 +34,10 @@ package net.thauvin.erik.mobibot.commands.tell; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.commands.AbstractCommand; import net.thauvin.erik.mobibot.commands.links.View; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -47,7 +49,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * @created 2016-07-02 * @since 1.0 */ -public class Tell { +public class Tell extends AbstractCommand { /** * The tell command. */ @@ -111,8 +113,8 @@ public class Tell { } // Delete message. - private void deleteMessage(final String sender, final String cmds, final boolean isPrivate) { - final String[] split = cmds.split(" "); + private void deleteMessage(final String sender, final String args, final boolean isOp, final boolean isPrivate) { + final String[] split = args.split(" "); if (split.length == 2) { final String id = split[1]; @@ -158,26 +160,65 @@ public class Tell { } } } else { - helpResponse(sender, isPrivate); + helpResponse(bot, args, sender, isOp, isPrivate); } } - /** - * Responds with Constants. - * - * @param sender The sender. - * @param isPrivate The private flag. - */ - public void helpResponse(final String sender, final boolean isPrivate) { - bot.send(sender, "To send a message to someone when they join the channel:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>"), isPrivate); + @NotNull + @Override + public String getCommand() { + return TELL_CMD; + } - bot.send(sender, "To view queued and sent messages:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + View.VIEW_CMD), isPrivate); + @NotNull + @Override + public List<String> getHelp() { + return List.of("To send a message to someone when they join the channel:", + Utils.helpIndent("%s " + TELL_CMD + " <nick> <message>"), + "To view queued and sent messages:", + Utils.helpIndent("%s " + TELL_CMD + ' ' + View.VIEW_CMD), + "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); + } - bot.send(sender, - "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), - isPrivate); + @Override + public boolean isOp() { + return false; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public void commandResponse(@NotNull final Mobibot bot, + @NotNull final String sender, + @NotNull final String login, + @NotNull final String args, + final boolean isOp, + final boolean isPrivate) { + if (StringUtils.isBlank(args)) { + helpResponse(bot, args, sender, isOp, isPrivate); + } else if (args.startsWith(View.VIEW_CMD)) { + if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(args)) { + viewAll(sender, isPrivate); + } else { + viewMessages(sender, isPrivate); + } + } else if (args.startsWith(TELL_DEL_KEYWORD + ' ')) { + deleteMessage(sender, args, isOp, isPrivate); + } else { + newMessage(sender, args, isOp, isPrivate); + } + + if (clean()) { + save(); + } } /** @@ -190,8 +231,8 @@ public class Tell { } // New message. - private void newMessage(final String sender, final String cmds, final boolean isPrivate) { - final String[] split = cmds.split(" ", 2); + private void newMessage(final String sender, final String args, final boolean isOp, final boolean isPrivate) { + final String[] split = args.split(" ", 2); if (split.length == 2 && (StringUtils.isNotBlank(split[1]) && split[1].contains(" "))) { if (messages.size() < maxSize) { @@ -202,39 +243,12 @@ public class Tell { save(); bot.send(sender, "Message [ID " + message.getId() + "] was queued for " - + Utils.bold(message.getRecipient()), true); + + Utils.bold(message.getRecipient()), isPrivate); } else { - bot.send(sender, "Sorry, the messages queue is currently full.", true); + bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate); } } else { - helpResponse(sender, isPrivate); - } - } - - /** - * Processes the commands. - * - * @param sender The sender's nick. - * @param cmds The commands string. - * @param isPrivate The private flag. - */ - public void response(final String sender, final String cmds, final boolean isPrivate) { - if (StringUtils.isBlank(cmds)) { - helpResponse(sender, isPrivate); - } else if (cmds.startsWith(View.VIEW_CMD)) { - if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) { - viewAll(sender, isPrivate); - } else { - viewMessages(sender, isPrivate); - } - } else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) { - deleteMessage(sender, cmds, isPrivate); - } else { - newMessage(sender, cmds, isPrivate); - } - - if (clean()) { - save(); + helpResponse(bot, args, sender, isOp, isPrivate); } } @@ -360,8 +374,10 @@ public class Tell { } else { bot.send(sender, "To delete one or all delivered messages:", isPrivate); bot.send(sender, - Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" - + TELL_ALL_KEYWORD + '>'), isPrivate); + Utils.helpIndent( + Utils.botCommand(bot.getNick(), isPrivate) + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + + TELL_ALL_KEYWORD + '>'), + isPrivate); bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), isPrivate); From 37f3c4941386a3844282b0638197a724e7a73939 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 30 Mar 2020 21:59:23 -0700 Subject: [PATCH 377/842] Added Utils.botCommand(). --- src/main/java/net/thauvin/erik/mobibot/Constants.java | 8 ++++++++ src/main/java/net/thauvin/erik/mobibot/Utils.java | 10 ++++++++++ .../thauvin/erik/mobibot/commands/AbstractCommand.kt | 8 ++------ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index 9943398..d985b72 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -42,6 +42,14 @@ import java.util.Locale; * @since 1.0 */ public final class Constants { + /** + * The bot's private message command format. + */ + public static final String BOT_PRIVATE_CMD = "/msg %s "; + /** + * The bot's public message command format. + */ + public static final String BOT_PUB_CMD = "%s: "; /** * The connect/read timeout in ms. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 67c81c8..07767a5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -335,4 +335,14 @@ public final class Utils { public static String utcDateTime(final LocalDateTime date) { return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); } + + public static String botCommand(final String name, boolean isPrivate) { + if (isPrivate) { + return String.format(Constants.BOT_PRIVATE_CMD, name); + } else { + return String.format(Constants.BOT_PUB_CMD, name); + } + + } + } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 4b42bc3..bcafa66 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils abstract class AbstractCommand { abstract val command: String @@ -53,12 +54,7 @@ abstract class AbstractCommand { open fun helpResponse(bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { if (!this.isOp || this.isOp == isOp) { for (h in help) { - if (isPrivate) { - bot.send(sender, String.format(h, "/msg ${bot.nick}"), true) - } else if (isPublic) { - bot.send(sender, String.format(h, "${bot.nick}:"), false) - } - + bot.send(sender, String.format(h, Utils.botCommand(bot.nick, isPrivate)), isPrivate) } return true } From 559634fbfee5cba0e8e7906b9265f6edc4bb739a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 31 Mar 2020 22:30:09 -0700 Subject: [PATCH 378/842] Converted Tell to an actual command. --- .../net/thauvin/erik/mobibot/Mobibot.java | 27 ++++++++------- .../erik/mobibot/commands/tell/Tell.java | 33 ++++++++++--------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index a7d83c1..7e5dd16 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -254,17 +254,19 @@ public class Mobibot extends PircBot { commands.add(new Users()); commands.add(new Versions()); + // Tell + tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); + if (tell.isEnabled()) { + commands.add(tell); + } + // Load the links commands commands.add(new Comment()); commands.add(new Posting()); commands.add(new Tags()); - // Get the tell command settings - tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); - commands.add(tell); commands.add(new UrlMgr(p.getProperty("tags", ""), p.getProperty("tags-keywords", ""))); commands.add(new View()); - // Load the modules addModule(new Calc()); addModule(new CurrencyConverter()); @@ -647,11 +649,6 @@ public class Mobibot extends PircBot { modules.stream().filter(AbstractModule::isEnabled) .forEach(module -> commandsNames.addAll(module.getCommands())); - // Tell command - if (tell.isEnabled()) { - commandsNames.add(Tell.TELL_CMD); - } - Collections.sort(commandsNames); Collections.sort(opsCommandsNames); } @@ -829,7 +826,9 @@ public class Mobibot extends PircBot { Recap.storeRecap(sender, message, false); } - tell.send(sender, true); + if (tell.isEnabled()) { + tell.send(sender, true); + } } /** @@ -911,7 +910,9 @@ public class Mobibot extends PircBot { */ @Override protected void onJoin(final String channel, final String sender, final String login, final String hostname) { - tell.send(sender); + if (tell.isEnabled()) { + tell.send(sender); + } } /** @@ -919,7 +920,9 @@ public class Mobibot extends PircBot { */ @Override protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { - tell.send(newNick); + if (tell.isEnabled()) { + tell.send(newNick); + } } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index c42fbf2..46bbd27 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -85,6 +85,7 @@ public class Tell extends AbstractCommand { * @param maxSize Max size. */ public Tell(final Mobibot bot, final String maxDays, final String maxSize) { + super(); this.bot = bot; this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS); this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE); @@ -187,12 +188,12 @@ public class Tell extends AbstractCommand { @Override public boolean isPublic() { - return true; + return isEnabled(); } @Override public boolean isVisible() { - return true; + return isEnabled(); } @Override @@ -202,22 +203,24 @@ public class Tell extends AbstractCommand { @NotNull final String args, final boolean isOp, final boolean isPrivate) { - if (StringUtils.isBlank(args)) { - helpResponse(bot, args, sender, isOp, isPrivate); - } else if (args.startsWith(View.VIEW_CMD)) { - if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(args)) { - viewAll(sender, isPrivate); + if (isEnabled()) { + if (StringUtils.isBlank(args)) { + helpResponse(bot, args, sender, isOp, isPrivate); + } else if (args.startsWith(View.VIEW_CMD)) { + if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(args)) { + viewAll(sender, isPrivate); + } else { + viewMessages(sender, isPrivate); + } + } else if (args.startsWith(TELL_DEL_KEYWORD + ' ')) { + deleteMessage(sender, args, isOp, isPrivate); } else { - viewMessages(sender, isPrivate); + newMessage(sender, args, isOp, isPrivate); } - } else if (args.startsWith(TELL_DEL_KEYWORD + ' ')) { - deleteMessage(sender, args, isOp, isPrivate); - } else { - newMessage(sender, args, isOp, isPrivate); - } - if (clean()) { - save(); + if (clean()) { + save(); + } } } From d2339044e64b36ca925ebc3cd838dde5a6f820d9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 31 Mar 2020 22:34:35 -0700 Subject: [PATCH 379/842] Added helpFormat. --- .../net/thauvin/erik/mobibot/Constants.java | 8 ------ .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- .../java/net/thauvin/erik/mobibot/Utils.java | 25 ++++++++++++------- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 16 ++++++------ .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 4 +-- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 6 ++++- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../erik/mobibot/commands/tell/Tell.java | 7 +++--- .../erik/mobibot/modules/AbstractModule.java | 15 +++++++---- .../thauvin/erik/mobibot/modules/Calc.java | 13 +++------- .../mobibot/modules/CurrencyConverter.java | 11 +++++--- .../thauvin/erik/mobibot/modules/Dice.java | 13 +++------- .../erik/mobibot/modules/GoogleSearch.java | 18 ++++--------- .../thauvin/erik/mobibot/modules/Joke.java | 13 +++------- .../thauvin/erik/mobibot/modules/Lookup.java | 13 +++------- .../thauvin/erik/mobibot/modules/Ping.java | 13 +++------- .../erik/mobibot/modules/RockPaperScissors.kt | 24 ++++++++---------- .../erik/mobibot/modules/StockQuote.java | 13 +++------- .../thauvin/erik/mobibot/modules/Twitter.java | 18 ++++--------- .../net/thauvin/erik/mobibot/modules/War.java | 13 +++------- .../erik/mobibot/modules/Weather2.java | 24 ++++++------------ .../erik/mobibot/modules/WorldTime.java | 20 ++++++--------- 32 files changed, 129 insertions(+), 182 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index d985b72..9943398 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -42,14 +42,6 @@ import java.util.Locale; * @since 1.0 */ public final class Constants { - /** - * The bot's private message command format. - */ - public static final String BOT_PRIVATE_CMD = "/msg %s "; - /** - * The bot's public message command format. - */ - public static final String BOT_PUB_CMD = "%s: "; /** * The connect/read timeout in ms. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 7e5dd16..e06b661 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -626,7 +626,7 @@ public class Mobibot extends PircBot { send(sender, "Type a URL on " + ircChannel + " to post it.", isPrivate); send(sender, "For more information on a specific command, type:", isPrivate); send(sender, - Utils.helpIndent(Utils.botCommand(getNick(), isPrivate) + Constants.HELP_CMD + " <command>"), + Utils.helpIndent(Utils.helpFormat("%c " + Constants.HELP_CMD + " <command>", getNick(), isPrivate)), isPrivate); send(sender, "The commands are:", isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 07767a5..e4f582a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -51,6 +51,8 @@ import java.util.concurrent.TimeUnit; * @since 1.0 */ public final class Utils { + private static final String[] searchFlags = { "%c", "%n" }; + /** * Disables the default constructor. * @@ -159,6 +161,20 @@ public final class Utils { return colorize(s, Colors.DARK_GREEN); } + /** + * Formats a help command by replacing {@code %c} with the bot's pub/priv command, and {@code %n} with the bot's + * nick. + * + * @param text The help command text. + * @param botNick The bot's nick. + * @param isPrivate The private flag. + * @return The formatted help command. + */ + public static String helpFormat(final String text, final String botNick, final boolean isPrivate) { + final String[] replace = { (isPrivate) ? "/msg " + botNick : botNick + ':', botNick }; + return StringUtils.replaceEach(text, searchFlags, replace); + } + /** * Returns indented help string. * @@ -336,13 +352,4 @@ public final class Utils { return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); } - public static String botCommand(final String name, boolean isPrivate) { - if (isPrivate) { - return String.format(Constants.BOT_PRIVATE_CMD, name); - } else { - return String.format(Constants.BOT_PUB_CMD, name); - } - - } - } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index bcafa66..99a678c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -54,7 +54,7 @@ abstract class AbstractCommand { open fun helpResponse(bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { if (!this.isOp || this.isOp == isOp) { for (h in help) { - bot.send(sender, String.format(h, Utils.botCommand(bot.nick, isPrivate)), isPrivate) + bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivate), isPrivate) } return true } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index 73c2d7c..cfd7938 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -40,7 +40,7 @@ class Cycle : AbstractCommand() { override val command = "cycle" override val help = listOf( "To have the bot leave the channel and come back:", - Utils.helpIndent("%s $command") + Utils.helpIndent("%c $command") ) override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 6ad42ab..3138b70 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -41,24 +41,24 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { init { if (defaultIgnore.isNotBlank()) { - ignored.addAll(defaultIgnore.split(", +?| +")) + ignored.addAll(defaultIgnore.split(", *| +".toRegex())) } } override val command = IGNORE_CMD override val help = listOf( "To ignore a link posted to the channel:", - Utils.helpIndent("https://www.foo.bar %s"), + Utils.helpIndent("https://www.foo.bar %n"), "To check your ignore status:", - Utils.helpIndent("%s $command"), + Utils.helpIndent("%c $command"), "To toggle your ignore status:", - Utils.helpIndent("%s $command $me") + Utils.helpIndent("%c $command $me") ) private val helpOp = listOf( "To ignore a link posted to the channel:", - Utils.helpIndent("https://www.foo.bar %s"), + Utils.helpIndent("https://www.foo.bar " + Utils.bold("%n"), false), "To add/remove nicks from the ignored list:", - Utils.helpIndent("%s $command <nick>|$me [<nick> ...]") + Utils.helpIndent("%c $command <nick> [<nick> ...]") ) override val isOp = false @@ -67,7 +67,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { companion object { const val IGNORE_CMD = "ignore" - private val ignored = HashSet<String>() + private val ignored = TreeSet<String>() @JvmStatic fun isNotIgnored(nick: String): Boolean { @@ -101,7 +101,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { ): Boolean { return if (isOp) { for (h in helpOp) { - bot.send(sender, String.format(h, bot.nick), isPrivate) + bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivate), isPrivate) } true } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt index 8c1969c..0f2f4f9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -41,7 +41,7 @@ class Info : AbstractCommand() { override val command = "info" override val help = listOf( "To view information about the bot:", - Utils.helpIndent("%s $command") + Utils.helpIndent("%c $command") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt index 75e4c1f..e7bd4d1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt @@ -39,7 +39,7 @@ class Me : AbstractCommand() { override val command = "me" override val help = listOf( "To have the bot perform an action:", - Utils.helpIndent("%s $command <action>") + Utils.helpIndent("%c $command <action>") ) override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index d14f437..0d02f05 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -39,7 +39,7 @@ class Modules : AbstractCommand() { override val command = "modules" override val help = listOf( "To view a list of enabled modules:", - Utils.helpIndent("%s $command") + Utils.helpIndent("%c $command") ) override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt index a65ef5c..8f5814f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt @@ -39,7 +39,7 @@ class Msg : AbstractCommand() { override val command = "msg" override val help = listOf( "To have the bot send a private message to someone:", - Utils.helpIndent("%s $command <nick> <text>") + Utils.helpIndent("%c $command <nick> <text>") ) override val isOp = true override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt index 1ab8256..95f3a55 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt @@ -39,7 +39,7 @@ class Nick : AbstractCommand() { override val command = "nick" override val help = listOf( "To change the bot's nickname:", - Utils.helpIndent("%s $command <nick>") + Utils.helpIndent("%c $command <nick>") ) override val isOp = true override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt index c6c690a..7dfbe5e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -42,7 +42,7 @@ class Recap : AbstractCommand() { override val command = "recap" override val help = listOf( "To list the last 10 public channel messages:", - Utils.helpIndent("%s $command") + Utils.helpIndent("%c $command") ) override val isOp = false override val isPublic = true @@ -67,7 +67,7 @@ class Recap : AbstractCommand() { fun storeRecap(sender: String, message: String, isAction: Boolean) { recaps.add( Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) - + " -> ${sender}: " + (if (isAction) " " else ": ") + message + + " - $sender" + (if (isAction) " " else ": ") + message ) if (recaps.size > 10) { recaps.removeAt(0) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt index 1ddb5d7..19e9f7e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt @@ -39,7 +39,7 @@ class Say : AbstractCommand() { override val command = "say" override val help = listOf( "To have the bot say something on the channel:", - Utils.helpIndent("%s $command <text>") + Utils.helpIndent("%c $command <text>") ) override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index fad7358..9ee5add 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -41,7 +41,7 @@ class Users : AbstractCommand() { override val command = "users" override val help = listOf( "To list the users present on the channel:", - Utils.helpIndent("%s $command") + Utils.helpIndent("%c $command") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index db996fc..3e8d911 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -98,7 +98,11 @@ class Comment : AbstractCommand() { if (super.helpResponse(bot, command, sender, isOp, isPrivate)) { if (isOp) { bot.send(sender, "To change a comment's author:", isPrivate) - bot.send(sender, Utils.helpIndent("/msg ${bot.nick} ${Constants.LINK_CMD}1.1:?<nick>"), isPrivate) + bot.send( + sender, + Utils.helpIndent("${Constants.LINK_CMD}1.1:?<nick>"), + isPrivate + ) } return true } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 8a90987..c5922b9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -51,7 +51,7 @@ class Posting : AbstractCommand() { Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", "To edit a comment, see: ", - Utils.helpIndent("%s ${Constants.HELP_CMD} ${Comment.COMMAND}") + Utils.helpIndent("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index c670a60..0edde92 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -45,7 +45,7 @@ class View : AbstractCommand() { override val command = VIEW_CMD override val help = listOf( "To list or search the current URL posts:", - Utils.helpIndent("%s $command [<start>] [<query>]") + Utils.helpIndent("%c $command [<start>] [<query>]") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index 46bbd27..ff626cb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -377,9 +377,10 @@ public class Tell extends AbstractCommand { } else { bot.send(sender, "To delete one or all delivered messages:", isPrivate); bot.send(sender, - Utils.helpIndent( - Utils.botCommand(bot.getNick(), isPrivate) + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" - + TELL_ALL_KEYWORD + '>'), + Utils.helpIndent(Utils.helpFormat( + "%c " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + TELL_ALL_KEYWORD + '>', + bot.getNick(), + isPrivate)), isPrivate); bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 12bee34..5b70957 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Utils; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; @@ -50,6 +51,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public abstract class AbstractModule { final List<String> commands = new ArrayList<>(); + final List<String> help = new ArrayList<>(); final Map<String, String> properties = new ConcurrentHashMap<>(); /** @@ -95,14 +97,17 @@ public abstract class AbstractModule { } /** - * Responds with the module's Constants. - * @param bot The bot's instance. + * Responds with the module's help. + * + * @param bot The bot's instance. * @param sender The sender. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public abstract void helpResponse(final Mobibot bot, - final String sender, - final boolean isPrivate); + public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + for (final String h : help) { + bot.send(sender, Utils.helpFormat(h, bot.getNick(), isPrivateMsgEnabled() && isPrivate), isPrivate); + } + } /** * Returns <code>true</code> if the module is enabled. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index f9e1e2f..400ecdb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -56,7 +56,11 @@ public class Calc extends AbstractModule { */ public Calc() { super(); + commands.add(CALC_CMD); + + help.add("To solve a mathematical calculation:"); + help.add(Utils.helpIndent("%c " + CALC_CMD + " <calculation>")); } /** @@ -93,13 +97,4 @@ public class Calc extends AbstractModule { helpResponse(bot, sender, isPrivate); } } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To solve a mathematical calculation:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>"), isPrivate); - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 1fd2c7c..03a7737 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -84,6 +84,7 @@ public final class CurrencyConverter extends ThreadedModule { */ public CurrencyConverter() { super(); + commands.add(CURRENCY_CMD); } @@ -239,11 +240,15 @@ public final class CurrencyConverter extends ThreadedModule { bot.send(sender, EMPTY_RATE_TABLE, isPrivate); } else { bot.send(sender, "To convert from one currency to another:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " 100 USD to EUR"), isPrivate); + bot.send(sender, + Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR", + bot.getNick(), + isPrivate)), isPrivate); bot.send(sender, "For a listing of current rates:", isPrivate); bot.send(sender, - Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD, - isPrivate); + Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + ' ' + CURRENCY_RATES_KEYWORD, + bot.getNick(), + isPrivate)), isPrivate); bot.send(sender, "The supported currencies are: ", isPrivate); bot.sendCommandsList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index ec1d57c..26ad7ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -55,7 +55,11 @@ public final class Dice extends AbstractModule { */ public Dice() { super(); + commands.add(DICE_CMD); + + help.add("To roll the dice:"); + help.add(Utils.helpIndent("%c " + DICE_CMD)); } /** @@ -92,13 +96,4 @@ public final class Dice extends AbstractModule { bot.action("tied."); } } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To roll the dice:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + DICE_CMD), isPrivate); - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index e8b6597..0f64d0c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -72,7 +72,12 @@ public final class GoogleSearch extends ThreadedModule { */ public GoogleSearch() { super(); + commands.add(GOOGLE_CMD); + + help.add("To search Google:"); + help.add(Utils.helpIndent("%c " + GOOGLE_CMD + " <query>")); + properties.put(GOOGLE_API_KEY_PROP, ""); properties.put(GOOGLE_CSE_KEY_PROP, ""); } @@ -137,19 +142,6 @@ public final class GoogleSearch extends ThreadedModule { } } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - if (isEnabled()) { - bot.send(sender, "To search Google:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>"), isPrivate); - } else { - bot.send(sender, "The Google search module is disabled.", isPrivate); - } - } - /** * Searches Google. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 5f30ccd..a8a6a42 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -63,7 +63,11 @@ public final class Joke extends ThreadedModule { */ public Joke() { super(); + commands.add(JOKE_CMD); + + help.add("To retrieve a random joke:"); + help.add(Utils.helpIndent("%c " + JOKE_CMD)); } /** @@ -120,13 +124,4 @@ public final class Joke extends ThreadedModule { bot.send(sender, e.getMessage(), isPrivate); } } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To retrieve a random joke:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + JOKE_CMD), isPrivate); - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index c1292f6..20ab522 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -62,7 +62,11 @@ public final class Lookup extends AbstractModule { */ public Lookup() { super(); + commands.add(LOOKUP_CMD); + + help.add("To perform a DNS lookup query:"); + help.add(Utils.helpIndent("%c " + LOOKUP_CMD + " <ip address or hostname>")); } /** @@ -190,13 +194,4 @@ public final class Lookup extends AbstractModule { helpResponse(bot, sender, true); } } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To perform a DNS lookup query:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>"), isPrivate); - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index 6123898..ae1f518 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -74,7 +74,11 @@ public class Ping extends AbstractModule { */ public Ping() { super(); + commands.add(PING_CMD); + + help.add("To ping the bot:"); + help.add(Utils.helpIndent("%c " + PING_CMD)); } /** @@ -89,13 +93,4 @@ public class Ping extends AbstractModule { final SecureRandom r = new SecureRandom(); bot.action(PINGS.get(r.nextInt(PINGS.size()))); } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To ping the bot:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + PING_CMD), isPrivate); - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 061f08e..7374fa8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -33,9 +33,9 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.green -import net.thauvin.erik.mobibot.Utils.helpIndent import net.thauvin.erik.mobibot.Utils.red import kotlin.random.Random @@ -50,6 +50,16 @@ class RockPaperScissors : AbstractModule() { add(Hands.PAPER.name.toLowerCase()) add(Hands.SCISSORS.name.toLowerCase()) } + + with(help) { + add("To play Rock Paper Scissors:") + add( + Utils.helpIndent( + "%c ${Hands.ROCK.name.toLowerCase()} | ${Hands.PAPER.name.toLowerCase()}" + + " | ${Hands.SCISSORS.name.toLowerCase()}" + ) + ) + } } enum class Hands(val action: String) { @@ -105,16 +115,4 @@ class RockPaperScissors : AbstractModule() { } } } - - override fun helpResponse(bot: Mobibot, sender: String, isPrivate: Boolean) { - bot.send(sender, "To play Rock Paper Scissors:", isPrivate) - bot.send( - sender, - helpIndent( - "${bot.nick}: ${Hands.ROCK.name.toLowerCase()} | ${Hands.PAPER.name.toLowerCase()}" - + " | ${Hands.SCISSORS.name.toLowerCase()}" - ), - isPrivate - ) - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 925c719..210d907 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -79,6 +79,10 @@ public final class StockQuote extends ThreadedModule { public StockQuote() { super(); commands.add(STOCK_CMD); + + help.add("To retrieve a stock quote:"); + help.add(Utils.helpIndent("%c " + STOCK_CMD + " <symbol|keywords>")); + properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); } @@ -200,15 +204,6 @@ public final class StockQuote extends ThreadedModule { } } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To retrieve a stock quote:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>"), isPrivate); - } - /** * Returns the specified stock quote from Alpha Avantage. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 2d0c997..8991bc3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -62,7 +62,12 @@ public final class Twitter extends ThreadedModule { */ public Twitter() { super(); + commands.add(TWITTER_CMD); + + help.add("To post to Twitter:"); + help.add(Utils.helpIndent("%c " + TWITTER_CMD + " <message>")); + properties.put(CONSUMER_SECRET_PROP, ""); properties.put(CONSUMER_KEY_PROP, ""); properties.put(TOKEN_PROP, ""); @@ -110,19 +115,6 @@ public final class Twitter extends ThreadedModule { } } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - if (isEnabled()) { - bot.send(sender, "To post to Twitter:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>"), isPrivate); - } else { - bot.send(sender, "The Twitter posting facility is " + Utils.bold("disabled") + '.', isPrivate); - } - } - /** * Posts on Twitter. * diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index a7b294b..bf089fb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -60,7 +60,11 @@ public final class War extends AbstractModule { */ public War() { super(); + commands.add(WAR_CMD); + + help.add("To play war:"); + help.add(Utils.helpIndent("%c " + WAR_CMD)); } /** @@ -97,13 +101,4 @@ public final class War extends AbstractModule { bot.action("wins."); } } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To play war:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WAR_CMD), isPrivate); - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 1463e36..4d58090 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -75,7 +75,15 @@ public class Weather2 extends ThreadedModule { */ public Weather2() { super(); + commands.add(WEATHER_CMD); + + help.add("To display weather information:"); + help.add(Utils.helpIndent("%c " + WEATHER_CMD + " <city> [, <country code>]")); + help.add("For example:"); + help.add(Utils.helpIndent("%c " + WEATHER_CMD + " paris, fr")); + help.add("The default ISO 3166 country code is " + bold("US") + ". Zip codes supported in most countries."); + properties.put(OWM_API_KEY_PROP, ""); } @@ -206,22 +214,6 @@ public class Weather2 extends ThreadedModule { return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To display weather information:", isPrivate); - bot.send(sender, - Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]"), - isPrivate); - bot.send(sender, "For example:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr"), isPrivate); - bot.send(sender, - "The default ISO 3166 country code is " + bold("US") - + ". Zip codes are supported in most countries.", isPrivate); - } - /** * Fetches the weather data from a specific city. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 74dbc4a..5435555 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -63,7 +63,6 @@ public final class WorldTime extends AbstractModule { // Supported countries private static final Map<String, String> COUNTRIES_MAP; - /** * The time command. */ @@ -160,9 +159,16 @@ public final class WorldTime extends AbstractModule { */ public WorldTime() { super(); + + help.add("To display a country's current date/time:"); + help.add(Utils.helpIndent("%c " + TIME_CMD) + " [<country code>]"); + help.add("For a listing of the supported countries:"); + help.add(Utils.helpIndent("%c " + TIME_CMD)); + commands.add(TIME_CMD); } + /** * Returns the current Internet (beat) Time. * @@ -234,18 +240,6 @@ public final class WorldTime extends AbstractModule { } } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { - bot.send(sender, "To display a country's current date/time:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]", isPrivate); - - bot.send(sender, "For a listing of the supported countries:", isPrivate); - bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD), isPrivate); - } - /** * {@inheritDoc} */ From 2067a228aa916de2f058d085b4a793f752efebbc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 31 Mar 2020 22:35:34 -0700 Subject: [PATCH 380/842] Fixed twitter autopost initialization (was too early.) --- .../java/net/thauvin/erik/mobibot/Mobibot.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index e06b661..e050236 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -130,8 +130,6 @@ public class Mobibot extends PircBot { private static final String DEFAULT_SERVER = "irc.freenode.net"; // Maximum number of times the bot will try to reconnect, if disconnected private static final int MAX_RECONNECT = 10; - // Number of milliseconds to delay between consecutive messages - private static final long MESSAGE_DELAY = 1000L; // Logger private static final Logger logger = LogManager.getLogger(Mobibot.class); // Ignore command @@ -226,7 +224,7 @@ public class Mobibot extends PircBot { setAutoNickChange(true); setLogin(p.getProperty("login", getName())); setVersion(p.getProperty("weblog", "")); - setMessageDelay(MESSAGE_DELAY); + // setMessageDelay(1000); setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", "")); // Set the URLs @@ -278,12 +276,10 @@ public class Mobibot extends PircBot { addModule(new RockPaperScissors()); addModule(new StockQuote()); + // Twitter twitterModule = new Twitter(); addModule(twitterModule); - twitterHandle = p.getProperty(Constants.TWITTER_HANDLE_PROP, ""); - isTwitterAutoPost = - Boolean.parseBoolean(p.getProperty(Constants.TWITTER_AUTOPOST_PROP, "false")) - && twitterModule.isEnabled(); + addModule(new War()); addModule(new Weather2()); addModule(new WorldTime()); @@ -295,6 +291,12 @@ public class Mobibot extends PircBot { } }); + // Twitter extra properties + twitterHandle = p.getProperty(Constants.TWITTER_HANDLE_PROP, ""); + isTwitterAutoPost = + Boolean.parseBoolean(p.getProperty(Constants.TWITTER_AUTOPOST_PROP, "false")) + && twitterModule.isEnabled(); + // Save the entries UrlMgr.saveEntries(this, true); } @@ -492,7 +494,7 @@ public class Mobibot extends PircBot { if (isNotBlank(feedUrl)) { new Thread(new FeedReader(this, sender, feedUrl)).start(); } else { - send(sender, "There is no weblog setup for this channel.", false); + send(sender, "There is no feed setup for this channel.", false); } } From df386802262310dcdb62d781b7996693a29cf564 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 31 Mar 2020 22:36:08 -0700 Subject: [PATCH 381/842] Tags are now set in lists. --- .../erik/mobibot/commands/links/UrlMgr.kt | 24 +++--- .../erik/mobibot/entries/EntryLink.java | 80 +++++++++---------- .../erik/mobibot/entries/EntryLinkTest.java | 2 +- 3 files changed, 50 insertions(+), 56 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index 1daf17e..de2db08 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -45,7 +45,8 @@ import org.jsoup.Jsoup import java.io.IOException class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { - private val tagsKeywords = ArrayList<String>() + private val keywords = ArrayList<String>() + private val tags = ArrayList<String>() override val command = Constants.LINK_CMD override val help = emptyList<String>() override val isOp = false @@ -53,19 +54,19 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { override val isVisible = false init { - tagsKeywords.addAll(keywords.split(", +?| +")) - Companion.defaultTags = defaultTags + this.keywords.addAll(keywords.split(TAG_MATCH.toRegex())) + tags.addAll(defaultTags.split(TAG_MATCH.toRegex())) } companion object { const val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*" + const val TAG_MATCH = ", *| +" // Entries array private val entries = ArrayList<EntryLink>(0) // History/backlogs array private val history = ArrayList<String>(0) - private lateinit var defaultTags: String @JvmStatic val entriesCount @@ -130,19 +131,18 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { val link = cmds[0].trim() if (!isDupEntry(bot, sender, link, isPrivate)) { val isBackup = saveDayBackup(bot) - val tags: StringBuilder = StringBuilder(defaultTags) var title = Constants.NO_TITLE if (cmds.size == 2) { val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) title = data[0].trim() if (data.size > 1) { - tags.append(' ').append(data[1].trim()) + tags.addAll(data[1].split(TAG_MATCH.toRegex())) } } - tags.append(matchTagKeywords(title)) title = fetchTitle(link, title) + tags.addAll(matchTagKeywords(title)) - entries.add(EntryLink(link, title, sender, login, bot.channel, tags.toString())) + entries.add(EntryLink(link, title, sender, login, bot.channel, tags)) val index: Int = entries.size - 1 val entry: EntryLink = entries[index] bot.send(EntriesUtils.buildLink(index, entry)) @@ -210,17 +210,17 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { return false } - private fun matchTagKeywords(title: String): String { + private fun matchTagKeywords(title: String): List<String> { val matches = ArrayList<String>() - if (tagsKeywords.isNotEmpty()) { - for (match in tagsKeywords) { + if (keywords.isNotEmpty()) { + for (match in keywords) { val m = match.trim() if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { matches.add(m) } } } - return matches.joinToString(" ") + return matches } private fun saveDayBackup(bot: Mobibot): Boolean { diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index af6986a..26f098a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -35,9 +35,11 @@ package net.thauvin.erik.mobibot.entries; import com.rometools.rome.feed.synd.SyndCategory; import com.rometools.rome.feed.synd.SyndCategoryImpl; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.commands.links.UrlMgr; import org.apache.commons.lang3.StringUtils; import java.io.Serializable; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -93,7 +95,7 @@ public class EntryLink implements Serializable { final String nick, final String login, final String channel, - final String tags) { + final List<String> tags) { this.link = link; this.title = title; this.nick = nick; @@ -124,8 +126,7 @@ public class EntryLink implements Serializable { this.nick = nick; this.channel = channel; this.date = new Date(date.getTime()); - - setTags(tags); + this.tags.addAll(tags); } /** @@ -345,53 +346,46 @@ public class EntryLink implements Serializable { * * @param tags The space-delimited tags. */ - @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public final void setTags(final String tags) { - if (tags != null) { - final String[] parts = tags.replace(", ", " ").replace(',', ' ').split(" "); - - SyndCategoryImpl tag; - String part; - char mod; - - for (final String rawPart : parts) { - part = rawPart.trim(); - - if (part.length() >= 2) { - tag = new SyndCategoryImpl(); - tag.setName(StringUtils.lowerCase(part.substring(1))); - - mod = part.charAt(0); - - if (mod == '-') { - // Don't remove the channel tag, if any - if (!channel.substring(1).equals(tag.getName())) { - this.tags.remove(tag); - } - } else if (mod == '+') { - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } else { - tag.setName(StringUtils.lowerCase(part.trim())); - - if (!this.tags.contains(tag)) { - this.tags.add(tag); - } - } - } - } - } + setTags(Arrays.asList(tags.split(UrlMgr.TAG_MATCH))); } /** * Sets the tags. * - * @param tags The tags. + * @param tags The tags list. */ - final void setTags(final List<SyndCategory> tags) { - this.tags.addAll(tags); + @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + public final void setTags(final List<String> tags) { + if (!tags.isEmpty()) { + SyndCategoryImpl category; + + for (final String tag : tags) { + if (!tag.isBlank()) { + final String t = StringUtils.lowerCase(tag); + final char mod = t.charAt(0); + if (mod == '-') { + // Don't remove the channel tag + if (!channel.substring(1).equals(t.substring(1))) { + category = new SyndCategoryImpl(); + category.setName(t.substring(1)); + this.tags.remove(category); + } + } else { + category = new SyndCategoryImpl(); + if (mod == '+') { + category.setName(t.substring(1)); + } else { + category.setName(t); + } + if (!this.tags.contains(category)) { + this.tags.add(category); + } + } + } + } + } } /** diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java index cc4ad92..9c32598 100644 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java @@ -49,7 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class EntryLinkTest { private final EntryLink entryLink = new EntryLink("https://www.mobitopia.org/", "Mobitopia", "Skynx", - "JimH", "#mobitopia", "tag1, tag2,tag3 TAG4 tag5"); + "JimH", "#mobitopia", List.of("tag1", "tag2", "tag3", "TAG4", "Tag5")); @Test From e3cbc4e80832b528d300d3640b53ffd825d30431 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 31 Mar 2020 22:36:18 -0700 Subject: [PATCH 382/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/FeedReader.java | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 9188758..d140105 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -83,11 +83,11 @@ class FeedReader implements Runnable { final SyndFeedInput input = new SyndFeedInput(); final SyndFeed feed = input.build(new XmlReader(new URL(url))); - SyndEntry item; final List<SyndEntry> items = feed.getEntries(); if (items.isEmpty()) { bot.send(sender, "There is currently nothing to view. Why don't you post something?", false); } else { + SyndEntry item; for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { item = items.get(i); bot.send(sender, item.getTitle(), false); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 3138b70..ffc42a0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.links.UrlMgr import java.util.* class Ignore(defaultIgnore: String) : AbstractCommand() { @@ -41,7 +42,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { init { if (defaultIgnore.isNotBlank()) { - ignored.addAll(defaultIgnore.split(", *| +".toRegex())) + ignored.addAll(defaultIgnore.split(UrlMgr.LINK_MATCH.toRegex())) } } From 2ffa5f29b99c55c9ffb98859c2096c41e16d191c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 31 Mar 2020 22:56:27 -0700 Subject: [PATCH 383/842] Fix for Java 9 compatibility (isBlank) --- .../erik/mobibot/entries/EntryLink.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 26f098a..48c5894 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -362,7 +362,7 @@ public class EntryLink implements Serializable { SyndCategoryImpl category; for (final String tag : tags) { - if (!tag.isBlank()) { + if (StringUtils.isNoneBlank(tag)) { final String t = StringUtils.lowerCase(tag); final char mod = t.charAt(0); if (mod == '-') { @@ -403,10 +403,16 @@ public class EntryLink implements Serializable { * @return A string representation of the object. */ @Override - public final String toString() { - - return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date - + ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\'' - + ", tags -> " + tags + ", title -> '" + title + '\'' + " ]"; + public String toString() { + return "EntryLink{" + + "comments=" + comments + + ", tags=" + tags + + ", channel='" + channel + '\'' + + ", date=" + date + + ", link='" + link + '\'' + + ", login='" + login + '\'' + + ", nick='" + nick + '\'' + + ", title='" + title + '\'' + + '}'; } } From bde70343b78a6b60aea02ee8002cc9b65dd34c29 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Apr 2020 11:12:43 -0700 Subject: [PATCH 384/842] Added toString() --- .../mobibot/commands/tell/TellMessage.java | 14 +++++++++ .../commands/tell/TellMessagesMgr.java | 2 -- .../erik/mobibot/entries/EntryComment.java | 31 ++++++++++++------- .../erik/mobibot/entries/EntryLink.java | 20 ++++++------ 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java index 89dafab..40d71f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java @@ -178,4 +178,18 @@ public class TellMessage implements Serializable { received = LocalDateTime.now(Clock.systemUTC()); isReceived = true; } + + @Override + public String toString() { + return "TellMessage{" + + "id='" + id + '\'' + + ", isNotified=" + isNotified + + ", isReceived=" + isReceived + + ", message='" + message + '\'' + + ", queued=" + queued + + ", received=" + received + + ", recipient='" + recipient + '\'' + + ", sender='" + sender + '\'' + + '}'; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java index fd98c72..e4050f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java @@ -89,7 +89,6 @@ final class TellMessagesMgr { @SuppressWarnings("unchecked") public static List<TellMessage> load(final String file, final Logger logger) { try { - try (final ObjectInput input = new ObjectInputStream( new BufferedInputStream(Files.newInputStream(Paths.get(file))))) { if (logger.isDebugEnabled()) { @@ -116,7 +115,6 @@ final class TellMessagesMgr { */ public static void save(final String file, final List<TellMessage> messages, final Logger logger) { try { - try (final ObjectOutput output = new ObjectOutputStream( new BufferedOutputStream(Files.newOutputStream(Paths.get(file))))) { if (logger.isDebugEnabled()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java index 1661350..5c51f1a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java @@ -42,7 +42,7 @@ import java.time.LocalDateTime; * @created Jan 31, 2004 * @since 1.0 */ -@SuppressWarnings({"PMD.DataClass"}) +@SuppressWarnings({ "PMD.DataClass" }) public class EntryComment implements Serializable { // Serial version UID static final long serialVersionUID = 1L; @@ -81,16 +81,6 @@ public class EntryComment implements Serializable { return comment; } - /** - * Sets the comment. - * - * @param comment The actual comment. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setComment(final String comment) { - this.comment = comment; - } - /** * Returns the comment's creation date. * @@ -110,6 +100,16 @@ public class EntryComment implements Serializable { return nick; } + /** + * Sets the comment. + * + * @param comment The actual comment. + */ + @SuppressWarnings("UnusedDeclaration") + public final void setComment(final String comment) { + this.comment = comment; + } + /** * Sets the nickname of the author of the comment. * @@ -118,4 +118,13 @@ public class EntryComment implements Serializable { public final void setNick(final String nick) { this.nick = nick; } + + @Override + public String toString() { + return "EntryComment{" + + "comment='" + comment + '\'' + + ", date=" + date + + ", nick='" + nick + '\'' + + '}'; + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index 48c5894..aaa09cf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -404,15 +404,15 @@ public class EntryLink implements Serializable { */ @Override public String toString() { - return "EntryLink{" + - "comments=" + comments + - ", tags=" + tags + - ", channel='" + channel + '\'' + - ", date=" + date + - ", link='" + link + '\'' + - ", login='" + login + '\'' + - ", nick='" + nick + '\'' + - ", title='" + title + '\'' + - '}'; + return "EntryLink{" + + "channel='" + channel + '\'' + + ", comments=" + comments + + ", date=" + date + + ", link='" + link + '\'' + + ", login='" + login + '\'' + + ", nick='" + nick + '\'' + + ", tags=" + tags + + ", title='" + title + '\'' + + '}'; } } From 404863669630f2abeb10e21d2b7f29f78e30c684 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Apr 2020 17:38:22 -0700 Subject: [PATCH 385/842] Converted Message to Kotlin. --- .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- .../thauvin/erik/mobibot/modules/Joke.java | 2 +- .../thauvin/erik/mobibot/modules/Twitter.java | 2 +- .../erik/mobibot/modules/WorldTime.java | 6 +- .../{ErrorMessage.java => ErrorMessage.kt} | 37 ++-- .../net/thauvin/erik/mobibot/msg/Message.java | 193 ------------------ .../net/thauvin/erik/mobibot/msg/Message.kt | 105 ++++++++++ .../{NoticeMessage.java => NoticeMessage.kt} | 31 ++- ...{PrivateMessage.java => PrivateMessage.kt} | 34 +-- .../{PublicMessage.java => PublicMessage.kt} | 32 +-- .../modules/CurrencyConverterTest.java | 6 +- .../mobibot/modules/GoogleSearchTest.java | 10 +- .../erik/mobibot/modules/JokeTest.java | 4 +- .../erik/mobibot/modules/StockQuoteTest.java | 2 +- .../erik/mobibot/modules/TwitterTest.java | 2 +- .../erik/mobibot/modules/Weather2Test.java | 8 +- .../erik/mobibot/modules/WordTimeTest.java | 4 +- 17 files changed, 196 insertions(+), 284 deletions(-) rename src/main/java/net/thauvin/erik/mobibot/msg/{ErrorMessage.java => ErrorMessage.kt} (71%) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/msg/Message.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/msg/Message.kt rename src/main/java/net/thauvin/erik/mobibot/msg/{NoticeMessage.java => NoticeMessage.kt} (73%) rename src/main/java/net/thauvin/erik/mobibot/msg/{PrivateMessage.java => PrivateMessage.kt} (75%) rename src/main/java/net/thauvin/erik/mobibot/msg/{PublicMessage.java => PublicMessage.kt} (75%) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index e050236..ffded5d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -970,7 +970,7 @@ public class Mobibot extends PircBot { * @param message The message. */ public final void send(final String who, final Message message) { - send(message.isNotice() ? who : getChannel(), message.getMessage(), message.getColor(), message.isPrivate()); + send(message.isNotice() ? who : getChannel(), message.getText(), message.getColor(), message.isPrivate()); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index a8a6a42..cc24afa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -118,7 +118,7 @@ public final class Joke extends ThreadedModule { @Override void run(final Mobibot bot, final String sender, final String cmd, final String arg, final boolean isPrivate) { try { - bot.send(Utils.cyan(randomJoke().getMessage())); + bot.send(Utils.cyan(randomJoke().getText())); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage(), isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 8991bc3..0ef6f15 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -142,7 +142,7 @@ public final class Twitter extends ThreadedModule { void run(final Mobibot bot, final String sender, final String cmd, final String message, final boolean isPrivate) { try { bot.send(sender, - post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMessage(), + post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getText(), isPrivate); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 5435555..91fb27c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -229,12 +229,12 @@ public final class WorldTime extends AbstractModule { } else { final Message msg = worldTime(args); if (isPrivate) { - bot.send(sender, msg.getMessage(), true); + bot.send(sender, msg.getText(), true); } else { if (msg.isError()) { - bot.send(sender, msg.getMessage(), false); + bot.send(sender, msg.getText(), false); } else { - bot.send(msg.getMessage()); + bot.send(msg.getText()); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt similarity index 71% rename from src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java rename to src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index dea79a6..6c00add 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -29,41 +29,38 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package net.thauvin.erik.mobibot.msg; +package net.thauvin.erik.mobibot.msg /** - * The <code>ErrorMessage</code> class. + * The `ErrorMessage` class. * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author [Erik C. Thauvin](https://erik.thauvin.net/) * @created 2019-04-07 * @since 1.0 */ -public class ErrorMessage extends Message { +class ErrorMessage : Message { /** * Creates a new error message. * - * @param message The error message. + * @param text The error message. */ - public ErrorMessage(final String message) { - super(); - this.setMessage(message); - this.setError(true); - this.setNotice(true); + constructor(text: String) : super() { + this.text = text + isError = true + isNotice = true } /** * Creates a new error message. * - * @param message The message. - * @param color The message color. + * @param text The message. + * @param color The message color. */ - @SuppressWarnings("unused") - public ErrorMessage(final String message, final String color) { - super(); - this.setMessage(message); - this.setError(true); - this.setNotice(true); - this.setColor(color); + @Suppress("unused") + constructor(text: String, color: String) : super() { + this.text = text + isError = true + isNotice = true + this.color = color } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java b/src/main/java/net/thauvin/erik/mobibot/msg/Message.java deleted file mode 100644 index cc70c2a..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Message.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.msg; - -import org.jibble.pircbot.Colors; - -/** - * The <code>Message</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 - */ -public class Message { - private String color = Colors.NORMAL; - private boolean isError; - private boolean isNotice; - private boolean isPrivate; - private String msg = ""; - - /** - * Creates a new message. - */ - public Message() { - // This constructor is intentionally empty - } - - /** - * Creates a new message. - * - * @param message The message. - * @param isNotice The notice flag. - * @param isError The error flag. - * @param isPrivate The Private message - */ - @SuppressWarnings("unused") - public Message(final String message, final boolean isNotice, final boolean isError, final boolean isPrivate) { - msg = message; - this.isNotice = isNotice; - this.isError = isError; - this.isPrivate = isPrivate; - } - - - /** - * Creates a new message. - * - * @param message The message. - * @param isNotice The notice flag. - * @param isError The error flag. - * @param isPrivate The Private message - * @param color The color. - */ - @SuppressWarnings("unused") - public Message(final String message, - final boolean isNotice, - final boolean isError, - final boolean isPrivate, - final String color) { - msg = message; - this.isNotice = isNotice; - this.isError = isError; - this.isPrivate = isPrivate; - this.color = color; - } - - /** - * Returns the message color. - * - * @return The color. - */ - public String getColor() { - return color; - } - - /** - * Returns the message. - * - * @return The message. - */ - public String getMessage() { - return msg; - } - - /** - * Returns the message error flag. - * - * @return The error flag. - */ - public boolean isError() { - return isError; - } - - /** - * Returns the message notice flag. - * - * @return The notice flag. - */ - public boolean isNotice() { - return isNotice; - } - - /** - * Returns the message private flag. - * - * @return The private flag. - */ - public boolean isPrivate() { - return isPrivate; - } - - /** - * Set the color. - * - * @param color The new color. - */ - public void setColor(final String color) { - this.color = color; - } - - /** - * Sets the message error flag. - * - * @param error The error flag. - */ - public void setError(final boolean error) { - isError = error; - } - - /** - * Sets the message. - * - * @param message The new message. - */ - public void setMessage(final String message) { - msg = message; - } - - /** - * Sets the message notice flag. - * - * @param isNotice The notice flag. - */ - public void setNotice(final boolean isNotice) { - this.isNotice = isNotice; - } - - /** - * Sets the message private flag. - * - * @param isPrivate The private flag. - */ - @SuppressWarnings("unused") - public void setPrivate(final boolean isPrivate) { - this.isPrivate = isPrivate; - } - - @Override - public String toString() { - return "Message{" + "color='" + color + '\'' + ", isError=" + isError + ", isNotice=" + isNotice - + ", isPrivate=" + isPrivate + ", msg='" + msg + '\'' + '}'; - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt new file mode 100644 index 0000000..d95e792 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt @@ -0,0 +1,105 @@ +/* + * Message.java + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg + +import org.jibble.pircbot.Colors + +/** + * The `Message` class. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @created 2019-04-07 + * @since 1.0 + */ +open class Message { + /** Color */ + var color = Colors.NORMAL + + /** Error */ + var isError = false + + /** Notice */ + var isNotice = false + + /** Private */ + var isPrivate = false + + /** Message text*/ + var text = "" + + /** Creates a new message. */ + constructor() { + // This constructor is intentionally empty + } + + /** + * Creates a new message. + * + * @param text The message. + * @param isNotice The notice flag. + * @param isError The error flag. + * @param isPrivate The Private message + */ + constructor(text: String, isNotice: Boolean, isError: Boolean, isPrivate: Boolean) { + this.text = text + this.isNotice = isNotice + this.isError = isError + this.isPrivate = isPrivate + } + + /** + * Creates a new message. + * + * @param text The message. + * @param isNotice The notice flag. + * @param isError The error flag. + * @param isPrivate The Private message + * @param color The color. + */ + constructor( + text: String, + isNotice: Boolean, + isError: Boolean, + isPrivate: Boolean, + color: String + ) { + this.text = text + this.isNotice = isNotice + this.isError = isError + this.isPrivate = isPrivate + this.color = color + } + + override fun toString(): String { + return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, message='$text')" + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt similarity index 73% rename from src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java rename to src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 258d947..b7c0ad3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -29,38 +29,35 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package net.thauvin.erik.mobibot.msg; +package net.thauvin.erik.mobibot.msg /** - * The <code>NoticeMessage</code> class. + * The `NoticeMessage` class. * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author [Erik C. Thauvin](https://erik.thauvin.net/) * @created 2019-04-07 * @since 1.0 */ -public class NoticeMessage extends Message { +class NoticeMessage : Message { /** * Creates a new notice. * - * @param message The notice's message. + * @param text The notice's message. */ - public NoticeMessage(final String message) { - super(); - this.setMessage(message); - this.setNotice(true); + constructor(text: String) : super() { + this.text = text + isNotice = true } /** * Create a new notice. * - * @param message The notice's message. - * @param color The color. + * @param text The notice's message. + * @param color The color. */ - public NoticeMessage(final String message, final String color) { - super(); - this.setMessage(message); - this.setNotice(true); - this.setColor(color); + constructor(text: String, color: String) : super() { + this.text = text + isNotice = true + this.color = color } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt similarity index 75% rename from src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java rename to src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 1079a4d..4f9d4e9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -29,32 +29,36 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package net.thauvin.erik.mobibot.msg; +package net.thauvin.erik.mobibot.msg /** - * The <code>PrivateMessage</code> class. + * The `PrivateMessage` class. * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author [Erik C. Thauvin](https://erik.thauvin.net/) * @created 2019-04-09 * @since 1.0 */ -@SuppressWarnings("unused") -public class PrivateMessage extends Message { - public PrivateMessage(final String message) { - super(); - this.setMessage(message); +@Suppress("unused") +class PrivateMessage : Message { + /** + * Creates a new private message. + * + * @param text The message. + */ + constructor(text: String) : super() { + this.text = text + isPrivate = true } /** * Creates a new private message. * - * @param message The message. - * @param color The message color. + * @param text The message. + * @param color The message color. */ - public PrivateMessage(final String message, final String color) { - super(); - this.setMessage(message); - this.setColor(color); + constructor(text: String, color: String) : super() { + this.text = text + this.color = color + isPrivate = true } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt similarity index 75% rename from src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java rename to src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 3c4e924..2bd991b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.java +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -29,32 +29,34 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package net.thauvin.erik.mobibot.msg; +package net.thauvin.erik.mobibot.msg /** - * The <code>PublicMessage</code> class. + * The `PublicMessage` class. * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> + * @author [Erik C. Thauvin](https://erik.thauvin.net/) * @created 2019-04-07 * @since 1.0 */ -public class PublicMessage extends Message { - public PublicMessage(final String message) { - super(); - this.setMessage(message); +class PublicMessage : Message { + /** + * Creates a new public message. + * + * @param text The message. + */ + constructor(text: String) : super() { + this.text = text } /** * Creates a new public message. * - * @param message The message. - * @param color The message color. + * @param text The message. + * @param color The message color. */ - @SuppressWarnings("unused") - public PublicMessage(final String message, final String color) { - super(); - this.setMessage(message); - this.setColor(color); + @Suppress("unused") + constructor(text: String, color: String) : super() { + this.text = text + this.color = color } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index 9291c4f..f72d74a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -54,11 +54,11 @@ public class CurrencyConverterTest { @Test @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") public void testConvertCurrency() { - assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMessage()) + assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getText()) .as("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR"); - assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getMessage()) + assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getText()) .as("100 USD to USD").contains("You're kidding, right?"); - assertThat(CurrencyConverter.convertCurrency("100 USD").getMessage()) + assertThat(CurrencyConverter.convertCurrency("100 USD").getText()) .as("100 USD").contains("Invalid query."); assertThat(CurrencyConverter.currencyRates().size()) .as("currencyRates().size() == 33").isEqualTo(33); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 13388bd..4b07647 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -63,20 +63,20 @@ public class GoogleSearchTest extends LocalProperties { try { List<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey); assertThat(messages).as("mobibot results not empty").isNotEmpty(); - assertThat(messages.get(0).getMessage()).as("found mobitopia").contains("mobibot"); + assertThat(messages.get(0).getText()).as("found mobitopia").contains("mobibot"); messages = GoogleSearch.searchGoogle("aapl", apiKey, cseKey); assertThat(messages).as("aapl results not empty").isNotEmpty(); - assertThat(messages.get(0).getMessage()).as("found apple").containsIgnoringCase("apple"); + assertThat(messages.get(0).getText()).as("found apple").containsIgnoringCase("apple"); assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "", "apiKey")).as("no API key").isInstanceOf( - ModuleException.class).hasNoCause(); + ModuleException.class).hasNoCause(); assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "apiKey", "")).as("no CSE API key").isInstanceOf( - ModuleException.class).hasNoCause(); + ModuleException.class).hasNoCause(); assertThatThrownBy(() -> GoogleSearch.searchGoogle("", "apikey", "apiKey")).as("no query").isInstanceOf( - ModuleException.class).hasNoCause(); + ModuleException.class).hasNoCause(); } catch (ModuleException e) { // Avoid displaying api keys in CI logs if ("true".equals(System.getenv("CI"))) { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java index d62cd2e..93836e7 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -51,7 +51,7 @@ public class JokeTest { @Test public void testRamdomJoke() throws ModuleException { - assertThat(Joke.randomJoke().getMessage().length() > 0).as("randomJoke() > 0").isTrue(); - assertThat(Joke.randomJoke().getMessage()).as("randomJoke()").containsIgnoringCase("chuck"); + assertThat(Joke.randomJoke().getText().length() > 0).as("randomJoke() > 0").isTrue(); + assertThat(Joke.randomJoke().getText()).as("randomJoke()").containsIgnoringCase("chuck"); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index b3a4971..5931cd5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -58,7 +58,7 @@ public class StockQuoteTest extends LocalProperties { try { final List<Message> messages = StockQuote.getQuote("apple inc", apiKey); assertThat(messages).as("response not empty").isNotEmpty(); - assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL").contains("Apple Inc."); + assertThat(messages.get(0).getText()).as("same stock symbol").contains("AAPL").contains("Apple Inc."); try { StockQuote.getQuote("012", apiKey); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java index cf64b4d..b1514a9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -74,6 +74,6 @@ public class TwitterTest { LocalProperties.getProperty(Twitter.TOKEN_SECRET_PROP), LocalProperties.getProperty(Constants.TWITTER_HANDLE_PROP), msg, - true).getMessage()).as("twitterPost(" + msg + ')').isEqualTo(msg); + true).getText()).as("twitterPost(" + msg + ')').isEqualTo(msg); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index f39c459..f22e049 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -54,12 +54,12 @@ public class Weather2Test extends LocalProperties { @Test public void testWeather() throws ModuleException { List<Message> messages = Weather2.getWeather("98204", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett").contains("US"); - assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Search").endsWith("98204%2CUS"); + assertThat(messages.get(0).getText()).as("is Everett").contains("Everett").contains("US"); + assertThat(messages.get(messages.size() - 1).getText()).as("is City Search").endsWith("98204%2CUS"); messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getMessage()).as("is UK").contains("London").contains("UK"); - assertThat(messages.get(messages.size() - 1).getMessage()).as("is City Code").endsWith("4517009"); + assertThat(messages.get(0).getText()).as("is UK").contains("London").contains("UK"); + assertThat(messages.get(messages.size() - 1).getText()).as("is City Code").endsWith("4517009"); assertThatThrownBy( () -> Weather2.getWeather("Montpellier, FR", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP))).as( diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java index 7f623ff..6d6e630 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java @@ -47,9 +47,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class WordTimeTest { @Test public void testWorldTime() { - assertThat(WorldTime.worldTime("PST").getMessage()).as("PST").endsWith(Utils.bold("Los Angeles")); + assertThat(WorldTime.worldTime("PST").getText()).as("PST").endsWith(Utils.bold("Los Angeles")); assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue(); - assertThat(WorldTime.worldTime("BEATS").getMessage()).as("BEATS").contains("@"); + assertThat(WorldTime.worldTime("BEATS").getText()).as("BEATS").contains("@"); } @Test From b99a2f568e6b991b34d9cb39763e3bb9f3d0cf3c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Apr 2020 20:00:51 -0700 Subject: [PATCH 386/842] Fixed defaultTags. --- .../erik/mobibot/commands/links/UrlMgr.kt | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index de2db08..b07d909 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -45,8 +45,8 @@ import org.jsoup.Jsoup import java.io.IOException class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { - private val keywords = ArrayList<String>() - private val tags = ArrayList<String>() + private val keywords: List<String> + private val defaultTags: List<String> override val command = Constants.LINK_CMD override val help = emptyList<String>() override val isOp = false @@ -54,8 +54,8 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { override val isVisible = false init { - this.keywords.addAll(keywords.split(TAG_MATCH.toRegex())) - tags.addAll(defaultTags.split(TAG_MATCH.toRegex())) + this.keywords = keywords.split(TAG_MATCH.toRegex()) + this.defaultTags = defaultTags.split(TAG_MATCH.toRegex()) } companion object { @@ -132,6 +132,7 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { if (!isDupEntry(bot, sender, link, isPrivate)) { val isBackup = saveDayBackup(bot) var title = Constants.NO_TITLE + val tags = ArrayList<String>(defaultTags) if (cmds.size == 2) { val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) title = data[0].trim() @@ -140,7 +141,7 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { } } title = fetchTitle(link, title) - tags.addAll(matchTagKeywords(title)) + matchTagKeywords(title, tags) entries.add(EntryLink(link, title, sender, login, bot.channel, tags)) val index: Int = entries.size - 1 @@ -182,13 +183,17 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { private fun fetchTitle(link: String, title: String): String { if (Constants.NO_TITLE == title) { try { - val html = Jsoup.connect(link).userAgent("Mozilla").get() + val html = Jsoup.connect(link) + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() val htmlTitle = html.title() val split = htmlTitle.split("( \\| )".toRegex(), 2) - return if (split.size == 2) { + return if (split.size == 2 && split[0].isNotBlank()) { split[0] - } else { + } else if (htmlTitle.isNotBlank()) { htmlTitle + } else { + title } } catch (ignore: IOException) { // Do nothing @@ -210,17 +215,13 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { return false } - private fun matchTagKeywords(title: String): List<String> { - val matches = ArrayList<String>() - if (keywords.isNotEmpty()) { - for (match in keywords) { - val m = match.trim() - if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { - matches.add(m) - } + private fun matchTagKeywords(title: String, tags: ArrayList<String>) { + for (match in keywords) { + val m = Regex.escape(match.trim()) + if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { + tags.add(m) } } - return matches } private fun saveDayBackup(bot: Mobibot): Boolean { From b73ffc061d5c831d7d245897558f658ed73041b3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Apr 2020 20:31:14 -0700 Subject: [PATCH 387/842] Added source & traget compatibility. --- build.gradle | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build.gradle b/build.gradle index 8d75ae5..6c69e9e 100644 --- a/build.gradle +++ b/build.gradle @@ -83,6 +83,17 @@ test { } } +java { + sourceCompatibility = JavaVersion.VERSION_1_9 + targetCompatibility = JavaVersion.VERSION_1_9 +} + +compileKotlin { + kotlinOptions { + jvmTarget = '9' + } +} + spotbugs { toolVersion.set("$versions.spotbugs") } From fc35b6bfc8b950e7c664feada0103a319691986b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 16 Apr 2020 15:40:30 -0700 Subject: [PATCH 388/842] Changed sendCommandsList to sendList. --- .../java/net/thauvin/erik/mobibot/Mobibot.java | 14 +++++++------- .../net/thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Users.kt | 4 ++-- .../erik/mobibot/modules/CurrencyConverter.java | 4 ++-- .../thauvin/erik/mobibot/modules/WorldTime.java | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index ffded5d..bc94f0c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -655,10 +655,10 @@ public class Mobibot extends PircBot { Collections.sort(opsCommandsNames); } - sendCommandsList(sender, commandsNames, 8, isPrivate, true); + sendList(sender, commandsNames, 8, isPrivate, true); if (isOp) { send(sender, "The op commands are:", isPrivate); - sendCommandsList(sender, opsCommandsNames, 8, isPrivate, true); + sendList(sender, opsCommandsNames, 8, isPrivate, true); } } @@ -994,11 +994,11 @@ public class Mobibot extends PircBot { * @param isPrivate The private flag. * @param isBold The bold flag */ - public final void sendCommandsList(final String nick, - final List<String> list, - final int size, - final boolean isPrivate, - final boolean isBold) { + public final void sendList(final String nick, + final List<String> list, + final int size, + final boolean isPrivate, + final boolean isBold) { for (int i = 0; i < list.size(); i += size) { send(nick, Utils.helpIndent( String.join(" ", list.subList(i, Math.min(list.size(), i + size))), isBold), isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index ffc42a0..79c6a08 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -144,7 +144,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { if (ignored.size > 0) { bot.send(sender, "The following nicks are ignored:", isPrivate) - bot.sendCommandsList(sender, ignored.toList(), 8, isPrivate, true) + bot.sendList(sender, ignored.toList(), 8, isPrivate, true) } else { bot.send(sender, "No one is currently ${Utils.bold("ignored")}.", isPrivate) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index 0d02f05..e997677 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -59,7 +59,7 @@ class Modules : AbstractCommand() { bot.send(sender, "There are no enabled modules.", isPrivate) } else { bot.send(sender, "The enabled modules are: ", isPrivate) - bot.sendCommandsList(sender, modulesNames, 7, isPrivate, false) + bot.sendList(sender, modulesNames, 7, isPrivate, false) } } else { bot.helpDefault(sender, isOp, isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index 9ee5add..73524f3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -60,12 +60,12 @@ class Users : AbstractCommand() { val nicks = ArrayList<String>() users.forEach { user -> if (bot.isOp(user.nick)) { - nicks.add('@'.toString() + user.nick) + nicks.add("@${user.nick}") } else { nicks.add(user.nick) } } - bot.send(sender, nicks.sorted().joinToString(" "), isPrivate) + bot.sendList(sender, nicks.sorted(), 8, isPrivate, false) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index 03a7737..c6c3fce 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -218,7 +218,7 @@ public final class CurrencyConverter extends ThreadedModule { } } else if (query.contains(CURRENCY_RATES_KEYWORD)) { bot.send(sender, "The currency rates for " + Utils.bold(pubDate) + " are:", isPrivate); - bot.sendCommandsList(sender, currencyRates(), 3, isPrivate, false); + bot.sendList(sender, currencyRates(), 3, isPrivate, false); } else { helpResponse(bot, sender, isPrivate); } @@ -250,7 +250,7 @@ public final class CurrencyConverter extends ThreadedModule { bot.getNick(), isPrivate)), isPrivate); bot.send(sender, "The supported currencies are: ", isPrivate); - bot.sendCommandsList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false); + bot.sendList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 91fb27c..2eaa86c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -225,7 +225,7 @@ public final class WorldTime extends AbstractModule { final boolean isPrivate) { if (args.length() == 0) { bot.send(sender, "The supported countries/zones are: ", isPrivate); - bot.sendCommandsList(sender, new ArrayList<>(COUNTRIES_MAP.keySet()), 17, false, false); + bot.sendList(sender, new ArrayList<>(COUNTRIES_MAP.keySet()), 17, false, false); } else { final Message msg = worldTime(args); if (isPrivate) { From c9366d856f227f25276818f819bb5b2b5da3f21d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 16 Apr 2020 15:51:43 -0700 Subject: [PATCH 389/842] Fixed title parsing. --- .../erik/mobibot/commands/links/UrlMgr.kt | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index b07d909..1a6cd77 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -131,7 +131,7 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { val link = cmds[0].trim() if (!isDupEntry(bot, sender, link, isPrivate)) { val isBackup = saveDayBackup(bot) - var title = Constants.NO_TITLE + var title = "" val tags = ArrayList<String>(defaultTags) if (cmds.size == 2) { val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) @@ -140,8 +140,14 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { tags.addAll(data[1].split(TAG_MATCH.toRegex())) } } - title = fetchTitle(link, title) - matchTagKeywords(title, tags) + + if (title.isBlank()) { + title = fetchTitle(link) + } + + if (title != Constants.NO_TITLE) { + matchTagKeywords(title, tags) + } entries.add(EntryLink(link, title, sender, login, bot.channel, tags)) val index: Int = entries.size - 1 @@ -180,26 +186,19 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { return message.matches(LINK_MATCH.toRegex()) } - private fun fetchTitle(link: String, title: String): String { - if (Constants.NO_TITLE == title) { - try { - val html = Jsoup.connect(link) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") - .get() - val htmlTitle = html.title() - val split = htmlTitle.split("( \\| )".toRegex(), 2) - return if (split.size == 2 && split[0].isNotBlank()) { - split[0] - } else if (htmlTitle.isNotBlank()) { - htmlTitle - } else { - title - } - } catch (ignore: IOException) { - // Do nothing + private fun fetchTitle(link: String): String { + try { + val html = Jsoup.connect(link) + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() + val title = html.title() + if (title.isNotBlank()) { + return title } + } catch (ignore: IOException) { + // Do nothing } - return title + return Constants.NO_TITLE } private fun isDupEntry(bot: Mobibot, sender: String, link: String, isPrivate: Boolean): Boolean { @@ -217,9 +216,9 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { private fun matchTagKeywords(title: String, tags: ArrayList<String>) { for (match in keywords) { - val m = Regex.escape(match.trim()) + val m = Regex.escape(match) if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { - tags.add(m) + tags.add(match) } } } From e022bc90ff3109471e6e0f2dccf85c4390a2e7f6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 16 Apr 2020 16:10:07 -0700 Subject: [PATCH 390/842] Updated dependencies. --- .idea/compiler.xml | 6 +----- .idea/misc.xml | 2 +- .idea/mobibot.iml | 4 ++-- .idea/modules/mobibot.main.iml | 11 +++++----- .idea/modules/mobibot.test.iml | 8 ++++---- build.gradle | 20 +++++++++---------- detekt-baseline.xml | 1 + licenses/LICENSE.txt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 10 files changed, 31 insertions(+), 35 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index bec3928..a87304a 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -21,10 +21,6 @@ <module name="mobibot.main" /> </profile> </annotationProcessing> - <bytecodeTargetLevel> - <module name="mobibot" target="14" /> - <module name="mobibot.main" target="14" /> - <module name="mobibot.test" target="14" /> - </bytecodeTargetLevel> + <bytecodeTargetLevel target="1.9" /> </component> </project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 52412b4..c88ae8f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,5 +6,5 @@ <component name="JavaScriptSettings"> <option name="languageLevel" value="ES6" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="false" project-jdk-name="14" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_9" default="false" project-jdk-name="14" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 771bd41..6dcb7d2 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+157" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+289" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> <excludeFolder url="file://$MODULE_DIR$/.gradle" /> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 6f0fcd0..b3165e0 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,17 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+157" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+289" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> - <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> + <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> <compilerSettings> <option name="additionalArguments" value="-Xallow-no-source-files" /> </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0-RC1/9203d2fdfba9923294f8b679f52136ce13f97575/okhttp-4.5.0-RC1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> + <option name="jvmTarget" value="9" /> <option name="languageVersion" value="1.3" /> <option name="apiVersion" value="1.3" /> <option name="pluginOptions"> @@ -35,7 +36,7 @@ </configuration> </facet> </component> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> + <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> <exclude-output /> <content url="file://$MODULE_DIR$/../../src/generated/java"> @@ -59,7 +60,7 @@ <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.4.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0-RC1" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index cfdf045..a9fac3b 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+157" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+289" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0-RC1/9203d2fdfba9923294f8b679f52136ce13f97575/okhttp-4.5.0-RC1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -41,7 +41,7 @@ </configuration> </facet> </component> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14"> + <component name="NewModuleRootManager"> <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> <exclude-output /> <content url="file://$MODULE_DIR$/../../src/test"> @@ -60,7 +60,7 @@ <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.4.1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0-RC1" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> diff --git a/build.gradle b/build.gradle index 6c69e9e..a42e6de 100644 --- a/build.gradle +++ b/build.gradle @@ -4,11 +4,11 @@ plugins { id 'com.github.ben-manes.versions' version '0.28.0' id 'com.github.spotbugs' version '4.0.5' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.7.2' + id 'io.gitlab.arturbosch.detekt' version '1.7.4' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.3.71' + id 'org.jetbrains.kotlin.jvm' version '1.3.72' id 'org.sonarqube' version '2.8' id 'pmd' } @@ -21,13 +21,11 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' -ext { - versions = [ - kotlin : '1.3.71', - log4j : '2.13.1', - spotbugs : '4.0.1' - ] -} +ext.versions = [ + kotlin : '1.3.72', + log4j : '2.13.1', + spotbugs: '4.0.1' +] repositories { mavenLocal() @@ -51,7 +49,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.6' - implementation 'com.squareup.okhttp3:okhttp:4.5.0-RC1' + implementation 'com.squareup.okhttp3:okhttp:4.5.0' implementation 'com.rometools:rome:1.12.2' @@ -125,7 +123,7 @@ tasks.withType(io.gitlab.arturbosch.detekt.Detekt) { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") - options.compilerArgs += [ "-Asemver.project.dir=$projectDir" ] + options.compilerArgs += ["-Asemver.project.dir=$projectDir"] } compileJava { diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 8af6eed..6f5dcde 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -13,6 +13,7 @@ <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> <ID>MagicNumber:UrlMgr.kt$UrlMgr$1000L</ID> <ID>MagicNumber:UrlMgr.kt$UrlMgr$60L</ID> + <ID>MagicNumber:Users.kt$Users$8</ID> <ID>MagicNumber:View.kt$View$8</ID> <ID>NestedBlockDepth:Comment.kt$Comment$commandResponse</ID> <ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID> diff --git a/licenses/LICENSE.txt b/licenses/LICENSE.txt index 4efe703..37785b0 100644 --- a/licenses/LICENSE.txt +++ b/licenses/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 00d806e..2836f6f 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1585467573570L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1587078052972L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "105"; - public static final String VERSION = "0.8.0-alpha+105"; + public static final String BUILDMETA = "294"; + public static final String VERSION = "0.8.0-alpha+294"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 3f02b34..6e5c59b 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Mar 29 00:39:32 PDT 2020 -version.buildmeta=105 +#Thu Apr 16 16:00:41 PDT 2020 +version.buildmeta=294 version.major=0 version.minor=8 version.patch=0 version.prerelease=alpha version.project=mobibot -version.semver=0.8.0-alpha+105 +version.semver=0.8.0-alpha+294 From bcf659c5a830a4b88ba9756457b1b25d41264f8b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Apr 2020 23:16:14 -0700 Subject: [PATCH 391/842] Updated dependencies. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 34 ++++++++++----------- .idea/modules/mobibot.test.iml | 36 +++++++++++------------ build.gradle | 13 ++++---- gradle/wrapper/gradle-wrapper.jar | Bin 58694 -> 58910 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 ++ gradlew.bat | 1 + 8 files changed, 46 insertions(+), 44 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 6dcb7d2..19d441c 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+289" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+299" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index b3165e0..a2d494c 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+289" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+299" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0-RC1/9203d2fdfba9923294f8b679f52136ce13f97575/okhttp-4.5.0-RC1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0/cfd127ae6de4862daa93e15ceae9291108eaabc5/okhttp-4.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.5.0/b6b52caca243e75a8fbb95ff6ec9de6b2bdb1481/okio-jvm-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -20,11 +20,11 @@ </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.71/7f5522e7a9d1736fabfdb4335630f64504ce8f20/kotlin-script-runtime-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.71/80b8eb0986d1391e6a1dcf9aba968d59165dc4f/kotlin-scripting-common-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.71/ee1ba4f199415255ea6ea55cc5dc41856b2729fd/kotlin-scripting-jvm-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.72/657d8d34d91e1964b4439378c09933e840bfe8d5/kotlin-script-runtime-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.72/e09990437040879d692655d66f58a64318681ffe/kotlin-scripting-common-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.72/7dde2c909e6f1b80245c7ca100d32a8646b5666d/kotlin-scripting-jvm-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> @@ -51,33 +51,33 @@ <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.2" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0-RC1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.71" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.3" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.71" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index a9fac3b..2031057 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+289" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+299" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0-RC1/9203d2fdfba9923294f8b679f52136ce13f97575/okhttp-4.5.0-RC1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0/cfd127ae6de4862daa93e15ceae9291108eaabc5/okhttp-4.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.5.0/b6b52caca243e75a8fbb95ff6ec9de6b2bdb1481/okio-jvm-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -25,11 +25,11 @@ </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.71/7f5522e7a9d1736fabfdb4335630f64504ce8f20/kotlin-script-runtime-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.71/80b8eb0986d1391e6a1dcf9aba968d59165dc4f/kotlin-scripting-common-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.71/ee1ba4f199415255ea6ea55cc5dc41856b2729fd/kotlin-scripting-jvm-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.72/657d8d34d91e1964b4439378c09933e840bfe8d5/kotlin-script-runtime-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.72/e09990437040879d692655d66f58a64318681ffe/kotlin-scripting-common-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.72/7dde2c909e6f1b80245c7ca100d32a8646b5666d/kotlin-scripting-jvm-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> @@ -52,41 +52,41 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="mobibot.main" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.2" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0-RC1" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.71" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.71" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.4.3" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.2.2" level="project" /> <orderEntry type="library" name="Gradle: com.beust:jcommander:1.72" level="project" /> <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.71" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> diff --git a/build.gradle b/build.gradle index a42e6de..693e640 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,9 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.28.0' - id 'com.github.spotbugs' version '4.0.5' + id 'com.github.spotbugs' version '4.0.7' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.7.4' + id 'io.gitlab.arturbosch.detekt' version '1.8.0' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -22,9 +22,8 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' ext.versions = [ - kotlin : '1.3.72', - log4j : '2.13.1', - spotbugs: '4.0.1' + log4j : '2.13.2', + spotbugs: '4.0.2' ] repositories { @@ -62,8 +61,8 @@ dependencies { implementation 'net.aksingh:owm-japis:2.5.3.0' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" + implementation platform('org.jetbrains.kotlin:kotlin-bom') + implementation 'org.jetbrains.kotlin:kotlin-stdlib' testImplementation 'org.testng:testng:7.2.0' testImplementation 'org.assertj:assertj-core:3.15.0' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 490fda8577df6c95960ba7077c43220e5bb2c0d9..62d4c053550b91381bbd28b1afc82d634bf73a8a 100644 GIT binary patch delta 6447 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 Xd0iPaGX_wOF#=p0c#ocs{}cZodb%%W delta 6107 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 F{|nTN-B$nr diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4b4429..1db799f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 2fe81a7..fbd7c51 100755 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 62bd9b9..5093609 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% From 34636ea9f95009793a5035bdc78fc88774cdc735 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Apr 2020 23:18:30 -0700 Subject: [PATCH 392/842] Added ChannelFeed command. --- .../net/thauvin/erik/mobibot/FeedReader.java | 4 +- .../net/thauvin/erik/mobibot/Mobibot.java | 116 ++++++------------ .../erik/mobibot/commands/ChannelFeed.kt | 64 ++++++++++ 3 files changed, 103 insertions(+), 81 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index d140105..cac105e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -48,7 +48,7 @@ import java.util.List; * @created Feb 1, 2004 * @since 1.0 */ -class FeedReader implements Runnable { +public class FeedReader implements Runnable { // Maximum number of feed items to display private static final int MAX_ITEMS = 5; @@ -68,7 +68,7 @@ class FeedReader implements Runnable { * @param sender The nick of the person who sent the message. * @param url The URL to fetch. */ - FeedReader(final Mobibot bot, final String sender, final String url) { + public FeedReader(final Mobibot bot, final String sender, final String url) { this.bot = bot; this.sender = sender; this.url = url; diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index bc94f0c..fdca6d2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -168,8 +168,6 @@ public class Mobibot extends PircBot { private final Twitter twitterModule; // Backlogs URL private String backLogsUrl = ""; - // Feed URL - private String feedUrl = ""; // Ident message private String identMsg = ""; // Ident nick @@ -229,41 +227,41 @@ public class Mobibot extends PircBot { // Set the URLs setWeblogUrl(getVersion()); - setFeedUrl(p.getProperty("feed", "")); setBacklogsUrl(Utils.ensureDir(p.getProperty("backlogs", weblogUrl), true)); // Set the pinboard authentication setPinboardAuth(p.getProperty("pinboard-api-token")); - // Set the ignored nicks - ignoreCommand = new Ignore(p.getProperty("ignore", "")); - // Load the commands - commands.add(new AddLog()); - commands.add(new Cycle()); - commands.add(ignoreCommand); - commands.add(new Info()); - commands.add(new Me()); - commands.add(new Modules()); - commands.add(new Msg()); - commands.add(new Nick()); - commands.add(new Recap()); - commands.add(new Say()); - commands.add(new Users()); - commands.add(new Versions()); + addCommand(new AddLog()); + addCommand(new ChannelFeed(getChannelName(), p.getProperty("feed", ""))); + addCommand(new Cycle()); + addCommand(new Info()); + addCommand(new Me()); + addCommand(new Modules()); + addCommand(new Msg()); + addCommand(new Nick()); + addCommand(new Recap()); + addCommand(new Say()); + addCommand(new Users()); + addCommand(new Versions()); - // Tell + // Ignore command + ignoreCommand = new Ignore(p.getProperty("ignore", "")); + addCommand(ignoreCommand); + + // Tell command tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); if (tell.isEnabled()) { - commands.add(tell); + addCommand(tell); } // Load the links commands - commands.add(new Comment()); - commands.add(new Posting()); - commands.add(new Tags()); - commands.add(new UrlMgr(p.getProperty("tags", ""), p.getProperty("tags-keywords", ""))); - commands.add(new View()); + addCommand(new Comment()); + addCommand(new Posting()); + addCommand(new Tags()); + addCommand(new UrlMgr(p.getProperty("tags", ""), p.getProperty("tags-keywords", ""))); + addCommand(new View()); // Load the modules addModule(new Calc()); @@ -485,19 +483,6 @@ public class Mobibot extends PircBot { } } - /** - * Responds with the title and links from the RSS feed. - * - * @param sender The nick of the person who sent the message. - */ - private void feedResponse(final String sender) { - if (isNotBlank(feedUrl)) { - new Thread(new FeedReader(this, sender, feedUrl)).start(); - } else { - send(sender, "There is no feed setup for this channel.", false); - } - } - /** * Returns the backlogs URL. * @@ -696,15 +681,9 @@ public class Mobibot extends PircBot { if (StringUtils.isBlank(topic)) { helpDefault(sender, isOp, isPrivate); } else { - final String lcTopic = lowerCase(topic).trim(); - if (lcTopic.equals(getChannelName())) { - send(sender, "To list the last 5 posts from the channel's weblog:", isPrivate); - send(sender, Utils.helpIndent(getNick() + ": " + getChannelName()), isPrivate); - } else { - // Command, Modules or Default - if (!helpCommands(sender, topic, isPrivate) && !helpModules(sender, lcTopic, isPrivate)) { - helpDefault(sender, isOp, isPrivate); - } + // Command, Modules or Default + if (!helpCommands(sender, topic, isPrivate) && !helpModules(sender, lowerCase(topic).trim(), isPrivate)) { + helpDefault(sender, isOp, isPrivate); } } } @@ -790,47 +769,35 @@ public class Mobibot extends PircBot { if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help helpResponse(sender, args, false); - } else if (cmd.equalsIgnoreCase(getChannelName())) { // mobibot: <channel> - feedResponse(sender); } else { - boolean skip = false; // Commands for (final AbstractCommand command : commands) { if (command.isPublic() && command.getCommand().startsWith(cmd)) { command.commandResponse(this, sender, login, args, isOp(sender), false); - skip = true; - break; + return; } } - if (!skip) { - // Modules - for (final AbstractModule module : modules) { // modules - for (final String c : module.getCommands()) { - if (cmd.startsWith(c)) { - module.commandResponse(this, sender, cmd, args, false); - break; - } + // Modules + for (final AbstractModule module : modules) { // modules + for (final String c : module.getCommands()) { + if (cmd.startsWith(c)) { + module.commandResponse(this, sender, cmd, args, false); + return; } } } } - } else { // Commands + } else { + // Commands, e.g.: https://www.example.com/ for (final AbstractCommand command : commands) { if (command.matches(message)) { command.commandResponse(this, sender, login, message, isOp(sender), false); - isCommand = true; - break; + return; } } } - if (!isCommand) { - Recap.storeRecap(sender, message, false); - } - - if (tell.isEnabled()) { - tell.send(sender, true); - } + Recap.storeRecap(sender, message, false); } /** @@ -1014,15 +981,6 @@ public class Mobibot extends PircBot { backLogsUrl = url; } - /** - * Sets the feed URL. - * - * @param feedUrl The feed URL. - */ - final void setFeedUrl(final String feedUrl) { - this.feedUrl = feedUrl; - } - /** * Sets the bot's identification. * diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt new file mode 100644 index 0000000..a5ce92a --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -0,0 +1,64 @@ +/* + * Feed.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.FeedReader +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import org.apache.commons.lang3.StringUtils.isNotBlank + +class ChannelFeed(channel: String, private val feedUrl: String) : AbstractCommand() { + override val command = channel + override val help = listOf( + "To list the last 5 posts from the channel's weblog feed:", + Utils.helpIndent("%c $channel") + ) + override val isOp = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse( + bot: Mobibot, + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isNotBlank(feedUrl)) { + Thread(FeedReader(bot, sender, feedUrl)).start() + } else { + bot.send(sender, "There is no feed setup for this channel.", false) + } + } +} From aba2633dc469ed1206f2aeef95e07ff4eb9a7c09 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 27 Apr 2020 23:19:16 -0700 Subject: [PATCH 393/842] Cleanup. --- .../net/thauvin/erik/mobibot/Constants.java | 8 + .../net/thauvin/erik/mobibot/Mobibot.java | 153 ++++++++---------- .../erik/mobibot/commands/links/UrlMgr.kt | 3 +- .../erik/mobibot/commands/tell/Tell.java | 5 +- .../commands/tell/TellMessagesMgr.java | 9 +- .../erik/mobibot/entries/EntriesMgr.java | 69 ++++---- .../thauvin/erik/mobibot/modules/Lookup.java | 5 +- .../mobibot/modules/GoogleSearchTest.java | 4 +- .../mobibot/modules/RockPaperScissorsTest.kt | 21 ++- .../erik/mobibot/modules/StockQuoteTest.java | 2 +- .../erik/mobibot/modules/TwitterTest.java | 16 +- .../erik/mobibot/modules/Weather2Test.java | 14 +- 12 files changed, 145 insertions(+), 164 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index 9943398..eaf8d2d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -54,6 +54,14 @@ public final class Constants { * The debug command. */ public static final String DEBUG_CMD = "debug"; + /** + * Default IRC Port. + */ + public static final int DEFAULT_PORT = 6667; + /** + * Default IRC Server. + */ + public static final String DEFAULT_SERVER = "irc.freenode.net"; /** * The die command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index fdca6d2..affe915 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -35,6 +35,7 @@ package net.thauvin.erik.mobibot; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.commands.AbstractCommand; import net.thauvin.erik.mobibot.commands.AddLog; +import net.thauvin.erik.mobibot.commands.ChannelFeed; import net.thauvin.erik.mobibot.commands.Cycle; import net.thauvin.erik.mobibot.commands.Ignore; import net.thauvin.erik.mobibot.commands.Info; @@ -122,16 +123,12 @@ public class Mobibot extends PircBot { ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " (" + Utils.green("https://www.mobitopia.org/mobibot/") + ')', "Written by Erik C. Thauvin (" + Utils.green("https://erik.thauvin.net/") + ')'); - // Timer - public static final Timer timer = new Timer(true); - // Default port - private static final int DEFAULT_PORT = 6667; - // Default server - private static final String DEFAULT_SERVER = "irc.freenode.net"; + // Logger + private static final Logger LOGGER = LogManager.getLogger(Mobibot.class); // Maximum number of times the bot will try to reconnect, if disconnected private static final int MAX_RECONNECT = 10; - // Logger - private static final Logger logger = LogManager.getLogger(Mobibot.class); + // Timer + private static final Timer TIMER = new Timer(true); // Ignore command public final Ignore ignoreCommand; // Automatically post links to Twitter @@ -179,6 +176,7 @@ public class Mobibot extends PircBot { // Weblog URL private String weblogUrl = ""; + /** * Creates a new {@link Mobibot} instance. * @@ -196,25 +194,20 @@ public class Mobibot extends PircBot { setName(nickname); - ircServer = p.getProperty("server", DEFAULT_SERVER); - ircPort = Utils.getIntProperty(p.getProperty("port"), DEFAULT_PORT); + ircServer = p.getProperty("server", Constants.DEFAULT_SERVER); + ircPort = Utils.getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT); ircChannel = channel; logsDir = logsDirPath; // Set the logger level - loggerLevel = logger.getLevel(); + loggerLevel = LOGGER.getLevel(); // Load the current entries and backlogs, if any try { UrlMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, ircChannel); - - if (logger.isDebugEnabled()) { - logger.debug("Last feed: {}", UrlMgr.getStartDate()); - } + LOGGER.debug("Last feed: {}", UrlMgr.getStartDate()); } catch (Exception e) { - if (logger.isErrorEnabled()) { - logger.error("An error occurred while loading the logs.", e); - } + LOGGER.error("An error occurred while loading the logs.", e); } // Initialize the bot @@ -273,15 +266,14 @@ public class Mobibot extends PircBot { addModule(new Ping()); addModule(new RockPaperScissors()); addModule(new StockQuote()); - - // Twitter - twitterModule = new Twitter(); - addModule(twitterModule); - addModule(new War()); addModule(new Weather2()); addModule(new WorldTime()); + // Twitter module + twitterModule = new Twitter(); + addModule(twitterModule); + // Load the modules properties modules.stream().filter(AbstractModule::hasProperties).forEach(module -> { for (final String s : module.getPropertyKeys()) { @@ -295,6 +287,11 @@ public class Mobibot extends PircBot { Boolean.parseBoolean(p.getProperty(Constants.TWITTER_AUTOPOST_PROP, "false")) && twitterModule.isEnabled(); + // Sort the command & module names + Collections.sort(commandsNames); + Collections.sort(opsCommandsNames); + Collections.sort(modulesNames); + // Save the entries UrlMgr.saveEntries(this, true); } @@ -418,6 +415,22 @@ public class Mobibot extends PircBot { } } + /** + * Adds a command. + * + * @param command The command to add. + */ + private void addCommand(final AbstractCommand command) { + commands.add(command); + if (command.isVisible()) { + if (command.isOp()) { + opsCommandsNames.add(command.getCommand()); + } else { + commandsNames.add(command.getCommand()); + } + } + } + /** * Adds a module. * @@ -426,6 +439,7 @@ public class Mobibot extends PircBot { private void addModule(final AbstractModule module) { modules.add(module); modulesNames.add(module.getClass().getSimpleName()); + commandsNames.addAll(module.getCommands()); } /** @@ -456,11 +470,7 @@ public class Mobibot extends PircBot { connect(ircServer, ircPort); } catch (Exception ex) { if (retries == MAX_RECONNECT) { - if (logger.isDebugEnabled()) { - logger.debug( - "Unable to reconnect to {} after {} retries.", ircServer, MAX_RECONNECT, ex); - } - + LOGGER.debug("Unable to reconnect to {} after {} retries.", ircServer, MAX_RECONNECT, ex); e.printStackTrace(System.err); System.exit(1); } @@ -526,7 +536,7 @@ public class Mobibot extends PircBot { * @return The bot's logger. */ public final Logger getLogger() { - return logger; + return LOGGER; } /** @@ -567,6 +577,15 @@ public class Mobibot extends PircBot { return buff.toString(); } + /** + * Returns the bot's timer. + * + * @return The timer. + */ + public final Timer getTimer() { + return TIMER; + } + /** * Get today's date for the feed. * @@ -616,30 +635,6 @@ public class Mobibot extends PircBot { Utils.helpIndent(Utils.helpFormat("%c " + Constants.HELP_CMD + " <command>", getNick(), isPrivate)), isPrivate); send(sender, "The commands are:", isPrivate); - - if (commandsNames.isEmpty()) { - // Feed command - commandsNames.add(getChannelName()); - - // Commands - for (final AbstractCommand command : commands) { - if (command.isVisible()) { - if (command.isOp()) { - opsCommandsNames.add(command.getCommand()); - } else { - commandsNames.add(command.getCommand()); - } - } - } - - // Modules commands - modules.stream().filter(AbstractModule::isEnabled) - .forEach(module -> commandsNames.addAll(module.getCommands())); - - Collections.sort(commandsNames); - Collections.sort(opsCommandsNames); - } - sendList(sender, commandsNames, 8, isPrivate, true); if (isOp) { send(sender, "The op commands are:", isPrivate); @@ -749,15 +744,13 @@ public class Mobibot extends PircBot { @Override protected final void onMessage(final String channel, final String sender, final String login, final String hostname, final String message) { - if (logger.isDebugEnabled()) { - logger.debug(">>> {} : {}", sender, message); + LOGGER.debug(">>> {} : {}", sender, message); + + if (tell.isEnabled()) { + tell.send(sender, true); } - boolean isCommand = false; - if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> - isCommand = true; - final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); final String cmd = lowerCase(cmds[0]); @@ -808,9 +801,7 @@ public class Mobibot extends PircBot { @Override protected final void onPrivateMessage(final String sender, final String login, final String hostname, final String message) { - if (logger.isDebugEnabled()) { - logger.debug(">>> {} : {}", sender, message); - } + LOGGER.debug(">>> {} : {}", sender, message); final String[] cmds = message.split(" ", 2); final String cmd = lowerCase(cmds[0]); @@ -828,15 +819,15 @@ public class Mobibot extends PircBot { sendRawLine("QUIT : Poof!"); System.exit(0); } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug - if (logger.isDebugEnabled()) { - Configurator.setLevel(logger.getName(), loggerLevel); + if (LOGGER.isDebugEnabled()) { + Configurator.setLevel(LOGGER.getName(), loggerLevel); } else { - Configurator.setLevel(logger.getName(), Level.DEBUG); + Configurator.setLevel(LOGGER.getName(), Level.DEBUG); } - send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true); + send(sender, "Debug logging is " + (LOGGER.isDebugEnabled() ? "enabled." : "disabled."), true); } else if (isOp && Constants.DIE_CMD.equals(cmd)) { // die send(sender + " has just signed my death sentence."); - timer.cancel(); + TIMER.cancel(); twitterShutdown(); twitterNotification("killed by " + sender + " on " + ircChannel); sleep(3); @@ -869,7 +860,7 @@ public class Mobibot extends PircBot { @Override protected final void onAction(final String sender, final String login, final String hostname, final String target, final String action) { - if (target != null && target.equals(ircChannel)) { + if (ircChannel.equals(target)) { Recap.storeRecap(sender, action, true); } } @@ -905,16 +896,10 @@ public class Mobibot extends PircBot { public final void send(final String sender, final String message, final boolean isPrivate) { if (isNotBlank(message) && isNotBlank(sender)) { if (isPrivate) { - if (logger.isDebugEnabled()) { - logger.debug("Sending message to {} : {}", sender, message); - } - + LOGGER.debug("Sending message to {} : {}", sender, message); sendMessage(sender, message); } else { - if (logger.isDebugEnabled()) { - logger.debug("Sending notice to {} : {}", sender, message); - } - + LOGGER.debug("Sending notice to {} : {}", sender, message); sendNotice(sender, message); } } @@ -1028,7 +1013,7 @@ public class Mobibot extends PircBot { } /** - * Add an entry to be posted on twitter. + * Add an entry to be posted on Twitter. * * @param index The entry index. */ @@ -1049,11 +1034,12 @@ public class Mobibot extends PircBot { entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + getChannel(); new Thread(() -> { try { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1); + } twitterModule.post(twitterHandle, msg, false); } catch (ModuleException e) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to post entry on twitter.", e); - } + LOGGER.warn("Failed to post entry on Twitter.", e); } }).start(); twitterEntries.remove(index); @@ -1083,16 +1069,14 @@ public class Mobibot extends PircBot { getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true); } catch (ModuleException e) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to notify @{}: {}", twitterHandle, msg, e); - } + LOGGER.warn("Failed to notify @{}: {}", twitterHandle, msg, e); } }).start(); } } /** - * Removes entry from twitter auto-post. + * Removes entry from Twitter auto-post. * * @param index The entry's index. */ @@ -1106,6 +1090,7 @@ public class Mobibot extends PircBot { */ final void twitterShutdown() { if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { + LOGGER.debug("Twitter shutdown."); for (final int i : twitterEntries) { twitterEntryPost(i); } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index 1a6cd77..539094f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -237,7 +237,8 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { private fun twitterPost(bot: Mobibot, index: Int) { if (bot.isTwitterAutoPost) { bot.twitterAddEntry(index) - Mobibot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) + bot.logger.debug("Scheduling ${Constants.LINK_CMD}${index + 1} for posting on Twitter.") + bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index ff626cb..fe96e9d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -106,10 +106,7 @@ public class Tell extends AbstractCommand { */ @SuppressWarnings("WeakerAccess") final boolean clean() { - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Cleaning the messages."); - } - + bot.getLogger().debug("Cleaning the messages."); return TellMessagesMgr.clean(messages, maxDays); } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java index e4050f7..b712438 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java @@ -91,9 +91,7 @@ final class TellMessagesMgr { try { try (final ObjectInput input = new ObjectInputStream( new BufferedInputStream(Files.newInputStream(Paths.get(file))))) { - if (logger.isDebugEnabled()) { - logger.debug("Loading the messages."); - } + logger.debug("Loading the messages."); return ((List<TellMessage>) input.readObject()); } @@ -117,10 +115,7 @@ final class TellMessagesMgr { try { try (final ObjectOutput output = new ObjectOutputStream( new BufferedOutputStream(Files.newOutputStream(Paths.get(file))))) { - if (logger.isDebugEnabled()) { - logger.debug("Saving the messages."); - } - + logger.debug("Saving the messages."); output.writeObject(messages); } } catch (IOException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java index d1ba70b..42c1fb9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java @@ -102,13 +102,13 @@ public final class EntriesMgr { * @throws FeedException If an error occurred while reading the feed. */ public static void loadBacklogs(final String file, final Collection<String> history) - throws IOException, FeedException { + throws IOException, FeedException { history.clear(); final SyndFeedInput input = new SyndFeedInput(); try (final InputStreamReader reader = - new InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { + new InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); @@ -134,7 +134,7 @@ public final class EntriesMgr { */ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public static String loadEntries(final String file, final String channel, final Collection<EntryLink> entries) - throws IOException, FeedException { + throws IOException, FeedException { entries.clear(); final SyndFeedInput input = new SyndFeedInput(); @@ -142,7 +142,7 @@ public final class EntriesMgr { final String today; try (final InputStreamReader reader = new InputStreamReader( - Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { + Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { final SyndFeed feed = input.build(reader); today = Utils.isoLocalDate(feed.getPublishedDate()); @@ -157,13 +157,13 @@ public final class EntriesMgr { for (int i = items.size() - 1; i >= 0; i--) { item = items.get(i); author = item.getAuthor() - .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); + .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); entry = new EntryLink(item.getLink(), - item.getTitle(), - author, - channel, - item.getPublishedDate(), - item.getCategories()); + item.getTitle(), + author, + channel, + item.getPublishedDate(), + item.getCategories()); description = item.getDescription(); comments = description.getValue().split("<br/>"); @@ -191,15 +191,14 @@ public final class EntriesMgr { * @param history The history array. * @param isDayBackup Set the true if the daily backup file should also be created. */ - @SuppressFBWarnings(value = {"CE_CLASS_ENVY", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, it does.") + @SuppressFBWarnings(value = { "CE_CLASS_ENVY", "CC_CYCLOMATIC_COMPLEXITY" }, + justification = "Yes, it does.") @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public static void saveEntries(final Mobibot bot, final List<EntryLink> entries, final List<String> history, final boolean isDayBackup) { - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Saving the feeds..."); - } + bot.getLogger().debug("Saving the feeds..."); if (StringUtils.isNotBlank(bot.getLogsDir()) && StringUtils.isNotBlank(bot.getWeblogUrl())) { try { @@ -209,7 +208,7 @@ public final class EntriesMgr { SyndEntry item; SyndContent description; try (final Writer fw = new OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.getLogsDir() + CURRENT_XML)), StandardCharsets.UTF_8)) { + Files.newOutputStream(Paths.get(bot.getLogsDir() + CURRENT_XML)), StandardCharsets.UTF_8)) { rss.setFeedType("rss_2.0"); rss.setTitle(bot.getChannel() + " IRC Links"); rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); @@ -225,14 +224,14 @@ public final class EntriesMgr { entry = entries.get(i); buff = new StringBuilder() - .append("Posted by <b>") - .append(entry.getNick()) - .append("</b> on <a href=\"irc://") - .append(bot.getIrcServer()).append('/') - .append(entry.getChannel()) - .append("\"><b>") - .append(entry.getChannel()) - .append("</b></a>"); + .append("Posted by <b>") + .append(entry.getNick()) + .append("</b> on <a href=\"irc://") + .append(bot.getIrcServer()).append('/') + .append(entry.getChannel()) + .append("\"><b>") + .append(entry.getChannel()) + .append("</b></a>"); if (entry.getCommentsCount() > 0) { buff.append(" <br/><br/>"); @@ -258,7 +257,8 @@ public final class EntriesMgr { item.setTitle(entry.getTitle()); item.setPublishedDate(entry.getDate()); item.setAuthor( - bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')'); + bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + + ')'); item.setCategories(entry.getTags()); items.add(item); @@ -266,16 +266,13 @@ public final class EntriesMgr { rss.setEntries(items); - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Writing the entries feed."); - } - + bot.getLogger().debug("Writing the entries feed."); output.output(rss, fw); } try (final Writer fw = new OutputStreamWriter( - Files.newOutputStream(Paths.get( - bot.getLogsDir() + bot.getToday() + XML_EXT)), StandardCharsets.UTF_8)) { + Files.newOutputStream(Paths.get( + bot.getLogsDir() + bot.getToday() + XML_EXT)), StandardCharsets.UTF_8)) { output.output(rss, fw); } @@ -290,12 +287,12 @@ public final class EntriesMgr { } try (final Writer fw = new OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.getLogsDir() + NAV_XML)), StandardCharsets.UTF_8)) { + Files.newOutputStream(Paths.get(bot.getLogsDir() + NAV_XML)), StandardCharsets.UTF_8)) { rss = new SyndFeedImpl(); rss.setFeedType("rss_2.0"); rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " - + bot.getChannel()); + + bot.getChannel()); rss.setLink(bot.getBacklogsUrl()); rss.setPublishedDate(Calendar.getInstance().getTime()); @@ -317,10 +314,7 @@ public final class EntriesMgr { rss.setEntries(items); - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Writing the backlog feed."); - } - + bot.getLogger().debug("Writing the backlog feed."); output.output(rss, fw); } } else { @@ -331,8 +325,7 @@ public final class EntriesMgr { bot.getLogger().warn("Unable to generate the entries feed.", e); } } else { - bot.getLogger() - .warn("Unable to generate the entries feed. At least one of the required property is missing."); + bot.getLogger().warn("Unable to generate the entries feed. A required property is missing."); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index 20ab522..a065068 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -180,10 +180,7 @@ public final class Lookup extends AbstractModule { bot.send("Unknown host."); } } catch (IOException ioe) { - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Unable to perform whois IP lookup: {}", args, ioe); - } - + bot.getLogger().debug("Unable to perform whois IP lookup: {}", args, ioe); bot.send("Unable to perform whois IP lookup: " + ioe.getMessage()); } } else { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 4b07647..23a1552 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -58,8 +58,8 @@ public class GoogleSearchTest extends LocalProperties { @SuppressWarnings("PMD.PreserveStackTrace") @Test public void testSearchGoogle() throws ModuleException { - final String apiKey = LocalProperties.getProperty(GoogleSearch.GOOGLE_API_KEY_PROP); - final String cseKey = LocalProperties.getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP); + final String apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP); + final String cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP); try { List<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey); assertThat(messages).as("mobibot results not empty").isNotEmpty(); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 6ca4a84..0429945 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -40,18 +40,25 @@ class RockPaperScissorsTest { @Test fun testWinLoseOrDraw() { assertThat( - RockPaperScissors.winLoseOrDraw("scissors", "paper")).`as`("scissors vs. paper").isEqualTo("win") + RockPaperScissors.winLoseOrDraw("scissors", "paper") + ).`as`("scissors vs. paper").isEqualTo("win") assertThat( - RockPaperScissors.winLoseOrDraw("paper", "rock")).`as`("paper vs. rock").isEqualTo("win") + RockPaperScissors.winLoseOrDraw("paper", "rock") + ).`as`("paper vs. rock").isEqualTo("win") assertThat( - RockPaperScissors.winLoseOrDraw("rock", "scissors")).`as`("rock vs. scissors").isEqualTo("win") + RockPaperScissors.winLoseOrDraw("rock", "scissors") + ).`as`("rock vs. scissors").isEqualTo("win") assertThat( - RockPaperScissors.winLoseOrDraw("paper", "scissors")).`as`("paper vs. scissors").isEqualTo("lose") + RockPaperScissors.winLoseOrDraw("paper", "scissors") + ).`as`("paper vs. scissors").isEqualTo("lose") assertThat( - RockPaperScissors.winLoseOrDraw("rock", "paper")).`as`("rock vs. paper").isEqualTo("lose") + RockPaperScissors.winLoseOrDraw("rock", "paper") + ).`as`("rock vs. paper").isEqualTo("lose") assertThat( - RockPaperScissors.winLoseOrDraw("scissors", "rock")).`as`("scissors vs. rock").isEqualTo("lose") + RockPaperScissors.winLoseOrDraw("scissors", "rock") + ).`as`("scissors vs. rock").isEqualTo("lose") assertThat( - RockPaperScissors.winLoseOrDraw("scissors", "scissors")).`as`("scissors vs. scissors").isEqualTo("draw") + RockPaperScissors.winLoseOrDraw("scissors", "scissors") + ).`as`("scissors vs. scissors").isEqualTo("draw") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 5931cd5..0b77df5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -61,7 +61,7 @@ public class StockQuoteTest extends LocalProperties { assertThat(messages.get(0).getText()).as("same stock symbol").contains("AAPL").contains("Apple Inc."); try { - StockQuote.getQuote("012", apiKey); + StockQuote.getQuote("blahfoo", apiKey); } catch (ModuleException e) { assertThat(e.getMessage()).as("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java index b1514a9..f289a79 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -48,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @created 2019-04-19 * @since 1.0 */ -public class TwitterTest { +public class TwitterTest extends LocalProperties { @SuppressFBWarnings("MDM") private String getCi() { if ("true".equals(System.getenv("CIRCLECI"))) { @@ -68,12 +68,12 @@ public class TwitterTest { public void testPostTwitter() throws ModuleException { final String msg = "Testing Twitter API from " + getCi(); assertThat(Twitter.twitterPost( - LocalProperties.getProperty(Twitter.CONSUMER_KEY_PROP), - LocalProperties.getProperty(Twitter.CONSUMER_SECRET_PROP), - LocalProperties.getProperty(Twitter.TOKEN_PROP), - LocalProperties.getProperty(Twitter.TOKEN_SECRET_PROP), - LocalProperties.getProperty(Constants.TWITTER_HANDLE_PROP), - msg, - true).getText()).as("twitterPost(" + msg + ')').isEqualTo(msg); + getProperty(Twitter.CONSUMER_KEY_PROP), + getProperty(Twitter.CONSUMER_SECRET_PROP), + getProperty(Twitter.TOKEN_PROP), + getProperty(Twitter.TOKEN_SECRET_PROP), + getProperty(Constants.TWITTER_HANDLE_PROP), + msg, + true).getText()).as("twitterPost(" + msg + ')').isEqualTo(msg); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index f22e049..5eb40d4 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -53,21 +53,19 @@ public class Weather2Test extends LocalProperties { @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") @Test public void testWeather() throws ModuleException { - List<Message> messages = Weather2.getWeather("98204", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); + List<Message> messages = Weather2.getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getText()).as("is Everett").contains("Everett").contains("US"); assertThat(messages.get(messages.size() - 1).getText()).as("is City Search").endsWith("98204%2CUS"); - messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); + messages = Weather2.getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP)); assertThat(messages.get(0).getText()).as("is UK").contains("London").contains("UK"); assertThat(messages.get(messages.size() - 1).getText()).as("is City Code").endsWith("4517009"); - assertThatThrownBy( - () -> Weather2.getWeather("Montpellier, FR", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP))).as( - "Montpellier not found").hasCauseInstanceOf(APIException.class); + assertThatThrownBy(() -> Weather2.getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP))) + .as("Montpellier not found").hasCauseInstanceOf(APIException.class); - assertThatThrownBy( - () -> Weather2.getWeather("test", "")).as( - "no API key").isInstanceOf(ModuleException.class).hasNoCause(); + assertThatThrownBy(() -> Weather2.getWeather("test", "")) + .as("no API key").isInstanceOf(ModuleException.class).hasNoCause(); messages = Weather2.getWeather("", "apikey"); assertThat(messages.get(0).isError()).as("no query").isTrue(); From e2faa55c9f977550661f7aeb6e3eb982c1239bc7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 28 Apr 2020 20:52:30 -0700 Subject: [PATCH 394/842] Gradle upgrade. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 19d441c..94b4d4b 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+299" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+330" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index a2d494c..beb8f3c 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+299" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+330" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 2031057..b33ac37 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+299" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+330" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1db799f..de4250e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 5c3b2074badc7dd8e65797d8f3ab08846adb128e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 28 Apr 2020 21:01:18 -0700 Subject: [PATCH 395/842] Added Addons to handle adding commands & modules and related properties. Commands now have their own properties and are initialized with the bot instance. --- detekt-baseline.xml | 4 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- .../java/net/thauvin/erik/mobibot/Addons.kt | 99 ++++++ .../net/thauvin/erik/mobibot/Constants.java | 9 +- .../net/thauvin/erik/mobibot/Mobibot.java | 330 +++++------------- .../net/thauvin/erik/mobibot/TwitterTimer.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 35 +- .../thauvin/erik/mobibot/commands/AddLog.kt | 5 +- .../erik/mobibot/commands/ChannelFeed.kt | 20 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 7 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 27 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 15 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 7 +- .../thauvin/erik/mobibot/commands/Modules.kt | 20 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 9 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 7 +- .../thauvin/erik/mobibot/commands/Recap.kt | 7 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 7 +- .../thauvin/erik/mobibot/commands/Users.kt | 7 +- .../erik/mobibot/commands/Versions.java | 15 +- .../erik/mobibot/commands/links/Comment.kt | 8 +- .../erik/mobibot/commands/links/Posting.kt | 34 +- .../erik/mobibot/commands/links/Tags.kt | 5 +- .../erik/mobibot/commands/links/UrlMgr.kt | 32 +- .../erik/mobibot/commands/links/View.kt | 9 +- .../erik/mobibot/commands/tell/Tell.java | 171 ++++----- .../erik/mobibot/modules/AbstractModule.java | 23 +- .../thauvin/erik/mobibot/modules/Calc.java | 9 +- .../mobibot/modules/CurrencyConverter.java | 25 +- .../thauvin/erik/mobibot/modules/Dice.java | 7 +- .../erik/mobibot/modules/GoogleSearch.java | 11 +- .../thauvin/erik/mobibot/modules/Joke.java | 11 +- .../thauvin/erik/mobibot/modules/Lookup.java | 9 +- .../thauvin/erik/mobibot/modules/Ping.java | 7 +- .../erik/mobibot/modules/RockPaperScissors.kt | 17 +- .../erik/mobibot/modules/StockQuote.java | 10 +- .../erik/mobibot/modules/ThreadedModule.java | 17 +- .../thauvin/erik/mobibot/modules/Twitter.java | 139 +++++++- .../net/thauvin/erik/mobibot/modules/War.java | 7 +- .../erik/mobibot/modules/Weather2.java | 12 +- .../erik/mobibot/modules/WorldTime.java | 11 +- .../erik/mobibot/modules/CalcTest.java | 2 +- .../modules/CurrencyConverterTest.java | 2 +- .../mobibot/modules/GoogleSearchTest.java | 2 +- .../erik/mobibot/modules/JokeTest.java | 2 +- .../erik/mobibot/modules/LookupTest.java | 2 +- .../erik/mobibot/modules/PingTest.java | 2 +- .../erik/mobibot/modules/StockQuoteTest.java | 2 +- .../erik/mobibot/modules/TwitterTest.java | 3 +- .../erik/mobibot/modules/Weather2Test.java | 2 +- .../erik/mobibot/modules/WordTimeTest.java | 2 +- version.properties | 6 +- 52 files changed, 669 insertions(+), 570 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/Addons.kt diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 6f5dcde..19a2a8f 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -2,7 +2,6 @@ <SmellBaseline> <Blacklist></Blacklist> <Whitelist> - <ID>LongParameterList:AbstractCommand.kt$AbstractCommand$( bot: Mobibot, sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> @@ -11,10 +10,9 @@ <ID>MagicNumber:Ignore.kt$Ignore$8</ID> <ID>MagicNumber:Modules.kt$Modules$7</ID> <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> - <ID>MagicNumber:UrlMgr.kt$UrlMgr$1000L</ID> - <ID>MagicNumber:UrlMgr.kt$UrlMgr$60L</ID> <ID>MagicNumber:Users.kt$Users$8</ID> <ID>MagicNumber:View.kt$View$8</ID> + <ID>NestedBlockDepth:Addons.kt$Addons$add</ID> <ID>NestedBlockDepth:Comment.kt$Comment$commandResponse</ID> <ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID> </Whitelist> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 2836f6f..686c1a4 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1587078052972L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1588132687770L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "294"; - public static final String VERSION = "0.8.0-alpha+294"; + public static final String BUILDMETA = "348"; + public static final String VERSION = "0.8.0-alpha+348"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt new file mode 100644 index 0000000..64b7487 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -0,0 +1,99 @@ +/* + * Addons.java + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.modules.AbstractModule +import java.util.* +import kotlin.collections.ArrayList + +/** + * Modules and Commands addons. + */ +class Addons { + val commands: MutableList<AbstractCommand> = ArrayList() + val modules: MutableList<AbstractModule> = ArrayList() + val modulesNames: MutableList<String> = ArrayList() + val names: MutableList<String> = ArrayList() + val ops: MutableList<String> = ArrayList() + + /** + * Add a module with properties. + */ + fun add(module: AbstractModule, props: Properties) { + with(module) { + if (hasProperties()) { + propertyKeys.forEach { + setProperty(it, props.getProperty(it, "")) + } + } + + if (isEnabled) { + modules.add(this) + modulesNames.add(this.javaClass.simpleName) + names.addAll(this.commands) + } + } + } + + /** + * Add a command with properties. + */ + fun add(command: AbstractCommand, props: Properties) { + with(command) { + if (hasProperties()) { + getPropertyKeys().forEach { + setProperty(it, props.getProperty(it, "")) + } + } + if (isEnabled()) { + commands.add(this) + if (isVisible) { + if (isOp) { + ops.add(name) + } else { + names.add(name) + } + } + } + } + } + + /** + * Sort commands and modules names. + */ + fun sort() { + names.sort() + ops.sort() + modulesNames.sort() + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.java index eaf8d2d..ee86294 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.java @@ -94,14 +94,7 @@ public final class Constants { * The timer delay in minutes. */ public static final long TIMER_DELAY = 10L; - /** - * The twitter post flag property key. - */ - public static final String TWITTER_AUTOPOST_PROP = "twitter-auto-post"; - /** - * The Twitter handle property key. - */ - public static final String TWITTER_HANDLE_PROP = "twitter-handle"; + /** * Properties version line argument. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index affe915..5911a83 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -62,7 +62,6 @@ import net.thauvin.erik.mobibot.modules.Dice; import net.thauvin.erik.mobibot.modules.GoogleSearch; import net.thauvin.erik.mobibot.modules.Joke; import net.thauvin.erik.mobibot.modules.Lookup; -import net.thauvin.erik.mobibot.modules.ModuleException; import net.thauvin.erik.mobibot.modules.Ping; import net.thauvin.erik.mobibot.modules.RockPaperScissors; import net.thauvin.erik.mobibot.modules.StockQuote; @@ -94,12 +93,8 @@ import java.io.InputStream; import java.io.PrintStream; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Properties; -import java.util.Set; import java.util.Timer; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -116,7 +111,9 @@ import static org.apache.commons.lang3.StringUtils.lowerCase; @Version(properties = "version.properties", className = "ReleaseInfo") public class Mobibot extends PircBot { - // Info strings + /** + * Info Strings. + */ @SuppressWarnings("indentation") public static final List<String> INFO = List.of( @@ -129,16 +126,8 @@ public class Mobibot extends PircBot { private static final int MAX_RECONNECT = 10; // Timer private static final Timer TIMER = new Timer(true); - // Ignore command - public final Ignore ignoreCommand; - // Automatically post links to Twitter - public final boolean isTwitterAutoPost; - // Tell object - public final Tell tell; - // Commands - private final List<AbstractCommand> commands = new ArrayList<>(20); - // Commands Names - private final List<String> commandsNames = new ArrayList<>(); + // Commands and Modules + private final Addons addons = new Addons(); // Main channel private final String ircChannel; // IRC port @@ -149,20 +138,12 @@ public class Mobibot extends PircBot { private final Level loggerLevel; // Log directory private final String logsDir; - // Modules - private final List<AbstractModule> modules = new ArrayList<>(0); - // Modules - private final List<String> modulesNames = new ArrayList<>(0); - // Operators commands names - private final List<String> opsCommandsNames = new ArrayList<>(); + // Tell command + private final Tell tell; // Today's date private final String today = Utils.today(); - // Twitter auto-posts. - private final Set<Integer> twitterEntries = new HashSet<>(); - // Twitter handle for channel join notifications - private final String twitterHandle; // Twitter module - private final Twitter twitterModule; + private final Twitter twitter; // Backlogs URL private String backLogsUrl = ""; // Ident message @@ -176,7 +157,6 @@ public class Mobibot extends PircBot { // Weblog URL private String weblogUrl = ""; - /** * Creates a new {@link Mobibot} instance. * @@ -226,71 +206,51 @@ public class Mobibot extends PircBot { setPinboardAuth(p.getProperty("pinboard-api-token")); // Load the commands - addCommand(new AddLog()); - addCommand(new ChannelFeed(getChannelName(), p.getProperty("feed", ""))); - addCommand(new Cycle()); - addCommand(new Info()); - addCommand(new Me()); - addCommand(new Modules()); - addCommand(new Msg()); - addCommand(new Nick()); - addCommand(new Recap()); - addCommand(new Say()); - addCommand(new Users()); - addCommand(new Versions()); - - // Ignore command - ignoreCommand = new Ignore(p.getProperty("ignore", "")); - addCommand(ignoreCommand); + addons.add(new AddLog(this), p); + addons.add(new ChannelFeed(this, getChannelName()), p); + addons.add(new Cycle(this), p); + addons.add(new Ignore(this), p); + addons.add(new Info(this), p); + addons.add(new Me(this), p); + addons.add(new Modules(this), p); + addons.add(new Msg(this), p); + addons.add(new Nick(this), p); + addons.add(new Recap(this), p); + addons.add(new Say(this), p); + addons.add(new Users(this), p); + addons.add(new Versions(this), p); // Tell command - tell = new Tell(this, p.getProperty("tell-max-days"), p.getProperty("tell-max-size")); - if (tell.isEnabled()) { - addCommand(tell); - } + tell = new Tell(this); + addons.add(tell, p); // Load the links commands - addCommand(new Comment()); - addCommand(new Posting()); - addCommand(new Tags()); - addCommand(new UrlMgr(p.getProperty("tags", ""), p.getProperty("tags-keywords", ""))); - addCommand(new View()); + addons.add(new Comment(this), p); + addons.add(new Posting(this), p); + addons.add(new Tags(this), p); + addons.add(new UrlMgr(this), p); + addons.add(new View(this), p); // Load the modules - addModule(new Calc()); - addModule(new CurrencyConverter()); - addModule(new Dice()); - addModule(new GoogleSearch()); - addModule(new Joke()); - addModule(new Lookup()); - addModule(new Ping()); - addModule(new RockPaperScissors()); - addModule(new StockQuote()); - addModule(new War()); - addModule(new Weather2()); - addModule(new WorldTime()); + addons.add(new Calc(this), p); + addons.add(new CurrencyConverter(this), p); + addons.add(new Dice(this), p); + addons.add(new GoogleSearch(this), p); + addons.add(new Joke(this), p); + addons.add(new Lookup(this), p); + addons.add(new Ping(this), p); + addons.add(new RockPaperScissors(this), p); + addons.add(new StockQuote(this), p); + addons.add(new War(this), p); + addons.add(new Weather2(this), p); + addons.add(new WorldTime(this), p); // Twitter module - twitterModule = new Twitter(); - addModule(twitterModule); + twitter = new Twitter(this); + addons.add(twitter, p); - // Load the modules properties - modules.stream().filter(AbstractModule::hasProperties).forEach(module -> { - for (final String s : module.getPropertyKeys()) { - module.setProperty(s, p.getProperty(s, "")); - } - }); - - // Twitter extra properties - twitterHandle = p.getProperty(Constants.TWITTER_HANDLE_PROP, ""); - isTwitterAutoPost = - Boolean.parseBoolean(p.getProperty(Constants.TWITTER_AUTOPOST_PROP, "false")) - && twitterModule.isEnabled(); - - // Sort the command & module names - Collections.sort(commandsNames); - Collections.sort(opsCommandsNames); - Collections.sort(modulesNames); + // Sort the addons + addons.sort(); // Save the entries UrlMgr.saveEntries(this, true); @@ -415,33 +375,6 @@ public class Mobibot extends PircBot { } } - /** - * Adds a command. - * - * @param command The command to add. - */ - private void addCommand(final AbstractCommand command) { - commands.add(command); - if (command.isVisible()) { - if (command.isOp()) { - opsCommandsNames.add(command.getCommand()); - } else { - commandsNames.add(command.getCommand()); - } - } - } - - /** - * Adds a module. - * - * @param module The module to add. - */ - private void addModule(final AbstractModule module) { - modules.add(module); - modulesNames.add(module.getClass().getSimpleName()); - commandsNames.addAll(module.getCommands()); - } - /** * Adds pin on pinboard. * @@ -487,10 +420,13 @@ public class Mobibot extends PircBot { * * @param entry The entry to delete. */ - public final void deletePin(final EntryLink entry) { + public final void deletePin(final int index, final EntryLink entry) { if (pinboard != null) { pinboard.deletePost(entry); } + if (twitter.isAutoPost()) { + twitter.removeEntry(index); + } } /** @@ -554,7 +490,7 @@ public class Mobibot extends PircBot { * @return The modules names. */ public final List<String> getModulesNames() { - return modulesNames; + return addons.getModulesNames(); } /** @@ -577,6 +513,15 @@ public class Mobibot extends PircBot { return buff.toString(); } + /** + * Returns the Tell command. + * + * @return The tell command. + */ + public final Tell getTell() { + return tell; + } + /** * Returns the bot's timer. * @@ -595,6 +540,15 @@ public class Mobibot extends PircBot { return today; } + /** + * Returns the Twitter command. + * + * @return The Twitter command. + */ + public final Twitter getTwitter() { + return twitter; + } + /** * Returns the weblog URL. * @@ -613,9 +567,9 @@ public class Mobibot extends PircBot { * @return {@code true} if the topic was found, {@code false} otherwise. */ private boolean helpCommands(final String sender, final String topic, final boolean isPrivate) { - for (final AbstractCommand command : commands) { - if (command.isVisible() && command.getCommand().startsWith(topic)) { - return command.helpResponse(this, topic, sender, isOp(sender), isPrivate); + for (final AbstractCommand command : addons.getCommands()) { + if (command.isVisible() && command.getName().startsWith(topic)) { + return command.helpResponse(topic, sender, isOp(sender), isPrivate); } } return false; @@ -635,10 +589,10 @@ public class Mobibot extends PircBot { Utils.helpIndent(Utils.helpFormat("%c " + Constants.HELP_CMD + " <command>", getNick(), isPrivate)), isPrivate); send(sender, "The commands are:", isPrivate); - sendList(sender, commandsNames, 8, isPrivate, true); + sendList(sender, addons.getNames(), 8, isPrivate, true); if (isOp) { send(sender, "The op commands are:", isPrivate); - sendList(sender, opsCommandsNames, 8, isPrivate, true); + sendList(sender, addons.getOps(), 8, isPrivate, true); } } @@ -651,13 +605,11 @@ public class Mobibot extends PircBot { * @return {@code true} if the topic was found, {@code false} otherwise. */ private boolean helpModules(final String sender, final String topic, final boolean isPrivate) { - for (final AbstractModule module : modules) { - if (module.isEnabled()) { - for (final String cmd : module.getCommands()) { - if (topic.equals(cmd)) { - module.helpResponse(this, sender, isPrivate); - return true; - } + for (final AbstractModule module : addons.getModules()) { + for (final String cmd : module.getCommands()) { + if (topic.equals(cmd)) { + module.helpResponse(sender, isPrivate); + return true; } } } @@ -721,7 +673,7 @@ public class Mobibot extends PircBot { */ public final void joinChannel() { joinChannel(ircChannel); - twitterNotification("has joined " + ircChannel); + twitter.notification("%1$s %2$s has joined %3$s"); } /** @@ -746,9 +698,7 @@ public class Mobibot extends PircBot { final String message) { LOGGER.debug(">>> {} : {}", sender, message); - if (tell.isEnabled()) { - tell.send(sender, true); - } + tell.send(sender, true); if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); @@ -764,17 +714,17 @@ public class Mobibot extends PircBot { helpResponse(sender, args, false); } else { // Commands - for (final AbstractCommand command : commands) { - if (command.isPublic() && command.getCommand().startsWith(cmd)) { - command.commandResponse(this, sender, login, args, isOp(sender), false); + for (final AbstractCommand command : addons.getCommands()) { + if (command.isPublic() && command.getName().startsWith(cmd)) { + command.commandResponse(sender, login, args, isOp(sender), false); return; } } // Modules - for (final AbstractModule module : modules) { // modules + for (final AbstractModule module : addons.getModules()) { // modules for (final String c : module.getCommands()) { if (cmd.startsWith(c)) { - module.commandResponse(this, sender, cmd, args, false); + module.commandResponse(sender, cmd, args, false); return; } } @@ -782,9 +732,9 @@ public class Mobibot extends PircBot { } } else { // Commands, e.g.: https://www.example.com/ - for (final AbstractCommand command : commands) { + for (final AbstractCommand command : addons.getCommands()) { if (command.matches(message)) { - command.commandResponse(this, sender, login, message, isOp(sender), false); + command.commandResponse(sender, login, message, isOp(sender), false); return; } } @@ -816,6 +766,7 @@ public class Mobibot extends PircBot { if (cmd.startsWith(Constants.HELP_CMD)) { // help helpResponse(sender, args, true); } else if (isOp && "kill".equals(cmd)) { // kill + twitter.notification("%1$s killed by " + sender + "on %3$s"); sendRawLine("QUIT : Poof!"); System.exit(0); } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug @@ -828,23 +779,23 @@ public class Mobibot extends PircBot { } else if (isOp && Constants.DIE_CMD.equals(cmd)) { // die send(sender + " has just signed my death sentence."); TIMER.cancel(); - twitterShutdown(); - twitterNotification("killed by " + sender + " on " + ircChannel); + twitter.shutdown(); + twitter.notification("%1$s stopped by " + sender + "on %3$s"); sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); } else { - for (final AbstractCommand command : commands) { - if (command.getCommand().startsWith(cmd)) { - command.commandResponse(this, sender, login, args, isOp, true); + for (final AbstractCommand command : addons.getCommands()) { + if (command.getName().startsWith(cmd)) { + command.commandResponse(sender, login, args, isOp, true); return; } } - for (final AbstractModule module : modules) { + for (final AbstractModule module : addons.getModules()) { if (module.isPrivateMsgEnabled()) { for (final String c : module.getCommands()) { if (cmd.equals(c)) { - module.commandResponse(this, sender, cmd, args, true); + module.commandResponse(sender, cmd, args, true); return; } } @@ -870,9 +821,7 @@ public class Mobibot extends PircBot { */ @Override protected void onJoin(final String channel, final String sender, final String login, final String hostname) { - if (tell.isEnabled()) { - tell.send(sender); - } + tell.send(sender); } /** @@ -880,9 +829,7 @@ public class Mobibot extends PircBot { */ @Override protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { - if (tell.isEnabled()) { - tell.send(newNick); - } + tell.send(newNick); } /** @@ -1012,91 +959,6 @@ public class Mobibot extends PircBot { } } - /** - * Add an entry to be posted on Twitter. - * - * @param index The entry index. - */ - public void twitterAddEntry(final int index) { - twitterEntries.add(index); - } - - /** - * Post an entry to twitter. - * - * @param index The post entry index. - */ - @SuppressFBWarnings("SUI_CONTAINS_BEFORE_REMOVE") - public final void twitterEntryPost(final int index) { - if (isTwitterAutoPost && twitterEntries.contains(index) && UrlMgr.getEntriesCount() >= index) { - final EntryLink entry = UrlMgr.getEntry(index); - final String msg = - entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + getChannel(); - new Thread(() -> { - try { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1); - } - twitterModule.post(twitterHandle, msg, false); - } catch (ModuleException e) { - LOGGER.warn("Failed to post entry on Twitter.", e); - } - }).start(); - twitterEntries.remove(index); - } - } - - /** - * Return the total count of links to be posted to twitter. - * - * @return The count of twitter links. - */ - public final int twitterLinksCount() { - return twitterEntries.size(); - } - - /** - * Send a notification to the registered Twitter handle. - * - * @param msg The twitter message. - */ - final void twitterNotification(final String msg) { - if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { - new Thread(() -> { - try { - twitterModule.post( - twitterHandle, - getName() + ' ' + ReleaseInfo.VERSION + " " + msg, - true); - } catch (ModuleException e) { - LOGGER.warn("Failed to notify @{}: {}", twitterHandle, msg, e); - } - }).start(); - } - } - - /** - * Removes entry from Twitter auto-post. - * - * @param index The entry's index. - */ - public final void twitterRemoveEntry(final int index) { - twitterEntries.remove(index); - } - - - /** - * Post all the links on twitter on shutdown. - */ - final void twitterShutdown() { - if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) { - LOGGER.debug("Twitter shutdown."); - for (final int i : twitterEntries) { - twitterEntryPost(i); - } - } - } - /** * Updates pin on pinboard. * diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt index 09abe27..5db5a93 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt @@ -36,6 +36,6 @@ import java.util.* class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() { override fun run() { - bot.twitterEntryPost(index) + bot.twitter.postEntry(index) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 99a678c..df19332 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -34,16 +34,18 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils +import java.util.concurrent.ConcurrentHashMap -abstract class AbstractCommand { - abstract val command: String +abstract class AbstractCommand(val bot: Mobibot) { + abstract val name: String abstract val help: List<String> abstract val isOp: Boolean abstract val isPublic: Boolean abstract val isVisible: Boolean + private val properties: MutableMap<String, String> = ConcurrentHashMap() + abstract fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -51,7 +53,7 @@ abstract class AbstractCommand { isPrivate: Boolean ) - open fun helpResponse(bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { + open fun helpResponse(command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { if (!this.isOp || this.isOp == isOp) { for (h in help) { bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivate), isPrivate) @@ -61,7 +63,32 @@ abstract class AbstractCommand { return false } + open fun getProperty(key: String) : String? { + return properties[key] + } + open fun getPropertyKeys(): Set<String> { + return properties.keys + } + + open fun hasProperties(): Boolean { + return properties.isNotEmpty() + } + + open fun initProperties(vararg keys: String) { + keys.forEach { + properties[it] = "" + } + } + + open fun isEnabled(): Boolean { + return true + } + open fun matches(message: String): Boolean { return false } + + open fun setProperty(key: String, value: String) { + properties[key] = value + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt index 0c01306..addface 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -38,15 +38,14 @@ import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.getHistory import net.thauvin.erik.mobibot.entries.EntriesMgr import java.io.File -class AddLog : AbstractCommand() { - override val command = "addlog" +class AddLog(bot: Mobibot) : AbstractCommand(bot) { + override val name = "addlog" override val help = emptyList<String>() override val isOp = true override val isPublic = false override val isVisible = false override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index a5ce92a..b97f260 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -35,10 +35,9 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.FeedReader import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import org.apache.commons.lang3.StringUtils.isNotBlank -class ChannelFeed(channel: String, private val feedUrl: String) : AbstractCommand() { - override val command = channel +class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { + override val name = channel override val help = listOf( "To list the last 5 posts from the channel's weblog feed:", Utils.helpIndent("%c $channel") @@ -47,18 +46,23 @@ class ChannelFeed(channel: String, private val feedUrl: String) : AbstractComman override val isPublic = true override val isVisible = true + companion object { + const val FEED_PROP = "feed" + } + override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean ) { - if (isNotBlank(feedUrl)) { - Thread(FeedReader(bot, sender, feedUrl)).start() - } else { - bot.send(sender, "There is no feed setup for this channel.", false) + with(getProperty(FEED_PROP)) { + if (!this.isNullOrBlank()) { + Thread(FeedReader(bot, sender, this)).start() + } else { + bot.send(sender, "There is no feed setup for this channel.", false) + } } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index cfd7938..dbccdd6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -35,12 +35,12 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -class Cycle : AbstractCommand() { +class Cycle(bot: Mobibot) : AbstractCommand(bot) { private val wait = 10 - override val command = "cycle" + override val name = "cycle" override val help = listOf( "To have the bot leave the channel and come back:", - Utils.helpIndent("%c $command") + Utils.helpIndent("%c $name") ) override val isOp = true override val isPublic = false @@ -48,7 +48,6 @@ class Cycle : AbstractCommand() { override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 79c6a08..2999272 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -37,29 +37,27 @@ import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.links.UrlMgr import java.util.* -class Ignore(defaultIgnore: String) : AbstractCommand() { +class Ignore(bot: Mobibot) : AbstractCommand(bot) { private val me = "me" init { - if (defaultIgnore.isNotBlank()) { - ignored.addAll(defaultIgnore.split(UrlMgr.LINK_MATCH.toRegex())) - } + initProperties(IGNORE_PROP) } - override val command = IGNORE_CMD + override val name = IGNORE_CMD override val help = listOf( "To ignore a link posted to the channel:", Utils.helpIndent("https://www.foo.bar %n"), "To check your ignore status:", - Utils.helpIndent("%c $command"), + Utils.helpIndent("%c $name"), "To toggle your ignore status:", - Utils.helpIndent("%c $command $me") + Utils.helpIndent("%c $name $me") ) private val helpOp = listOf( "To ignore a link posted to the channel:", Utils.helpIndent("https://www.foo.bar " + Utils.bold("%n"), false), "To add/remove nicks from the ignored list:", - Utils.helpIndent("%c $command <nick> [<nick> ...]") + Utils.helpIndent("%c $name <nick> [<nick> ...]") ) override val isOp = false @@ -68,6 +66,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { companion object { const val IGNORE_CMD = "ignore" + const val IGNORE_PROP = IGNORE_CMD private val ignored = TreeSet<String>() @JvmStatic @@ -77,7 +76,6 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { } override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -94,7 +92,6 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { } override fun helpResponse( - bot: Mobibot, command: String, sender: String, isOp: Boolean, @@ -106,7 +103,7 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { } true } else { - super.helpResponse(bot, command, sender, isOp, isPrivate) + super.helpResponse(command, sender, isOp, isPrivate) } } @@ -149,4 +146,12 @@ class Ignore(defaultIgnore: String) : AbstractCommand() { bot.send(sender, "No one is currently ${Utils.bold("ignored")}.", isPrivate) } } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (IGNORE_PROP == key) { + ignored.addAll(value.split(UrlMgr.LINK_MATCH.toRegex())) + } + } + } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt index 0f2f4f9..fc6e546 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -37,18 +37,17 @@ import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.links.UrlMgr import java.lang.management.ManagementFactory -class Info : AbstractCommand() { - override val command = "info" +class Info(bot: Mobibot) : AbstractCommand(bot) { + override val name = "info" override val help = listOf( "To view information about the bot:", - Utils.helpIndent("%c $command") + Utils.helpIndent("%c $name") ) override val isOp = false override val isPublic = true override val isVisible = true override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -56,7 +55,7 @@ class Info : AbstractCommand() { isPrivate: Boolean ) { for (info in Mobibot.INFO) { - bot.send(sender, info, isPrivate) + bot.send(sender, info, isPrivate) } val info = StringBuilder("Uptime: ") @@ -67,13 +66,13 @@ class Info : AbstractCommand() { append(UrlMgr.entriesCount) if (isOp) { - if (bot.tell.isEnabled) { + if (bot.tell.isEnabled()) { append(", Messages: ") append(bot.tell.size()) } - if (bot.isTwitterAutoPost) { + if (bot.twitter.isAutoPost) { append(", Twitter: ") - append(bot.twitterLinksCount()) + append(bot.twitter.entriesCount()) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt index e7bd4d1..4b27cd7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt @@ -35,18 +35,17 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -class Me : AbstractCommand() { - override val command = "me" +class Me(bot: Mobibot) : AbstractCommand(bot) { + override val name = "me" override val help = listOf( "To have the bot perform an action:", - Utils.helpIndent("%c $command <action>") + Utils.helpIndent("%c $name <action>") ) override val isOp = true override val isPublic = false override val isVisible = true override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index e997677..af2f629 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -35,18 +35,17 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -class Modules : AbstractCommand() { - override val command = "modules" +class Modules(bot: Mobibot) : AbstractCommand(bot) { + override val name = "modules" override val help = listOf( "To view a list of enabled modules:", - Utils.helpIndent("%c $command") + Utils.helpIndent("%c $name") ) override val isOp = true override val isPublic = false override val isVisible = true override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -54,12 +53,13 @@ class Modules : AbstractCommand() { isPrivate: Boolean ) { if (isOp) { - val modulesNames = bot.modulesNames - if (modulesNames.isEmpty()) { - bot.send(sender, "There are no enabled modules.", isPrivate) - } else { - bot.send(sender, "The enabled modules are: ", isPrivate) - bot.sendList(sender, modulesNames, 7, isPrivate, false) + with(bot.modulesNames) { + if (isEmpty()) { + bot.send(sender, "There are no enabled modules.", isPrivate) + } else { + bot.send(sender, "The enabled modules are: ", isPrivate) + bot.sendList(sender, this, 7, isPrivate, false) + } } } else { bot.helpDefault(sender, isOp, isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt index 8f5814f..37c50df 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt @@ -35,18 +35,17 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -class Msg : AbstractCommand() { - override val command = "msg" +class Msg(bot: Mobibot) : AbstractCommand(bot) { + override val name = "msg" override val help = listOf( "To have the bot send a private message to someone:", - Utils.helpIndent("%c $command <nick> <text>") + Utils.helpIndent("%c $name <nick> <text>") ) override val isOp = true override val isPublic = true override val isVisible = true override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -58,7 +57,7 @@ class Msg : AbstractCommand() { if (args.length > 2) { bot.send(msg[0], msg[1], isPrivate) } else { - helpResponse(bot, command, sender, isOp, isPrivate) + helpResponse(name, sender, isOp, isPrivate) } } else { bot.helpDefault(sender, isOp, isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt index 95f3a55..6d12d2f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt @@ -35,18 +35,17 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -class Nick : AbstractCommand() { - override val command = "nick" +class Nick(bot: Mobibot) : AbstractCommand(bot) { + override val name = "nick" override val help = listOf( "To change the bot's nickname:", - Utils.helpIndent("%c $command <nick>") + Utils.helpIndent("%c $name <nick>") ) override val isOp = true override val isPublic = true override val isVisible = true override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt index 7dfbe5e..168a0cf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -38,11 +38,11 @@ import java.time.Clock import java.time.LocalDateTime import java.util.* -class Recap : AbstractCommand() { - override val command = "recap" +class Recap(bot: Mobibot) : AbstractCommand(bot) { + override val name = "recap" override val help = listOf( "To list the last 10 public channel messages:", - Utils.helpIndent("%c $command") + Utils.helpIndent("%c $name") ) override val isOp = false override val isPublic = true @@ -76,7 +76,6 @@ class Recap : AbstractCommand() { } override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt index 19e9f7e..be8d6f0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt @@ -35,11 +35,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -class Say : AbstractCommand() { - override val command = "say" +class Say(bot: Mobibot) : AbstractCommand(bot) { + override val name = "say" override val help = listOf( "To have the bot say something on the channel:", - Utils.helpIndent("%c $command <text>") + Utils.helpIndent("%c $name <text>") ) override val isOp = true override val isPublic = false @@ -47,7 +47,6 @@ class Say : AbstractCommand() { override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index 73524f3..49d1130 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -37,11 +37,11 @@ import net.thauvin.erik.mobibot.Utils import org.jibble.pircbot.User import java.util.* -class Users : AbstractCommand() { - override val command = "users" +class Users(bot: Mobibot) : AbstractCommand(bot) { + override val name = "users" override val help = listOf( "To list the users present on the channel:", - Utils.helpIndent("%c $command") + Utils.helpIndent("%c $name") ) override val isOp = false override val isPublic = true @@ -49,7 +49,6 @@ class Users : AbstractCommand() { override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 91f0044..7b80f3d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -50,9 +50,13 @@ public class Versions extends AbstractCommand { + ", " + System.getProperty("java.vm.info") + ')'); + public Versions(@NotNull final Mobibot bot) { + super(bot); + } + @NotNull @Override - public String getCommand() { + public String getName() { return "versions"; } @@ -61,7 +65,7 @@ public class Versions extends AbstractCommand { @Override public List<String> getHelp() { return List.of("To view the versions data (bot, java, etc.):", - Utils.helpIndent("%s " + getCommand())); + Utils.helpIndent("%s " + getName())); } @Override @@ -80,18 +84,17 @@ public class Versions extends AbstractCommand { } @Override - public void commandResponse(@NotNull final Mobibot bot, - @NotNull final String sender, + public void commandResponse(@NotNull final String sender, @NotNull final String login, @NotNull final String args, final boolean isOp, final boolean isPrivate) { if (isOp) { for (final String v : versions) { - bot.send(sender, v, isPrivate); + getBot().send(sender, v, isPrivate); } } else { - bot.helpDefault(sender, false, isPrivate); + getBot().helpDefault(sender, false, isPrivate); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 3e8d911..2ecba68 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -39,8 +39,8 @@ import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink -class Comment : AbstractCommand() { - override val command = COMMAND +class Comment(bot: Mobibot) : AbstractCommand(bot) { + override val name = COMMAND override val help = listOf( "To add a comment:", Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), @@ -59,7 +59,6 @@ class Comment : AbstractCommand() { } override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -89,13 +88,12 @@ class Comment : AbstractCommand() { } override fun helpResponse( - bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean ): Boolean { - if (super.helpResponse(bot, command, sender, isOp, isPrivate)) { + if (super.helpResponse(command, sender, isOp, isPrivate)) { if (isOp) { bot.send(sender, "To change a comment's author:", isPrivate) bot.send( diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index c5922b9..42e7e4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -39,8 +39,8 @@ import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink -class Posting : AbstractCommand() { - override val command = "posting" +class Posting(bot: Mobibot) : AbstractCommand(bot) { + override val name = "posting" override val help = listOf( "Post a URL, by saying it on a line on its own:", Utils.helpIndent("<url> [<title>] ${Tags.COMMAND}}: <+tag> [...]]"), @@ -58,7 +58,6 @@ class Posting : AbstractCommand() { override val isVisible = true override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -70,14 +69,14 @@ class Posting : AbstractCommand() { if (index < UrlMgr.entriesCount) { when (val cmd = cmds[1].trim()) { - "" -> showEntry(bot, index) - "-" -> removeEntry(bot, sender, login, isOp, index) // L1:- + "" -> showEntry(index) + "-" -> removeEntry(sender, login, isOp, index) // L1:- else -> { when (cmd[0]) { - '|' -> changeTitle(bot, cmd, index) // L1:|<title> - '=' -> changeUrl(bot, cmd, login, isOp, index) // L1:=<url> - '?' -> changeAuthor(bot, cmd, sender, isOp, index) // L1:?<author> - else -> addComment(bot, cmd, sender, index) // L1:<comment> + '|' -> changeTitle(cmd, index) // L1:|<title> + '=' -> changeUrl(cmd, login, isOp, index) // L1:=<url> + '?' -> changeAuthor(cmd, sender, isOp, index) // L1:?<author> + else -> addComment(cmd, sender, index) // L1:<comment> } } } @@ -88,7 +87,7 @@ class Posting : AbstractCommand() { return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex()) } - private fun addComment(bot: Mobibot, cmd: String, sender: String, index: Int) { + private fun addComment(cmd: String, sender: String, index: Int) { val entry: EntryLink = UrlMgr.getEntry(index) val commentIndex = entry.addComment(cmd, sender) val comment = entry.getComment(commentIndex) @@ -96,7 +95,7 @@ class Posting : AbstractCommand() { UrlMgr.saveEntries(bot, false) } - private fun changeTitle(bot: Mobibot, cmd: String, index: Int) { + private fun changeTitle(cmd: String, index: Int) { if (cmd.length > 1) { val entry: EntryLink = UrlMgr.getEntry(index) entry.title = cmd.substring(1).trim() @@ -106,7 +105,7 @@ class Posting : AbstractCommand() { } } - private fun changeUrl(bot: Mobibot, cmd: String, login: String, isOp: Boolean, index: Int) { + private fun changeUrl(cmd: String, login: String, isOp: Boolean, index: Int) { val entry: EntryLink = UrlMgr.getEntry(index) if (entry.login == login || isOp) { val link = cmd.substring(1) @@ -120,7 +119,7 @@ class Posting : AbstractCommand() { } } - private fun changeAuthor(bot: Mobibot, cmd: String, sender: String, isOp: Boolean, index: Int) { + private fun changeAuthor(cmd: String, sender: String, isOp: Boolean, index: Int) { if (isOp) { if (cmd.length > 1) { val entry: EntryLink = UrlMgr.getEntry(index) @@ -133,13 +132,10 @@ class Posting : AbstractCommand() { } } - private fun removeEntry(bot: Mobibot, sender: String, login: String, isOp: Boolean, index: Int) { + private fun removeEntry(sender: String, login: String, isOp: Boolean, index: Int) { val entry: EntryLink = UrlMgr.getEntry(index) if (entry.login == login || isOp) { - bot.deletePin(entry) - if (bot.isTwitterAutoPost) { - bot.twitterRemoveEntry(index) - } + bot.deletePin(index, entry) UrlMgr.removeEntry(index) bot.send("Entry ${Constants.LINK_CMD}${index + 1} removed.") UrlMgr.saveEntries(bot, false) @@ -148,7 +144,7 @@ class Posting : AbstractCommand() { } } - private fun showEntry(bot: Mobibot, index: Int) { + private fun showEntry(index: Int) { val entry: EntryLink = UrlMgr.getEntry(index) bot.send(EntriesUtils.buildLink(index, entry)) if (entry.hasTags()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index 6978794..511ca46 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -39,8 +39,8 @@ import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink -class Tags : AbstractCommand() { - override val command = COMMAND +class Tags(bot: Mobibot) : AbstractCommand(bot) { + override val name = COMMAND override val help = listOf( "To categorize or tag a URL, use its label and a T:", Utils.helpIndent("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") @@ -54,7 +54,6 @@ class Tags : AbstractCommand() { } override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt index 539094f..5a914a4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt @@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.TwitterTimer import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Ignore @@ -44,22 +43,24 @@ import net.thauvin.erik.mobibot.entries.EntryLink import org.jsoup.Jsoup import java.io.IOException -class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { - private val keywords: List<String> - private val defaultTags: List<String> - override val command = Constants.LINK_CMD +class UrlMgr(bot: Mobibot) : AbstractCommand(bot) { + private val keywords: MutableList<String> = ArrayList() + private val defaultTags: MutableList<String> = ArrayList() + + override val name = Constants.LINK_CMD override val help = emptyList<String>() override val isOp = false override val isPublic = false override val isVisible = false init { - this.keywords = keywords.split(TAG_MATCH.toRegex()) - this.defaultTags = defaultTags.split(TAG_MATCH.toRegex()) + initProperties(TAGS_PROP, KEYWORDS_PROP) } companion object { const val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*" + const val KEYWORDS_PROP = "tags-keywords" + const val TAGS_PROP = "tags" const val TAG_MATCH = ", *| +" // Entries array @@ -118,7 +119,6 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { } override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -157,8 +157,8 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { // Add Entry to pinboard. bot.addPin(entry) - // Queue link for posting to twitter - twitterPost(bot, index) + // Queue link for posting to Twitter. + bot.twitter.queueEntry(index) saveEntries(bot, isBackup) @@ -175,7 +175,6 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { } override fun helpResponse( - bot: Mobibot, command: String, sender: String, isOp: Boolean, @@ -234,11 +233,12 @@ class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() { return false } - private fun twitterPost(bot: Mobibot, index: Int) { - if (bot.isTwitterAutoPost) { - bot.twitterAddEntry(index) - bot.logger.debug("Scheduling ${Constants.LINK_CMD}${index + 1} for posting on Twitter.") - bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (KEYWORDS_PROP == key) { + keywords.addAll(value.split(TAG_MATCH.toRegex())) + } else if (KEYWORDS_PROP == key) { + defaultTags.addAll(value.split(TAG_MATCH.toRegex())) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index 0edde92..329b190 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -40,12 +40,12 @@ import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.getEntry import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink -class View : AbstractCommand() { +class View(bot: Mobibot) : AbstractCommand(bot) { private val maxEntries = 8 - override val command = VIEW_CMD + override val name = VIEW_CMD override val help = listOf( "To list or search the current URL posts:", - Utils.helpIndent("%c $command [<start>] [<query>]") + Utils.helpIndent("%c $name [<start>] [<query>]") ) override val isOp = false override val isPublic = true @@ -56,7 +56,6 @@ class View : AbstractCommand() { } override fun commandResponse( - bot: Mobibot, sender: String, login: String, args: String, @@ -110,7 +109,7 @@ class View : AbstractCommand() { i++ if (sent == maxEntries && i < max) { bot.send( - sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), false + sender, "To view more, try: " + Utils.bold("${bot.nick}: $name ${i + 1} $lcArgs"), false ) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index fe96e9d..05efbec 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -50,45 +50,43 @@ import java.util.concurrent.CopyOnWriteArrayList; * @since 1.0 */ public class Tell extends AbstractCommand { + /** + * Max days property. + */ + public static final String MAX_DAYS_PROP = "tell-max-days"; + /** + * Max size proeprty. + */ + public static final String MAX_SIZE_PROP = "tell-max-size"; /** * The tell command. */ public static final String TELL_CMD = "tell"; // Arrow private static final String ARROW = " --> "; - // Default maximum number of days to keep messages - private static final int DEFAULT_TELL_MAX_DAYS = 7; - // Default message max queue size - private static final int DEFAULT_TELL_MAX_SIZE = 50; // Serialized object file extension private static final String SER_EXT = ".ser"; // All keyword private static final String TELL_ALL_KEYWORD = "all"; //T he delete command. private static final String TELL_DEL_KEYWORD = "del"; - // Bot instance - private final Mobibot bot; - // Maximum number of days to keep messages - private final int maxDays; - // Message maximum queue size - private final int maxSize; // Messages queue private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); // Serialized object file private final String serializedObject; + // Maximum number of days to keep messages + private int maxDays = 7; + // Message maximum queue size + private int maxSize = 50; /** * Creates a new instance. * - * @param bot The bot. - * @param maxDays Max days. - * @param maxSize Max size. + * @param bot The bot. */ - public Tell(final Mobibot bot, final String maxDays, final String maxSize) { - super(); - this.bot = bot; - this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS); - this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE); + public Tell(final Mobibot bot) { + super(bot); + initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP); // Load the message queue serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT; @@ -106,7 +104,7 @@ public class Tell extends AbstractCommand { */ @SuppressWarnings("WeakerAccess") final boolean clean() { - bot.getLogger().debug("Cleaning the messages."); + getBot().getLogger().debug("Cleaning the messages."); return TellMessagesMgr.clean(messages, maxDays); } @@ -128,9 +126,9 @@ public class Tell extends AbstractCommand { if (deleted) { save(); - bot.send(sender, "Delivered messages have been deleted.", isPrivate); + getBot().send(sender, "Delivered messages have been deleted.", isPrivate); } else { - bot.send(sender, "No delivered messages were found.", isPrivate); + getBot().send(sender, "No delivered messages were found.", isPrivate); } } else { @@ -139,11 +137,11 @@ public class Tell extends AbstractCommand { for (final TellMessage message : messages) { found = message.isMatchId(id); - if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender))) { + if (found && (message.getSender().equalsIgnoreCase(sender) || getBot().isOp(sender))) { messages.remove(message); save(); - bot.send(sender, "Your message was deleted from the queue.", isPrivate); + getBot().send(sender, "Your message was deleted from the queue.", isPrivate); deleted = true; break; } @@ -151,20 +149,20 @@ public class Tell extends AbstractCommand { if (!deleted) { if (found) { - bot.send(sender, "Only messages that you sent can be deleted.", isPrivate); + getBot().send(sender, "Only messages that you sent can be deleted.", isPrivate); } else { - bot.send(sender, "The specified message [ID " + id + "] could not be found.", isPrivate); + getBot().send(sender, "The specified message [ID " + id + "] could not be found.", isPrivate); } } } } else { - helpResponse(bot, args, sender, isOp, isPrivate); + helpResponse(args, sender, isOp, isPrivate); } } @NotNull @Override - public String getCommand() { + public String getName() { return TELL_CMD; } @@ -172,9 +170,9 @@ public class Tell extends AbstractCommand { @Override public List<String> getHelp() { return List.of("To send a message to someone when they join the channel:", - Utils.helpIndent("%s " + TELL_CMD + " <nick> <message>"), + Utils.helpIndent("%c " + TELL_CMD + " <nick> <message>"), "To view queued and sent messages:", - Utils.helpIndent("%s " + TELL_CMD + ' ' + View.VIEW_CMD), + Utils.helpIndent("%c " + TELL_CMD + ' ' + View.VIEW_CMD), "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); } @@ -194,17 +192,16 @@ public class Tell extends AbstractCommand { } @Override - public void commandResponse(@NotNull final Mobibot bot, - @NotNull final String sender, + public void commandResponse(@NotNull final String sender, @NotNull final String login, @NotNull final String args, final boolean isOp, final boolean isPrivate) { if (isEnabled()) { if (StringUtils.isBlank(args)) { - helpResponse(bot, args, sender, isOp, isPrivate); + helpResponse(args, sender, isOp, isPrivate); } else if (args.startsWith(View.VIEW_CMD)) { - if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(args)) { + if (getBot().isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(args)) { viewAll(sender, isPrivate); } else { viewMessages(sender, isPrivate); @@ -221,15 +218,21 @@ public class Tell extends AbstractCommand { } } - /** - * Returns <code>true</code> if enabled. - * - * @return <code>true</code> or <code>false</code> - */ + @Override public boolean isEnabled() { return maxSize > 0 && maxDays > 0; } + @Override + public void setProperty(@NotNull final String key, @NotNull final String value) { + super.setProperty(key, value); + if (MAX_DAYS_PROP.equals(key)) { + this.maxDays = Utils.getIntProperty(value, maxDays); + } else if (MAX_SIZE_PROP.equals(key)) { + this.maxSize = Utils.getIntProperty(value, maxSize); + } + } + // New message. private void newMessage(final String sender, final String args, final boolean isOp, final boolean isPrivate) { final String[] split = args.split(" ", 2); @@ -242,13 +245,13 @@ public class Tell extends AbstractCommand { save(); - bot.send(sender, "Message [ID " + message.getId() + "] was queued for " - + Utils.bold(message.getRecipient()), isPrivate); + getBot().send(sender, "Message [ID " + message.getId() + "] was queued for " + + Utils.bold(message.getRecipient()), isPrivate); } else { - bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate); + getBot().send(sender, "Sorry, the messages queue is currently full.", isPrivate); } } else { - helpResponse(bot, args, sender, isOp, isPrivate); + helpResponse(args, sender, isOp, isPrivate); } } @@ -257,7 +260,7 @@ public class Tell extends AbstractCommand { */ @SuppressWarnings("WeakerAccess") final void save() { - TellMessagesMgr.save(serializedObject, messages, bot.getLogger()); + TellMessagesMgr.save(serializedObject, messages, getBot().getLogger()); } /** @@ -267,14 +270,14 @@ public class Tell extends AbstractCommand { * @param isMessage The message flag. */ public void send(final String nickname, final boolean isMessage) { - if (!nickname.equals(bot.getNick()) && isEnabled()) { + if (isEnabled() && !nickname.equals(getBot().getNick())) { messages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> { if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { if (message.getSender().equals(nickname)) { if (!isMessage) { - bot.send(nickname, Utils.bold("You") + " wanted me to remind you: " - + Utils.reverseColor(message.getMessage()), - true); + getBot().send(nickname, Utils.bold("You") + " wanted me to remind you: " + + Utils.reverseColor(message.getMessage()), + true); message.setIsReceived(); message.setIsNotified(); @@ -282,9 +285,9 @@ public class Tell extends AbstractCommand { save(); } } else { - bot.send(nickname, message.getSender() + " wanted me to tell you: " - + Utils.reverseColor(message.getMessage()), - true); + getBot().send(nickname, message.getSender() + " wanted me to tell you: " + + Utils.reverseColor(message.getMessage()), + true); message.setIsReceived(); @@ -292,12 +295,12 @@ public class Tell extends AbstractCommand { } } else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message.isNotified()) { - bot.send(nickname, - "Your message " - + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " - + Utils.bold(message.getRecipient()) + " on " - + Utils.utcDateTime(message.getReceived()), - true); + getBot().send(nickname, + "Your message " + + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + + Utils.bold(message.getRecipient()) + " on " + + Utils.utcDateTime(message.getReceived()), + true); message.setIsNotified(); @@ -329,13 +332,13 @@ public class Tell extends AbstractCommand { private void viewAll(final String sender, final boolean isPrivate) { if (!messages.isEmpty()) { for (final TellMessage message : messages) { - bot.send(sender, Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) - + " [ID: " + message.getId() + ", " - + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - isPrivate); + getBot().send(sender, Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) + + " [ID: " + message.getId() + ", " + + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', + isPrivate); } } else { - bot.send(sender, "There are no messages in the queue.", isPrivate); + getBot().send(sender, "There are no messages in the queue.", isPrivate); } } @@ -347,41 +350,41 @@ public class Tell extends AbstractCommand { if (message.isMatch(sender)) { if (!hasMessage) { hasMessage = true; - bot.send(sender, "Here are your messages: ", isPrivate); + getBot().send(sender, "Here are your messages: ", isPrivate); } if (message.isReceived()) { - bot.send(sender, - Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " - + Utils.bold(message.getId()) + ", DELIVERED]", - isPrivate); + getBot().send(sender, + Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " + + Utils.bold(message.getId()) + ", DELIVERED]", + isPrivate); } else { - bot.send(sender, - Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " - + Utils.bold(message.getId()) + ", QUEUED]", - isPrivate); + getBot().send(sender, + Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) + + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " + + Utils.bold(message.getId()) + ", QUEUED]", + isPrivate); } - bot.send(sender, Utils.helpIndent(message.getMessage()), isPrivate); + getBot().send(sender, Utils.helpIndent(message.getMessage()), isPrivate); } } if (!hasMessage) { - bot.send(sender, "You have no messages in the queue.", isPrivate); + getBot().send(sender, "You have no messages in the queue.", isPrivate); } else { - bot.send(sender, "To delete one or all delivered messages:", isPrivate); - bot.send(sender, - Utils.helpIndent(Utils.helpFormat( - "%c " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + TELL_ALL_KEYWORD + '>', - bot.getNick(), - isPrivate)), - isPrivate); - bot.send(sender, - "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), - isPrivate); + getBot().send(sender, "To delete one or all delivered messages:", isPrivate); + getBot().send(sender, + Utils.helpIndent(Utils.helpFormat( + "%c " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + TELL_ALL_KEYWORD + '>', + getBot().getNick(), + isPrivate)), + isPrivate); + getBot().send(sender, + "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), + isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java index 5b70957..f8483af 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java @@ -50,21 +50,24 @@ import java.util.concurrent.ConcurrentHashMap; * @since 1.0 */ public abstract class AbstractModule { + final Mobibot bot; final List<String> commands = new ArrayList<>(); final List<String> help = new ArrayList<>(); final Map<String, String> properties = new ConcurrentHashMap<>(); + AbstractModule(final Mobibot bot) { + this.bot = bot; + } + /** * Responds to a command. * - * @param bot The bot's instance. * @param sender The sender. * @param cmd The command. * @param args The command arguments. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public abstract void commandResponse(final Mobibot bot, - final String sender, + public abstract void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate); @@ -99,16 +102,26 @@ public abstract class AbstractModule { /** * Responds with the module's help. * - * @param bot The bot's instance. * @param sender The sender. * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. */ - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + public void helpResponse(final String sender, final boolean isPrivate) { for (final String h : help) { bot.send(sender, Utils.helpFormat(h, bot.getNick(), isPrivateMsgEnabled() && isPrivate), isPrivate); } } + /** + * Initializes the properties. + * + * @param keys The properties keys to initialize. + */ + public void initProperties(final String... keys) { + for (final String key : keys) { + properties.put(key, ""); + } + } + /** * Returns <code>true</code> if the module is enabled. * diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java index 400ecdb..862711f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java @@ -54,8 +54,8 @@ public class Calc extends AbstractModule { /** * The default constructor. */ - public Calc() { - super(); + public Calc(final Mobibot bot) { + super(bot); commands.add(CALC_CMD); @@ -86,15 +86,14 @@ public class Calc extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { if (StringUtils.isNotBlank(args)) { bot.send(calc(args)); } else { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index c6c3fce..f2dd205 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -82,8 +82,8 @@ public final class CurrencyConverter extends ThreadedModule { /** * Creates a new {@link CurrencyConverter} instance. */ - public CurrencyConverter() { - super(); + public CurrencyConverter(final Mobibot bot) { + super(bot); commands.add(CURRENCY_CMD); } @@ -180,8 +180,7 @@ public final class CurrencyConverter extends ThreadedModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { @@ -191,7 +190,7 @@ public final class CurrencyConverter extends ThreadedModule { } } - super.commandResponse(bot, sender, cmd, args, isPrivate); + super.commandResponse(sender, cmd, args, isPrivate); } /** @@ -199,7 +198,7 @@ public final class CurrencyConverter extends ThreadedModule { */ @SuppressFBWarnings("REDOS") @Override - void run(final Mobibot bot, final String sender, final String cmd, final String query, final boolean isPrivate) { + void run(final String sender, final String cmd, final String query, final boolean isPrivate) { if (EXCHANGE_RATES.isEmpty()) { try { loadRates(); @@ -214,13 +213,13 @@ public final class CurrencyConverter extends ThreadedModule { final Message msg = convertCurrency(query); bot.send(sender, msg); if (msg.isError()) { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } } else if (query.contains(CURRENCY_RATES_KEYWORD)) { bot.send(sender, "The currency rates for " + Utils.bold(pubDate) + " are:", isPrivate); bot.sendList(sender, currencyRates(), 3, isPrivate, false); } else { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } } @@ -228,7 +227,7 @@ public final class CurrencyConverter extends ThreadedModule { * {@inheritDoc} */ @Override - public void helpResponse(final Mobibot bot, final String sender, final boolean isPrivate) { + public void helpResponse(final String sender, final boolean isPrivate) { if (EXCHANGE_RATES.isEmpty()) { try { loadRates(); @@ -241,14 +240,12 @@ public final class CurrencyConverter extends ThreadedModule { } else { bot.send(sender, "To convert from one currency to another:", isPrivate); bot.send(sender, - Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR", - bot.getNick(), - isPrivate)), isPrivate); + Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR", bot.getNick(), false)), + isPrivate); bot.send(sender, "For a listing of current rates:", isPrivate); bot.send(sender, Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + ' ' + CURRENCY_RATES_KEYWORD, - bot.getNick(), - isPrivate)), isPrivate); + bot.getNick(), false)), isPrivate); bot.send(sender, "The supported currencies are: ", isPrivate); bot.sendList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java index 26ad7ef..861a244 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java @@ -53,8 +53,8 @@ public final class Dice extends AbstractModule { /** * The default constructor. */ - public Dice() { - super(); + public Dice(final Mobibot bot) { + super(bot); commands.add(DICE_CMD); @@ -66,8 +66,7 @@ public final class Dice extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index 0f64d0c..bc05a1f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -70,16 +70,15 @@ public final class GoogleSearch extends ThreadedModule { /** * Creates a new {@link GoogleSearch} instance. */ - public GoogleSearch() { - super(); + public GoogleSearch(final Mobibot bot) { + super(bot); commands.add(GOOGLE_CMD); help.add("To search Google:"); help.add(Utils.helpIndent("%c " + GOOGLE_CMD + " <query>")); - properties.put(GOOGLE_API_KEY_PROP, ""); - properties.put(GOOGLE_CSE_KEY_PROP, ""); + initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP); } /** @@ -146,7 +145,7 @@ public final class GoogleSearch extends ThreadedModule { * Searches Google. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String query, final boolean isPrivate) { + void run(final String sender, final String cmd, final String query, final boolean isPrivate) { if (StringUtils.isNotBlank(query)) { try { final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), @@ -159,7 +158,7 @@ public final class GoogleSearch extends ThreadedModule { bot.send(sender, e.getMessage(), isPrivate); } } else { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index cc24afa..00d1076 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -61,8 +61,8 @@ public final class Joke extends ThreadedModule { /** * Creates a new {@link Joke} instance. */ - public Joke() { - super(); + public Joke(final Mobibot bot) { + super(bot); commands.add(JOKE_CMD); @@ -104,19 +104,18 @@ public final class Joke extends ThreadedModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { - new Thread(() -> run(bot, sender, cmd, args, isPrivate)).start(); + new Thread(() -> run(sender, cmd, args, isPrivate)).start(); } /** * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String arg, final boolean isPrivate) { + void run(final String sender, final String cmd, final String arg, final boolean isPrivate) { try { bot.send(Utils.cyan(randomJoke().getText())); } catch (ModuleException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java index a065068..f97284d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java @@ -60,8 +60,8 @@ public final class Lookup extends AbstractModule { /** * The default constructor. */ - public Lookup() { - super(); + public Lookup(final Mobibot bot) { + super(bot); commands.add(LOOKUP_CMD); @@ -151,8 +151,7 @@ public final class Lookup extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { @@ -188,7 +187,7 @@ public final class Lookup extends AbstractModule { } } } else { - helpResponse(bot, sender, true); + helpResponse(sender, true); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java index ae1f518..2e10363 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java @@ -72,8 +72,8 @@ public class Ping extends AbstractModule { /** * The default constructor. */ - public Ping() { - super(); + public Ping(final Mobibot bot) { + super(bot); commands.add(PING_CMD); @@ -85,8 +85,7 @@ public class Ping extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 7374fa8..68bf1e3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -43,7 +43,7 @@ import kotlin.random.Random /** * Simple module example in Kotlin. */ -class RockPaperScissors : AbstractModule() { +class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { init { with(commands) { add(Hands.ROCK.name.toLowerCase()) @@ -96,22 +96,21 @@ class RockPaperScissors : AbstractModule() { } } - override fun commandResponse(bot: Mobibot, sender: String, cmd: String, args: String?, isPrivate: Boolean) { + override fun commandResponse(sender: String, cmd: String, args: String?, isPrivate: Boolean) { val hand = Hands.valueOf(cmd.toUpperCase()) val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] when { hand == botHand -> { - bot.action("${green(hand.name)} vs. ${green(botHand.name)} ~ The game is tied ~") + bot.send("${green(hand.name)} vs. ${green(botHand.name)}") + bot.action("tied.") } hand.beats(botHand) -> { - bot.action( - "${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)} ~ You win ~" - ) + bot.send("${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)}") + bot.action("lost.") } else -> { - bot.action( - "${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)} ~ You lose ~" - ) + bot.send("${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)}") + bot.action("wins.") } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 210d907..03b6ebb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -76,14 +76,14 @@ public final class StockQuote extends ThreadedModule { /** * Creates a new {@link StockQuote} instance. */ - public StockQuote() { - super(); + public StockQuote(final Mobibot bot) { + super(bot); commands.add(STOCK_CMD); help.add("To retrieve a stock quote:"); help.add(Utils.helpIndent("%c " + STOCK_CMD + " <symbol|keywords>")); - properties.put(ALPHAVANTAGE_API_KEY_PROP, ""); + initProperties(ALPHAVANTAGE_API_KEY_PROP); } @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", @@ -208,7 +208,7 @@ public final class StockQuote extends ThreadedModule { * Returns the specified stock quote from Alpha Avantage. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String symbol, final boolean isPrivate) { + void run(final String sender, final String cmd, final String symbol, final boolean isPrivate) { if (StringUtils.isNotBlank(symbol)) { try { final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); @@ -220,7 +220,7 @@ public final class StockQuote extends ThreadedModule { bot.send(e.getMessage()); } } else { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java index 6bdc29f..11d3cec 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java @@ -42,27 +42,26 @@ import net.thauvin.erik.mobibot.Mobibot; * @since 1.0 */ public abstract class ThreadedModule extends AbstractModule { - /** - * {@inheritDoc} - */ + ThreadedModule(final Mobibot bot) { + super(bot); + } + @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { if (isEnabled() && args.length() > 0) { - new Thread(() -> run(bot, sender, cmd, args, isPrivate)).start(); + new Thread(() -> run(sender, cmd, args, isPrivate)).start(); } else { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } } /** * Runs the thread. */ - abstract void run(Mobibot bot, - String sender, + abstract void run(String sender, @SuppressWarnings("unused") String cmd, String args, boolean isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 0ef6f15..5f47ef9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -32,15 +32,27 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.Constants; import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.ReleaseInfo; +import net.thauvin.erik.mobibot.TwitterTimer; import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.commands.links.UrlMgr; +import net.thauvin.erik.mobibot.entries.EntryLink; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; +import org.apache.commons.lang3.StringUtils; import twitter4j.DirectMessage; import twitter4j.Status; import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; +import java.util.HashSet; +import java.util.Set; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + /** * The Twitter module. * @@ -50,28 +62,31 @@ import twitter4j.conf.ConfigurationBuilder; */ public final class Twitter extends ThreadedModule { // Property keys + static final String AUTOPOST_PROP = "twitter-auto-post"; static final String CONSUMER_KEY_PROP = "twitter-consumerKey"; static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; + static final String HANDLE_PROP = "twitter-handle"; static final String TOKEN_PROP = "twitter-token"; static final String TOKEN_SECRET_PROP = "twitter-tokenSecret"; // Twitter command private static final String TWITTER_CMD = "twitter"; + // Twitter auto-posts. + private final Set<Integer> entries = new HashSet<>(); + /** * Creates a new {@link Twitter} instance. */ - public Twitter() { - super(); + public Twitter(final Mobibot bot) { + super(bot); commands.add(TWITTER_CMD); help.add("To post to Twitter:"); help.add(Utils.helpIndent("%c " + TWITTER_CMD + " <message>")); - properties.put(CONSUMER_SECRET_PROP, ""); - properties.put(CONSUMER_KEY_PROP, ""); - properties.put(TOKEN_PROP, ""); - properties.put(TOKEN_SECRET_PROP, ""); + properties.put(AUTOPOST_PROP, "false"); + initProperties(CONSUMER_KEY_PROP,CONSUMER_SECRET_PROP,HANDLE_PROP,TOKEN_PROP,TOKEN_SECRET_PROP); } /** @@ -115,6 +130,70 @@ public final class Twitter extends ThreadedModule { } } + /** + * Add an entry to be posted on Twitter. + * + * @param index The entry index. + */ + public final void addEntry(final int index) { + entries.add(index); + } + + public final int entriesCount() { + return entries.size(); + } + + public String getHandle() { + return properties.get(HANDLE_PROP); + } + + public final boolean hasEntry(final int index) { + return entries.contains(index); + } + + public boolean isAutoPost() { + return isEnabled() && Boolean.parseBoolean(properties.get(AUTOPOST_PROP)); + } + + @Override + boolean isValidProperties() { + for (final String s : getPropertyKeys()) { + if (!AUTOPOST_PROP.equals(s) && !HANDLE_PROP.equals(s) && StringUtils.isBlank(properties.get(s))) { + return false; + } + } + return true; + } + + /** + * Send a notification to the registered Twitter handle. + * + * @param msg The twitter message. + */ + public final void notification(final String msg) { + if (isEnabled() && isNotBlank(getHandle())) { + new Thread(() -> { + try { + post(String.format(msg, bot.getName(), ReleaseInfo.VERSION, bot.getChannel()), true); + } catch (ModuleException e) { + bot.getLogger().warn("Failed to notify @{}: {}", getHandle(), msg, e); + } + }).start(); + } + } + + /** + * Posts on Twitter. + * + * @param message The message to post. + * @param isDm The direct message flag. + * @throws ModuleException If an error occurs while posting. + */ + public void post(final String message, final boolean isDm) + throws ModuleException { + post(properties.get(HANDLE_PROP), message, isDm); + } + /** * Posts on Twitter. * @@ -135,11 +214,48 @@ public final class Twitter extends ThreadedModule { isDm); } + /** + * Post an entry to twitter. + * + * @param index The post entry index. + */ + @SuppressFBWarnings("SUI_CONTAINS_BEFORE_REMOVE") + public final void postEntry(final int index) { + if (isAutoPost() && hasEntry(index) && UrlMgr.getEntriesCount() >= index) { + final EntryLink entry = UrlMgr.getEntry(index); + final String msg = + entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + bot.getChannel(); + new Thread(() -> { + try { + if (bot.getLogger().isDebugEnabled()) { + bot.getLogger().debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1); + } + post(msg, false); + } catch (ModuleException e) { + bot.getLogger().warn("Failed to post entry on Twitter.", e); + } + }).start(); + removeEntry(index); + } + } + + public void queueEntry(final int index) { + if (isAutoPost()) { + addEntry(index); + bot.getLogger().debug("Scheduling ${Constants.LINK_CMD}${index + 1} for posting on Twitter."); + bot.getTimer().schedule(new TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L); + } + } + + public final void removeEntry(final int index) { + entries.remove(index); + } + /** * Posts to twitter. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String message, final boolean isPrivate) { + void run(final String sender, final String cmd, final String message, final boolean isPrivate) { try { bot.send(sender, post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getText(), @@ -149,4 +265,13 @@ public final class Twitter extends ThreadedModule { bot.send(sender, e.getMessage(), isPrivate); } } + + /** + * Post all the entries to Twitter on shutdown. + */ + public final void shutdown() { + for (final int index : entries) { + postEntry(index); + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index bf089fb..b1be81e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -58,8 +58,8 @@ public final class War extends AbstractModule { /** * The default constructor. */ - public War() { - super(); + public War(final Mobibot bot) { + super(bot); commands.add(WAR_CMD); @@ -71,8 +71,7 @@ public final class War extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 4d58090..a0894d0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -73,8 +73,8 @@ public class Weather2 extends ThreadedModule { /** * Creates a new {@link Weather2} instance. */ - public Weather2() { - super(); + public Weather2(final Mobibot bot) { + super(bot); commands.add(WEATHER_CMD); @@ -84,7 +84,7 @@ public class Weather2 extends ThreadedModule { help.add(Utils.helpIndent("%c " + WEATHER_CMD + " paris, fr")); help.add("The default ISO 3166 country code is " + bold("US") + ". Zip codes supported in most countries."); - properties.put(OWM_API_KEY_PROP, ""); + initProperties(OWM_API_KEY_PROP); } private static OWM.Country getCountry(final String countryCode) { @@ -218,12 +218,12 @@ public class Weather2 extends ThreadedModule { * Fetches the weather data from a specific city. */ @Override - void run(final Mobibot bot, final String sender, final String cmd, final String args, final boolean isPrivate) { + void run(final String sender, final String cmd, final String args, final boolean isPrivate) { if (StringUtils.isNotBlank(args)) { try { final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); if (messages.get(0).isError()) { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } else { for (final Message msg : messages) { bot.send(sender, msg); @@ -234,7 +234,7 @@ public class Weather2 extends ThreadedModule { bot.send(e.getMessage()); } } else { - helpResponse(bot, sender, isPrivate); + helpResponse(sender, isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 2eaa86c..48092d8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -63,9 +63,7 @@ public final class WorldTime extends AbstractModule { // Supported countries private static final Map<String, String> COUNTRIES_MAP; - /** - * The time command. - */ + // The Time command private static final String TIME_CMD = "time"; static { @@ -157,8 +155,8 @@ public final class WorldTime extends AbstractModule { /** * Creates a new {@link WorldTime} instance. */ - public WorldTime() { - super(); + public WorldTime(final Mobibot bot) { + super(bot); help.add("To display a country's current date/time:"); help.add(Utils.helpIndent("%c " + TIME_CMD) + " [<country code>]"); @@ -218,8 +216,7 @@ public final class WorldTime extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final Mobibot bot, - final String sender, + public void commandResponse(final String sender, final String cmd, final String args, final boolean isPrivate) { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java index c108282..2f9b21e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java @@ -55,6 +55,6 @@ public class CalcTest { @Test public void testCalcImpl() { - AbstractModuleTest.testAbstractModule(new Calc()); + AbstractModuleTest.testAbstractModule(new Calc(null)); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index f72d74a..f1b410a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -68,6 +68,6 @@ public class CurrencyConverterTest { @Test public void testCurrencyConvertererImpl() { - AbstractModuleTest.testAbstractModule(new CurrencyConverter()); + AbstractModuleTest.testAbstractModule(new CurrencyConverter(null)); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 23a1552..0d8a5f9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -51,7 +51,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; public class GoogleSearchTest extends LocalProperties { @Test public void testGoogleSearchImpl() { - AbstractModuleTest.testAbstractModule(new GoogleSearch()); + AbstractModuleTest.testAbstractModule(new GoogleSearch(null)); } @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE") diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java index 93836e7..df6ddc7 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -46,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class JokeTest { @Test public void testJokeImpl() { - AbstractModuleTest.testAbstractModule(new Joke()); + AbstractModuleTest.testAbstractModule(new Joke(null)); } @Test diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java index d0d55f5..6b1b7c4 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java @@ -49,7 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class LookupTest { @Test public void testLookupImpl() { - AbstractModuleTest.testAbstractModule(new Lookup()); + AbstractModuleTest.testAbstractModule(new Lookup(null)); } @Test diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java index 8dc6c8e..564f111 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java @@ -46,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class PingTest { @Test public void testPingImpl() { - AbstractModuleTest.testAbstractModule(new Ping()); + AbstractModuleTest.testAbstractModule(new Ping(null)); } @Test diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 0b77df5..2eec2a0 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -84,6 +84,6 @@ public class StockQuoteTest extends LocalProperties { @Test public void testStockQuoteImpl() { - AbstractModuleTest.testAbstractModule(new StockQuote()); + AbstractModuleTest.testAbstractModule(new StockQuote(null)); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java index f289a79..04cf36c 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Constants; import org.testng.annotations.Test; import java.net.InetAddress; @@ -72,7 +71,7 @@ public class TwitterTest extends LocalProperties { getProperty(Twitter.CONSUMER_SECRET_PROP), getProperty(Twitter.TOKEN_PROP), getProperty(Twitter.TOKEN_SECRET_PROP), - getProperty(Constants.TWITTER_HANDLE_PROP), + getProperty(Twitter.HANDLE_PROP), msg, true).getText()).as("twitterPost(" + msg + ')').isEqualTo(msg); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index 5eb40d4..0c1309e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -74,6 +74,6 @@ public class Weather2Test extends LocalProperties { @Test public void testWeather2Impl() { - AbstractModuleTest.testAbstractModule(new Weather2()); + AbstractModuleTest.testAbstractModule(new Weather2(null)); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java index 6d6e630..9c7299a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java @@ -54,6 +54,6 @@ public class WordTimeTest { @Test public void testWorldTimeImpl() { - AbstractModuleTest.testAbstractModule(new Lookup()); + AbstractModuleTest.testAbstractModule(new Lookup(null)); } } diff --git a/version.properties b/version.properties index 6e5c59b..46bdf8b 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Apr 16 16:00:41 PDT 2020 -version.buildmeta=294 +#Tue Apr 28 20:58:04 PDT 2020 +version.buildmeta=348 version.major=0 version.minor=8 version.patch=0 version.prerelease=alpha version.project=mobibot -version.semver=0.8.0-alpha+294 +version.semver=0.8.0-alpha+348 From 6787b0dcbe74d8b188f49946390597cbd590e5a1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 29 Apr 2020 11:51:22 -0700 Subject: [PATCH 396/842] Updated PMD version. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 94b4d4b..8c4120d 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+330" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+349" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index beb8f3c..13a7f5b 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+330" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+349" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index b33ac37..ece1a8b 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+330" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+349" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/build.gradle b/build.gradle index 693e640..0a7ea7a 100644 --- a/build.gradle +++ b/build.gradle @@ -104,7 +104,7 @@ tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { } pmd { - toolVersion = '6.22.0' + toolVersion = '6.23.0' ignoreFailures = true ruleSets = [] ruleSetFiles = files("${projectDir}/config/pmd.xml") From 1a5ae72d6e275df8f88e8f4539a9c613b1a4f66a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 29 Apr 2020 11:52:03 -0700 Subject: [PATCH 397/842] Added urlReader() method. --- .../java/net/thauvin/erik/mobibot/Utils.java | 22 +++++++++++++++ .../erik/mobibot/modules/GoogleSearch.java | 27 +++++-------------- .../thauvin/erik/mobibot/modules/Joke.java | 24 +++-------------- .../net/thauvin/erik/mobibot/UtilsTest.java | 16 ++++++++--- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index e4f582a..3d0632a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -32,16 +32,23 @@ package net.thauvin.erik.mobibot; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.commons.lang3.StringUtils; import org.jibble.pircbot.Colors; import org.jsoup.Jsoup; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * Miscellaneous utilities class. @@ -332,6 +339,21 @@ public final class Utils { return info.toString(); } + /** + * Reads contents of a URL. + * + * @param url The URL to read. + * @return The URL contents. + * @throws IOException If an IO error occurs. + */ + @SuppressFBWarnings("SECSSSRFUC") + public static String urlReader(final URL url) throws IOException { + try (final BufferedReader reader = new BufferedReader( + new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + } + /** * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>. * diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index bc05a1f..a36789f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -42,11 +42,8 @@ import org.jibble.pircbot.Colors; import org.json.JSONArray; import org.json.JSONObject; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; import java.net.URL; -import java.net.URLConnection; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -111,25 +108,15 @@ public final class GoogleSearch extends ThreadedModule { + "&q=" + q + "&filter=1&num=5&alt=json"); - final URLConnection conn = url.openConnection(); - final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = - new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - } + final JSONObject json = new JSONObject(Utils.urlReader(url)); + final JSONArray ja = json.getJSONArray("items"); - final JSONObject json = new JSONObject(sb.toString()); - final JSONArray ja = json.getJSONArray("items"); - - for (int i = 0; i < ja.length(); i++) { - final JSONObject j = ja.getJSONObject(i); - results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); - results.add( - new NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN)); - } + for (int i = 0; i < ja.length(); i++) { + final JSONObject j = ja.getJSONObject(i); + results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); + results.add( + new NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN)); } } catch (IOException e) { throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 00d1076..5c1d858 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -38,11 +38,7 @@ import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.PublicMessage; import org.json.JSONObject; -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.StandardCharsets; /** * The Joke module. @@ -79,22 +75,10 @@ public final class Joke extends ThreadedModule { static Message randomJoke() throws ModuleException { try { final URL url = new URL(JOKE_URL); - final URLConnection conn = url.openConnection(); - - final StringBuilder sb = new StringBuilder(); - try (final BufferedReader reader = - new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - } - - final JSONObject json = new JSONObject(sb.toString()); - - return new PublicMessage( - json.getJSONObject("value").get("joke").toString().replace("\\'", "'") - .replace("\\\"", "\"")); - } + final JSONObject json = new JSONObject(Utils.urlReader(url)); + return new PublicMessage( + json.getJSONObject("value").get("joke").toString().replace("\\'", "'") + .replace("\\\"", "\"")); } catch (Exception e) { throw new ModuleException("randomJoke()", "An error has occurred retrieving a random joke.", e); } diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index df2ff57..b754fbf 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -38,6 +38,8 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.File; +import java.io.IOException; +import java.net.URL; import java.time.LocalDateTime; import java.util.Calendar; @@ -52,7 +54,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class UtilsTest { private static final String ASCII = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; private final Calendar cal = Calendar.getInstance(); private final LocalDateTime localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0); @@ -71,7 +73,7 @@ public class UtilsTest { @Test public void testColorize() { assertThat(Utils.colorize(ASCII, Colors.REVERSE)).as("colorize(reverse)").isEqualTo( - Colors.REVERSE + ASCII + Colors.REVERSE); + Colors.REVERSE + ASCII + Colors.REVERSE); assertThat(Utils.colorize(ASCII, Colors.RED)).as("colorize(red)").isEqualTo(Colors.RED + ASCII + Colors.NORMAL); assertThat(Utils.colorize(null, Colors.RED)).as("colorize(null)").isEqualTo(Colors.NORMAL); } @@ -85,7 +87,7 @@ public class UtilsTest { public void testEnsureDir() { assertThat(Utils.ensureDir("dir", false)).as("ensureDir(dir, false)").isEqualTo("dir" + File.separatorChar); assertThat(Utils.ensureDir("https://erik.thauvin.net", true)).as("ensureDir(erik.thauvin.net, true)").isEqualTo( - "https://erik.thauvin.net/"); + "https://erik.thauvin.net/"); } @Test @@ -136,7 +138,7 @@ public class UtilsTest { @Test public void testUnescapeXml() { assertThat(Utils.unescapeXml("<a name="test & ''">")).isEqualTo( - "<a name=\"test & ''\">"); + "<a name=\"test & ''\">"); } @Test @@ -144,6 +146,12 @@ public class UtilsTest { assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(Utils.uptime(547800300076L)); } + @Test + public void testUrlReader() throws IOException { + assertThat(Utils.urlReader(new URL("https://postman-echo.com/status/200"))).as("urlReader()").isEqualTo( + "{\"status\":200}"); + } + @Test public void testUtcDateTime() { assertThat(Utils.utcDateTime(cal.getTime())).as("utcDateTime(date)").isEqualTo("1952-02-17 12:30"); From 725937bf61c11fd3071b561176b4105433e250c0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 29 Apr 2020 11:52:28 -0700 Subject: [PATCH 398/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/FeedReader.java | 2 +- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index cac105e..8c58b09 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -85,7 +85,7 @@ public class FeedReader implements Runnable { final List<SyndEntry> items = feed.getEntries(); if (items.isEmpty()) { - bot.send(sender, "There is currently nothing to view. Why don't you post something?", false); + bot.send(sender, "There is currently nothing to view.", false); } else { SyndEntry item; for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java index f2dd205..2794395 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java @@ -240,12 +240,12 @@ public final class CurrencyConverter extends ThreadedModule { } else { bot.send(sender, "To convert from one currency to another:", isPrivate); bot.send(sender, - Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR", bot.getNick(), false)), - isPrivate); + Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR", + bot.getNick(), isPrivateMsgEnabled())), isPrivate); bot.send(sender, "For a listing of current rates:", isPrivate); bot.send(sender, Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + ' ' + CURRENCY_RATES_KEYWORD, - bot.getNick(), false)), isPrivate); + bot.getNick(), isPrivateMsgEnabled())), isPrivate); bot.send(sender, "The supported currencies are: ", isPrivate); bot.sendList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false); } From 8bc04b09403458e62465b7f3597fff8d22d2c86a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 30 Apr 2020 14:51:02 -0700 Subject: [PATCH 399/842] Renamed UrlMgr to LinksMgr. --- detekt-baseline.xml | 2 +- .../net/thauvin/erik/mobibot/Mobibot.java | 45 ++++++------------- .../thauvin/erik/mobibot/commands/AddLog.kt | 4 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 4 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 4 +- .../erik/mobibot/commands/links/Comment.kt | 10 ++--- .../commands/links/{UrlMgr.kt => LinksMgr.kt} | 2 +- .../erik/mobibot/commands/links/Posting.kt | 28 ++++++------ .../erik/mobibot/commands/links/Tags.kt | 6 +-- .../erik/mobibot/commands/links/View.kt | 4 +- .../erik/mobibot/entries/EntryLink.java | 4 +- .../thauvin/erik/mobibot/modules/Twitter.java | 6 +-- 12 files changed, 50 insertions(+), 69 deletions(-) rename src/main/java/net/thauvin/erik/mobibot/commands/links/{UrlMgr.kt => LinksMgr.kt} (99%) diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 19a2a8f..d61a8d8 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -14,6 +14,6 @@ <ID>MagicNumber:View.kt$View$8</ID> <ID>NestedBlockDepth:Addons.kt$Addons$add</ID> <ID>NestedBlockDepth:Comment.kt$Comment$commandResponse</ID> - <ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID> + <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$commandResponse</ID> </Whitelist> </SmellBaseline> diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 5911a83..16d09b9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -48,9 +48,9 @@ import net.thauvin.erik.mobibot.commands.Say; import net.thauvin.erik.mobibot.commands.Users; import net.thauvin.erik.mobibot.commands.Versions; import net.thauvin.erik.mobibot.commands.links.Comment; +import net.thauvin.erik.mobibot.commands.links.LinksMgr; import net.thauvin.erik.mobibot.commands.links.Posting; import net.thauvin.erik.mobibot.commands.links.Tags; -import net.thauvin.erik.mobibot.commands.links.UrlMgr; import net.thauvin.erik.mobibot.commands.links.View; import net.thauvin.erik.mobibot.commands.tell.Tell; import net.thauvin.erik.mobibot.entries.EntriesMgr; @@ -96,6 +96,7 @@ import java.nio.file.Paths; import java.util.List; import java.util.Properties; import java.util.Timer; +import java.util.regex.Pattern; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.lowerCase; @@ -184,8 +185,8 @@ public class Mobibot extends PircBot { // Load the current entries and backlogs, if any try { - UrlMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, ircChannel); - LOGGER.debug("Last feed: {}", UrlMgr.getStartDate()); + LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, ircChannel); + LOGGER.debug("Last feed: {}", LinksMgr.getStartDate()); } catch (Exception e) { LOGGER.error("An error occurred while loading the logs.", e); } @@ -228,7 +229,7 @@ public class Mobibot extends PircBot { addons.add(new Comment(this), p); addons.add(new Posting(this), p); addons.add(new Tags(this), p); - addons.add(new UrlMgr(this), p); + addons.add(new LinksMgr(this), p); addons.add(new View(this), p); // Load the modules @@ -253,7 +254,7 @@ public class Mobibot extends PircBot { addons.sort(); // Save the entries - UrlMgr.saveEntries(this, true); + LinksMgr.saveEntries(this, true); } /** @@ -286,20 +287,20 @@ public class Mobibot extends PircBot { // Parse the command line final CommandLineParser parser = new DefaultParser(); - CommandLine line = null; + CommandLine commandLine = null; try { - line = parser.parse(options, args); + commandLine = parser.parse(options, args); } catch (ParseException e) { System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); e.printStackTrace(System.err); System.exit(1); } - if (line.hasOption(Constants.HELP_ARG.charAt(0))) { + if (commandLine.hasOption(Constants.HELP_ARG.charAt(0))) { // Output the usage new HelpFormatter().printHelp(Mobibot.class.getName(), options); - } else if (line.hasOption(Constants.VERSION_ARG.charAt(0))) { + } else if (commandLine.hasOption(Constants.VERSION_ARG.charAt(0))) { for (final String s : INFO) { System.out.println(s); } @@ -307,7 +308,7 @@ public class Mobibot extends PircBot { final Properties p = new Properties(); try (final InputStream fis = Files.newInputStream( - Paths.get(line.getOptionValue(Constants.PROPS_ARG.charAt(0), "./mobibot.properties")))) { + Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG.charAt(0), "./mobibot.properties")))) { // Load the properties files p.load(fis); } catch (FileNotFoundException e) { @@ -325,7 +326,7 @@ public class Mobibot extends PircBot { final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); // Redirect the stdout and stderr - if (!line.hasOption(Constants.DEBUG_ARG.charAt(0))) { + if (!commandLine.hasOption(Constants.DEBUG_ARG.charAt(0))) { try { final PrintStream stdout = new PrintStream( new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); @@ -493,26 +494,6 @@ public class Mobibot extends PircBot { return addons.getModulesNames(); } - /** - * Returns the bot's nickname regexp pattern. - * - * @return The nickname regexp pattern. - */ - private String getNickPattern() { - final StringBuilder buff = new StringBuilder(0); - - for (final char c : getNick().toCharArray()) { - if (Character.isLetter(c)) { - buff.append('[').append(lowerCase(String.valueOf(c))).append(StringUtils.upperCase(String.valueOf(c))) - .append(']'); - } else { - buff.append(c); - } - } - - return buff.toString(); - } - /** * Returns the Tell command. * @@ -700,7 +681,7 @@ public class Mobibot extends PircBot { tell.send(sender, true); - if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command> + if (message.matches("(?i)" + Pattern.quote(getNick()) + ":.*")) { // mobibot: <command> final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); final String cmd = lowerCase(cmds[0]); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt index addface..f7639a5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -33,8 +33,8 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.addHistory -import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.getHistory +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.addHistory +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getHistory import net.thauvin.erik.mobibot.entries.EntriesMgr import java.io.File diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 2999272..de8d58b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.commands.links.UrlMgr +import net.thauvin.erik.mobibot.commands.links.LinksMgr import java.util.* class Ignore(bot: Mobibot) : AbstractCommand(bot) { @@ -150,7 +150,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { override fun setProperty(key: String, value: String) { super.setProperty(key, value) if (IGNORE_PROP == key) { - ignored.addAll(value.split(UrlMgr.LINK_MATCH.toRegex())) + ignored.addAll(value.split(LinksMgr.LINK_MATCH.toRegex())) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt index fc6e546..1698028 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.commands.links.UrlMgr +import net.thauvin.erik.mobibot.commands.links.LinksMgr import java.lang.management.ManagementFactory class Info(bot: Mobibot) : AbstractCommand(bot) { @@ -63,7 +63,7 @@ class Info(bot: Mobibot) : AbstractCommand(bot) { with(info) { append(Utils.uptime(ManagementFactory.getRuntimeMXBean().uptime)) append(" [Entries: ") - append(UrlMgr.entriesCount) + append(LinksMgr.entriesCount) if (isOp) { if (bot.tell.isEnabled()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 2ecba68..63f5ffa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -68,8 +68,8 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { val cmds = args.substring(1).split("[.:]".toRegex(), 3) val index = cmds[0].toInt() - 1 - if (index < UrlMgr.entriesCount) { - val entry: EntryLink = UrlMgr.getEntry(index) + if (index < LinksMgr.entriesCount) { + val entry: EntryLink = LinksMgr.getEntry(index) val commentIndex = cmds[1].toInt() - 1 if (commentIndex < entry.commentsCount) { when (val cmd = cmds[2].trim()) { @@ -124,7 +124,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { val comment = entry.getComment(commentIndex) comment.nick = cmd.substring(1) bot.send(EntriesUtils.buildComment(index, commentIndex, comment)) - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } else { bot.send(sender, "Please ask a channel op to change the author of this comment for you.", false) } @@ -141,7 +141,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { if (isOp || sender == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) bot.send("Comment ${Constants.LINK_CMD}${index + 1}.${commentIndex + 1} removed.") - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } else { bot.send(sender, "Please ask a channel op to delete this comment for you.", false) } @@ -151,7 +151,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { entry.setComment(commentIndex, cmd, sender) val comment = entry.getComment(commentIndex) bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false) - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } private fun showComment(bot: Mobibot, entry: EntryLink, index: Int, commentIndex: Int) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt similarity index 99% rename from src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt rename to src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 5a914a4..f4213d4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/UrlMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -43,7 +43,7 @@ import net.thauvin.erik.mobibot.entries.EntryLink import org.jsoup.Jsoup import java.io.IOException -class UrlMgr(bot: Mobibot) : AbstractCommand(bot) { +class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { private val keywords: MutableList<String> = ArrayList() private val defaultTags: MutableList<String> = ArrayList() diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 42e7e4d..26a74ce 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -67,7 +67,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { val cmds = args.substring(1).split(":", limit = 2) val index = cmds[0].toInt() - 1 - if (index < UrlMgr.entriesCount) { + if (index < LinksMgr.entriesCount) { when (val cmd = cmds[1].trim()) { "" -> showEntry(index) "-" -> removeEntry(sender, login, isOp, index) // L1:- @@ -88,33 +88,33 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { } private fun addComment(cmd: String, sender: String, index: Int) { - val entry: EntryLink = UrlMgr.getEntry(index) + val entry: EntryLink = LinksMgr.getEntry(index) val commentIndex = entry.addComment(cmd, sender) val comment = entry.getComment(commentIndex) bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false) - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } private fun changeTitle(cmd: String, index: Int) { if (cmd.length > 1) { - val entry: EntryLink = UrlMgr.getEntry(index) + val entry: EntryLink = LinksMgr.getEntry(index) entry.title = cmd.substring(1).trim() bot.updatePin(entry.link, entry) bot.send(EntriesUtils.buildLink(index, entry)) - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } } private fun changeUrl(cmd: String, login: String, isOp: Boolean, index: Int) { - val entry: EntryLink = UrlMgr.getEntry(index) + val entry: EntryLink = LinksMgr.getEntry(index) if (entry.login == login || isOp) { val link = cmd.substring(1) - if (link.matches(UrlMgr.LINK_MATCH.toRegex())) { + if (link.matches(LinksMgr.LINK_MATCH.toRegex())) { val oldLink = entry.link entry.link = link bot.updatePin(oldLink, entry) bot.send(EntriesUtils.buildLink(index, entry)) - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } } } @@ -122,10 +122,10 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { private fun changeAuthor(cmd: String, sender: String, isOp: Boolean, index: Int) { if (isOp) { if (cmd.length > 1) { - val entry: EntryLink = UrlMgr.getEntry(index) + val entry: EntryLink = LinksMgr.getEntry(index) entry.nick = cmd.substring(1) bot.send(EntriesUtils.buildLink(index, entry)) - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } } else { bot.send(sender, "Please ask a channel op to change the author of this link for you.", false) @@ -133,19 +133,19 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { } private fun removeEntry(sender: String, login: String, isOp: Boolean, index: Int) { - val entry: EntryLink = UrlMgr.getEntry(index) + val entry: EntryLink = LinksMgr.getEntry(index) if (entry.login == login || isOp) { bot.deletePin(index, entry) - UrlMgr.removeEntry(index) + LinksMgr.removeEntry(index) bot.send("Entry ${Constants.LINK_CMD}${index + 1} removed.") - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } else { bot.send(sender, "Please ask a channel op to remove this entry for you.", false) } } private fun showEntry(index: Int) { - val entry: EntryLink = UrlMgr.getEntry(index) + val entry: EntryLink = LinksMgr.getEntry(index) bot.send(EntriesUtils.buildLink(index, entry)) if (entry.hasTags()) { bot.send(EntriesUtils.buildTags(index, entry)) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index 511ca46..a313b01 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -63,15 +63,15 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { val cmds = args.substring(1).split("T:", limit = 2) val index = cmds[0].toInt() - 1 - if (index < UrlMgr.entriesCount) { + if (index < LinksMgr.entriesCount) { val cmd = cmds[1].trim() - val entry: EntryLink = UrlMgr.getEntry(index) + val entry: EntryLink = LinksMgr.getEntry(index) if (cmd.isNotEmpty()) { if (entry.login == login || isOp) { entry.setTags(cmd) bot.updatePin(entry.link, entry) bot.send(EntriesUtils.buildTags(index, entry)) - UrlMgr.saveEntries(bot, false) + LinksMgr.saveEntries(bot, false) } else { bot.send(sender, "Please ask a channel op to change the tags for you.",isPrivate) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index 329b190..98dd423 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -35,8 +35,8 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.entriesCount -import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.getEntry +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entriesCount +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getEntry import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java index aaa09cf..ba9a3f5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java @@ -35,7 +35,7 @@ package net.thauvin.erik.mobibot.entries; import com.rometools.rome.feed.synd.SyndCategory; import com.rometools.rome.feed.synd.SyndCategoryImpl; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.commands.links.UrlMgr; +import net.thauvin.erik.mobibot.commands.links.LinksMgr; import org.apache.commons.lang3.StringUtils; import java.io.Serializable; @@ -347,7 +347,7 @@ public class EntryLink implements Serializable { * @param tags The space-delimited tags. */ public final void setTags(final String tags) { - setTags(Arrays.asList(tags.split(UrlMgr.TAG_MATCH))); + setTags(Arrays.asList(tags.split(LinksMgr.TAG_MATCH))); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 5f47ef9..7264694 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -38,7 +38,7 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.ReleaseInfo; import net.thauvin.erik.mobibot.TwitterTimer; import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.commands.links.UrlMgr; +import net.thauvin.erik.mobibot.commands.links.LinksMgr; import net.thauvin.erik.mobibot.entries.EntryLink; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; @@ -221,8 +221,8 @@ public final class Twitter extends ThreadedModule { */ @SuppressFBWarnings("SUI_CONTAINS_BEFORE_REMOVE") public final void postEntry(final int index) { - if (isAutoPost() && hasEntry(index) && UrlMgr.getEntriesCount() >= index) { - final EntryLink entry = UrlMgr.getEntry(index); + if (isAutoPost() && hasEntry(index) && LinksMgr.getEntriesCount() >= index) { + final EntryLink entry = LinksMgr.getEntry(index); final String msg = entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + bot.getChannel(); new Thread(() -> { From b55bfb45b7728a8d2edebd05d3795e9a00e0805b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 30 Apr 2020 18:18:42 -0700 Subject: [PATCH 400/842] Updated dependencies. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 8 ++++---- .idea/modules/mobibot.test.iml | 8 ++++---- build.gradle | 8 ++------ .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 8c4120d..f17fbd0 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+349" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+359" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 13a7f5b..a1e4f5f 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+349" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+359" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0/cfd127ae6de4862daa93e15ceae9291108eaabc5/okhttp-4.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.5.0/b6b52caca243e75a8fbb95ff6ec9de6b2bdb1481/okio-jvm-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -60,7 +60,7 @@ <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.6.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> @@ -73,7 +73,7 @@ <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.6.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index ece1a8b..e4032fa 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+349" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+359" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.5.0/cfd127ae6de4862daa93e15ceae9291108eaabc5/okhttp-4.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.5.0/b6b52caca243e75a8fbb95ff6ec9de6b2bdb1481/okio-jvm-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -60,7 +60,7 @@ <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.6.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> @@ -75,7 +75,7 @@ <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.6.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> diff --git a/build.gradle b/build.gradle index 0a7ea7a..f38e8d5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.28.0' - id 'com.github.spotbugs' version '4.0.7' + id 'com.github.spotbugs' version '4.0.8' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.8.0' id 'jacoco' @@ -48,7 +48,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.6' - implementation 'com.squareup.okhttp3:okhttp:4.5.0' + implementation 'com.squareup.okhttp3:okhttp:4.6.0' implementation 'com.rometools:rome:1.12.2' @@ -115,10 +115,6 @@ detekt { baseline = file("detekt-baseline.xml") } -tasks.withType(io.gitlab.arturbosch.detekt.Detekt) { - exclude("**/**.java") -} - tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 686c1a4..e70a3aa 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1588132687770L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1588283857809L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "348"; - public static final String VERSION = "0.8.0-alpha+348"; + public static final String BUILDMETA = "357"; + public static final String VERSION = "0.8.0-alpha+357"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 46bdf8b..4cd9b26 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Apr 28 20:58:04 PDT 2020 -version.buildmeta=348 +#Thu Apr 30 15:03:19 PDT 2020 +version.buildmeta=359 version.major=0 version.minor=8 version.patch=0 version.prerelease=alpha version.project=mobibot -version.semver=0.8.0-alpha+348 +version.semver=0.8.0-alpha+359 From eecc2f093f955094c5a3c5be6a06da33ce11df3f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 1 May 2020 22:57:17 -0700 Subject: [PATCH 401/842] Fixed bug in setProperty where the wrong constant was used. --- .../java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index f4213d4..6db470a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -237,7 +237,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { super.setProperty(key, value) if (KEYWORDS_PROP == key) { keywords.addAll(value.split(TAG_MATCH.toRegex())) - } else if (KEYWORDS_PROP == key) { + } else if (TAGS_PROP == key) { defaultTags.addAll(value.split(TAG_MATCH.toRegex())) } } From 2461504888f95b437248dc7a6562b894ee72fe3a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 1 May 2020 22:57:51 -0700 Subject: [PATCH 402/842] Updated Gradle. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index f17fbd0..d60f499 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+359" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+360" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index a1e4f5f..752d102 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+359" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+360" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index e4032fa..0003454 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+359" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+360" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index de4250e..f1cd993 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index e70a3aa..a3567a6 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1588283857809L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1588392356654L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "357"; - public static final String VERSION = "0.8.0-alpha+357"; + public static final String BUILDMETA = "360"; + public static final String VERSION = "0.8.0-alpha+360"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 4cd9b26..b80a0ac 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Apr 30 15:03:19 PDT 2020 -version.buildmeta=359 +#Fri May 01 21:48:04 PDT 2020 +version.buildmeta=361 version.major=0 version.minor=8 version.patch=0 version.prerelease=alpha version.project=mobibot -version.semver=0.8.0-alpha+359 +version.semver=0.8.0-alpha+361 From 6c394c373dde86605ff639efb2e0b89d18a851e5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 10 May 2020 21:25:06 -0700 Subject: [PATCH 403/842] Fixed missing property initialization. --- .../java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index b97f260..1211fcb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -50,6 +50,10 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { const val FEED_PROP = "feed" } + init { + initProperties(FEED_PROP) + } + override fun commandResponse( sender: String, login: String, From ea21cd53832ce45a9192eaff06ae18b2e72476d1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 19 May 2020 00:00:02 -0700 Subject: [PATCH 404/842] Updated dependencies. --- .idea/compiler.xml | 15 +++------------ .idea/jarRepositories.xml | 5 +++++ .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 6 +++--- .idea/modules/mobibot.test.iml | 6 +++--- build.gradle | 12 ++++++------ gradle/wrapper/gradle-wrapper.properties | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 9 files changed, 28 insertions(+), 32 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index a87304a..121136f 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -4,19 +4,10 @@ <annotationProcessing> <profile name="Gradle Imported" enabled="true"> <outputRelativeToContentRoot value="true" /> - <option name="semver.project.dir" value="K:\java\mobibot" /> + <option name="semver.project.dir" value="$PROJECT_DIR$" /> <processorPath useClasspath="false"> - <entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" /> - <entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" /> - </processorPath> - <module name="mobibot.main" /> - </profile> - <profile name="Gradle Imported" enabled="true"> - <outputRelativeToContentRoot value="true" /> - <option name="semver.project.dir" value="K:\java\mobibot" /> - <processorPath useClasspath="false"> - <entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" /> - <entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" /> + <entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/1.2.0/1009ce8f284d9d9e839aae2d7a6e0bf2a2b401bf/semver-1.2.0.jar" /> + <entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.6/1b8707299c34406ed0ba40bbf8513352ac4765c9/compiler-0.9.6.jar" /> </processorPath> <module name="mobibot.main" /> </profile> diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index d32dd97..bd33cec 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -26,5 +26,10 @@ <option name="name" value="BintrayJCenter" /> <option name="url" value="https://jcenter.bintray.com/" /> </remote-repository> + <remote-repository> + <option name="id" value="MavenLocal" /> + <option name="name" value="MavenLocal" /> + <option name="url" value="file:$MAVEN_REPOSITORY$" /> + </remote-repository> </component> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index d60f499..b8a9c15 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+360" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+361" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 752d102..a5171c7 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+360" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+361" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/1.2.0/1009ce8f284d9d9e839aae2d7a6e0bf2a2b401bf/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/pinboard-poster/1.0.1/56296a0293d621e99c479d26760a7aa345d237e7/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -26,7 +26,7 @@ <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> - <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> </array> </option> <option name="errors"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 0003454..1e28242 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+360" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+361" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main;K:/java/mobibot/build/classes/kotlin/main;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar;K:/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/pinboard-poster/1.0.1/56296a0293d621e99c479d26760a7aa345d237e7/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -31,7 +31,7 @@ <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> - <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> </array> </option> <option name="errors"> diff --git a/build.gradle b/build.gradle index f38e8d5..10d51ca 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,9 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.28.0' - id 'com.github.spotbugs' version '4.0.8' + id 'com.github.spotbugs' version '4.2.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.8.0' + id 'io.gitlab.arturbosch.detekt' version '1.9.1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -22,8 +22,8 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j : '2.13.2', - spotbugs: '4.0.2' + log4j : '2.13.3', + spotbugs: '4.0.3' ] repositories { @@ -48,7 +48,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.6' - implementation 'com.squareup.okhttp3:okhttp:4.6.0' + implementation 'com.squareup.okhttp3:okhttp:4.7.1' implementation 'com.rometools:rome:1.12.2' @@ -65,7 +65,7 @@ dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib' testImplementation 'org.testng:testng:7.2.0' - testImplementation 'org.assertj:assertj-core:3.15.0' + testImplementation 'org.assertj:assertj-core:3.16.1' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f1cd993..a4f0001 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index a3567a6..6d8bae9 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1588392356654L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1589170478622L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "360"; - public static final String VERSION = "0.8.0-alpha+360"; + public static final String BUILDMETA = "367"; + public static final String VERSION = "0.8.0-alpha+367"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index b80a0ac..46a0dc2 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri May 01 21:48:04 PDT 2020 -version.buildmeta=361 +#Sun May 10 21:14:35 PDT 2020 +version.buildmeta=367 version.major=0 version.minor=8 version.patch=0 version.prerelease=alpha version.project=mobibot -version.semver=0.8.0-alpha+361 +version.semver=0.8.0-alpha+367 From d843c65f6f3e4387c56608b0503d640a8f0aac8a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 13 Jun 2020 02:04:04 -0700 Subject: [PATCH 405/842] Updated dependencies. --- .idea/compiler.xml | 4 ++-- .idea/jarRepositories.xml | 5 +++++ .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 18 ++++++++--------- .idea/modules/mobibot.test.iml | 20 +++++++++---------- build.gradle | 14 ++++++------- config/pmd.xml | 3 +-- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 8 ++++---- 9 files changed, 42 insertions(+), 38 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 121136f..5a0b3ad 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -6,8 +6,8 @@ <outputRelativeToContentRoot value="true" /> <option name="semver.project.dir" value="$PROJECT_DIR$" /> <processorPath useClasspath="false"> - <entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/1.2.0/1009ce8f284d9d9e839aae2d7a6e0bf2a2b401bf/semver-1.2.0.jar" /> - <entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.spullara.mustache.java/compiler/0.9.6/1b8707299c34406ed0ba40bbf8513352ac4765c9/compiler-0.9.6.jar" /> + <entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" /> + <entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" /> </processorPath> <module name="mobibot.main" /> </profile> diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index bd33cec..29dc1da 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -31,5 +31,10 @@ <option name="name" value="MavenLocal" /> <option name="url" value="file:$MAVEN_REPOSITORY$" /> </remote-repository> + <remote-repository> + <option name="id" value="MavenLocal" /> + <option name="name" value="MavenLocal" /> + <option name="url" value="file:$MAVEN_REPOSITORY$/" /> + </remote-repository> </component> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index b8a9c15..d08914c 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+361" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+367" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index a5171c7..a48ef5b 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+361" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+367" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/semver/1.2.0/1009ce8f284d9d9e839aae2d7a6e0bf2a2b401bf/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/pinboard-poster/1.0.1/56296a0293d621e99c479d26760a7aa345d237e7/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.3/755cc5d84d32b31beeaf8597181f0fc4eac98e16/spotbugs-annotations-4.0.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -26,7 +26,7 @@ <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> + <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> </option> <option name="errors"> @@ -51,18 +51,18 @@ <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.3" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.6.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.7.2" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 1e28242..698e09a 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+361" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+367" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.2/8eb1fc1914eb2550bf3ddea26917c9a7cbb00593/log4j-core-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.2/49df25f7a35dd7fbd8131fc5ab09665d18e3d4fe/log4j-slf4j-impl-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.2/567ea514dedd8679c429c5b5b39b0d67b6464c3c/log4j-api-2.13.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.thauvin.erik/pinboard-poster/1.0.1/56296a0293d621e99c479d26760a7aa345d237e7/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.6.0/1885ec944e0a74e78359756e88ce656fd5a596d5/okhttp-4.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.2/e203eecc94365a6885f262d0e4c99ec059980d2e/spotbugs-annotations-4.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.15.0/b5b633545f357f576bd0661b302914a3951319d/assertj-core-3.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jdom/jdom2/2.0.6/6f14738ec2e9dd0011e343717fa624a10f8aab64/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.3/755cc5d84d32b31beeaf8597181f0fc4eac98e16/spotbugs-annotations-4.0.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -31,7 +31,7 @@ <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> + <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> </option> <option name="errors"> @@ -52,24 +52,24 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="mobibot.main" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.6.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.7.2" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.3" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> - <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.15.0" level="project" /> + <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.16.1" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> diff --git a/build.gradle b/build.gradle index 10d51ca..05cf95e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,14 +2,14 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.28.0' - id 'com.github.spotbugs' version '4.2.0' + id 'com.github.spotbugs' version '4.3.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.9.1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.3.72' - id 'org.sonarqube' version '2.8' + id 'org.sonarqube' version '3.0' id 'pmd' } @@ -23,7 +23,7 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j : '2.13.3', - spotbugs: '4.0.3' + spotbugs: '4.0.4' ] repositories { @@ -48,11 +48,11 @@ dependencies { implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.6' - implementation 'com.squareup.okhttp3:okhttp:4.7.1' + implementation 'com.squareup.okhttp3:okhttp:4.7.2' - implementation 'com.rometools:rome:1.12.2' + implementation 'com.rometools:rome:1.13.1' - implementation 'org.json:json:20190722' + implementation 'org.json:json:20200518' implementation 'org.jsoup:jsoup:1.13.1' implementation 'net.objecthunter:exp4j:0.4.8' @@ -104,7 +104,7 @@ tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { } pmd { - toolVersion = '6.23.0' + toolVersion = '6.24.0' ignoreFailures = true ruleSets = [] ruleSetFiles = files("${projectDir}/config/pmd.xml") diff --git a/config/pmd.xml b/config/pmd.xml index 65fe3ec..1233156 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -20,8 +20,7 @@ value="./MethodDeclarator[@Image='hashCode' or @Image='equals' or @Image='toString']"/> </properties> </rule> - <rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInCaseInsensitiveComparisons"/> - <rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInComparisons"/> + <rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons"/> <rule ref="category/java/bestpractices.xml/PreserveStackTrace"/> <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/> <!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 6d8bae9..f7959aa 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1589170478622L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1592038362460L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "367"; - public static final String VERSION = "0.8.0-alpha+367"; + public static final String BUILDMETA = "370"; + public static final String VERSION = "0.8.0-alpha+370"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index 46a0dc2..e16403d 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun May 10 21:14:35 PDT 2020 -version.buildmeta=367 +#Sat Jun 13 02:01:11 PDT 2020 +version.buildmeta=001 version.major=0 version.minor=8 version.patch=0 -version.prerelease=alpha +version.prerelease=beta version.project=mobibot -version.semver=0.8.0-alpha+367 +version.semver=0.8.0-beta+001 From d6a85ef4af145ea1aecd48ff6967707e4501be86 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 13 Jun 2020 03:27:50 -0700 Subject: [PATCH 406/842] Cleaned up msg. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 10 ++-- .idea/modules/mobibot.test.iml | 10 ++-- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 8 ++-- .../net/thauvin/erik/mobibot/Mobibot.java | 2 +- .../thauvin/erik/mobibot/modules/Joke.java | 2 +- .../thauvin/erik/mobibot/modules/Twitter.java | 2 +- .../erik/mobibot/modules/WorldTime.java | 6 +-- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 28 ++--------- .../net/thauvin/erik/mobibot/msg/Message.kt | 48 ++++++------------- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 24 ++-------- .../erik/mobibot/msg/PrivateMessage.kt | 22 ++------- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 22 ++------- .../modules/CurrencyConverterTest.java | 6 +-- .../mobibot/modules/GoogleSearchTest.java | 4 +- .../erik/mobibot/modules/JokeTest.java | 4 +- .../erik/mobibot/modules/StockQuoteTest.java | 2 +- .../erik/mobibot/modules/TwitterTest.java | 2 +- .../erik/mobibot/modules/Weather2Test.java | 8 ++-- .../erik/mobibot/modules/WordTimeTest.java | 4 +- version.properties | 6 +-- 21 files changed, 68 insertions(+), 154 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index d08914c..65c3f72 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+367" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+001" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index a48ef5b..e420b4e 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+367" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+001" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.3/755cc5d84d32b31beeaf8597181f0fc4eac98e16/spotbugs-annotations-4.0.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -51,7 +51,7 @@ <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.4" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> @@ -61,7 +61,7 @@ <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.7.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> @@ -74,7 +74,7 @@ <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.6.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 698e09a..6376829 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+367" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+001" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.3/755cc5d84d32b31beeaf8597181f0fc4eac98e16/spotbugs-annotations-4.0.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -61,13 +61,13 @@ <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.7.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.3" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.4" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.16.1" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> @@ -76,7 +76,7 @@ <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.6.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index f7959aa..06bb921 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1592038362460L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1592043035449L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String PRERELEASE = "alpha"; - public static final String BUILDMETA = "370"; - public static final String VERSION = "0.8.0-alpha+370"; + public static final String PRERELEASE = "beta"; + public static final String BUILDMETA = "004"; + public static final String VERSION = "0.8.0-beta+004"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 16d09b9..945ad1f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -850,7 +850,7 @@ public class Mobibot extends PircBot { * @param message The message. */ public final void send(final String who, final Message message) { - send(message.isNotice() ? who : getChannel(), message.getText(), message.getColor(), message.isPrivate()); + send(message.isNotice() ? who : getChannel(), message.getMsg(), message.getColor(), message.isPrivate()); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java index 5c1d858..23ed888 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java @@ -101,7 +101,7 @@ public final class Joke extends ThreadedModule { @Override void run(final String sender, final String cmd, final String arg, final boolean isPrivate) { try { - bot.send(Utils.cyan(randomJoke().getText())); + bot.send(Utils.cyan(randomJoke().getMsg())); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); bot.send(sender, e.getMessage(), isPrivate); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 7264694..7728a9f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -258,7 +258,7 @@ public final class Twitter extends ThreadedModule { void run(final String sender, final String cmd, final String message, final boolean isPrivate) { try { bot.send(sender, - post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getText(), + post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMsg(), isPrivate); } catch (ModuleException e) { bot.getLogger().warn(e.getDebugMessage(), e); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java index 48092d8..05bcc22 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java @@ -226,12 +226,12 @@ public final class WorldTime extends AbstractModule { } else { final Message msg = worldTime(args); if (isPrivate) { - bot.send(sender, msg.getText(), true); + bot.send(sender, msg.getMsg(), true); } else { if (msg.isError()) { - bot.send(sender, msg.getText(), false); + bot.send(sender, msg.getMsg(), false); } else { - bot.send(msg.getText()); + bot.send(msg.getMsg()); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 6c00add..b353fe8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -38,29 +38,11 @@ package net.thauvin.erik.mobibot.msg * @created 2019-04-07 * @since 1.0 */ -class ErrorMessage : Message { - /** - * Creates a new error message. - * - * @param text The error message. - */ - constructor(text: String) : super() { - this.text = text - isError = true - isNotice = true - } - - /** - * Creates a new error message. - * - * @param text The message. - * @param color The message color. - */ - @Suppress("unused") - constructor(text: String, color: String) : super() { - this.text = text - isError = true - isNotice = true +class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { + init { + this.msg = msg this.color = color + isError = true + isNotice = true } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt index d95e792..d3fe758 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt @@ -41,8 +41,12 @@ import org.jibble.pircbot.Colors * @since 1.0 */ open class Message { + companion object { + var DEFAULT_COLOR = Colors.NORMAL + } + /** Color */ - var color = Colors.NORMAL + var color = DEFAULT_COLOR /** Error */ var isError = false @@ -54,7 +58,7 @@ open class Message { var isPrivate = false /** Message text*/ - var text = "" + var msg = "" /** Creates a new message. */ constructor() { @@ -64,42 +68,18 @@ open class Message { /** * Creates a new message. * - * @param text The message. - * @param isNotice The notice flag. - * @param isError The error flag. - * @param isPrivate The Private message - */ - constructor(text: String, isNotice: Boolean, isError: Boolean, isPrivate: Boolean) { - this.text = text - this.isNotice = isNotice - this.isError = isError - this.isPrivate = isPrivate - } - - /** - * Creates a new message. - * - * @param text The message. - * @param isNotice The notice flag. - * @param isError The error flag. - * @param isPrivate The Private message + * @param msg The message. * @param color The color. + * @param isNotice The notice flag. + * @param isError The error flag. + * @param isPrivate The private flag. */ - constructor( - text: String, - isNotice: Boolean, - isError: Boolean, - isPrivate: Boolean, - color: String - ) { - this.text = text + @JvmOverloads + constructor(msg: String, color: String = DEFAULT_COLOR, isNotice: Boolean, isError: Boolean, isPrivate: Boolean) { + this.msg = msg + this.color = color this.isNotice = isNotice this.isError = isError this.isPrivate = isPrivate - this.color = color - } - - override fun toString(): String { - return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, message='$text')" } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index b7c0ad3..3286d60 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -38,26 +38,10 @@ package net.thauvin.erik.mobibot.msg * @created 2019-04-07 * @since 1.0 */ -class NoticeMessage : Message { - /** - * Creates a new notice. - * - * @param text The notice's message. - */ - constructor(text: String) : super() { - this.text = text - isNotice = true - } - - /** - * Create a new notice. - * - * @param text The notice's message. - * @param color The color. - */ - constructor(text: String, color: String) : super() { - this.text = text - isNotice = true +class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { + init { + this.msg = msg this.color = color + isNotice = true } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 4f9d4e9..8eb4281 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -39,25 +39,9 @@ package net.thauvin.erik.mobibot.msg * @since 1.0 */ @Suppress("unused") -class PrivateMessage : Message { - /** - * Creates a new private message. - * - * @param text The message. - */ - constructor(text: String) : super() { - this.text = text - isPrivate = true - } - - /** - * Creates a new private message. - * - * @param text The message. - * @param color The message color. - */ - constructor(text: String, color: String) : super() { - this.text = text +class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { + init { + this.msg = msg this.color = color isPrivate = true } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 2bd991b..3d2ac0f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -38,25 +38,9 @@ package net.thauvin.erik.mobibot.msg * @created 2019-04-07 * @since 1.0 */ -class PublicMessage : Message { - /** - * Creates a new public message. - * - * @param text The message. - */ - constructor(text: String) : super() { - this.text = text - } - - /** - * Creates a new public message. - * - * @param text The message. - * @param color The message color. - */ - @Suppress("unused") - constructor(text: String, color: String) : super() { - this.text = text +class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { + init { + this.msg = msg this.color = color } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java index f1b410a..b67fd6f 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java @@ -54,11 +54,11 @@ public class CurrencyConverterTest { @Test @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") public void testConvertCurrency() { - assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getText()) + assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMsg()) .as("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR"); - assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getText()) + assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getMsg()) .as("100 USD to USD").contains("You're kidding, right?"); - assertThat(CurrencyConverter.convertCurrency("100 USD").getText()) + assertThat(CurrencyConverter.convertCurrency("100 USD").getMsg()) .as("100 USD").contains("Invalid query."); assertThat(CurrencyConverter.currencyRates().size()) .as("currencyRates().size() == 33").isEqualTo(33); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 0d8a5f9..7633f84 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -63,11 +63,11 @@ public class GoogleSearchTest extends LocalProperties { try { List<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey); assertThat(messages).as("mobibot results not empty").isNotEmpty(); - assertThat(messages.get(0).getText()).as("found mobitopia").contains("mobibot"); + assertThat(messages.get(0).getMsg()).as("found mobitopia").contains("mobibot"); messages = GoogleSearch.searchGoogle("aapl", apiKey, cseKey); assertThat(messages).as("aapl results not empty").isNotEmpty(); - assertThat(messages.get(0).getText()).as("found apple").containsIgnoringCase("apple"); + assertThat(messages.get(0).getMsg()).as("found apple").containsIgnoringCase("apple"); assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "", "apiKey")).as("no API key").isInstanceOf( ModuleException.class).hasNoCause(); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java index df6ddc7..9880241 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java @@ -51,7 +51,7 @@ public class JokeTest { @Test public void testRamdomJoke() throws ModuleException { - assertThat(Joke.randomJoke().getText().length() > 0).as("randomJoke() > 0").isTrue(); - assertThat(Joke.randomJoke().getText()).as("randomJoke()").containsIgnoringCase("chuck"); + assertThat(Joke.randomJoke().getMsg().length() > 0).as("randomJoke() > 0").isTrue(); + assertThat(Joke.randomJoke().getMsg()).as("randomJoke()").containsIgnoringCase("chuck"); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 2eec2a0..715fe64 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -58,7 +58,7 @@ public class StockQuoteTest extends LocalProperties { try { final List<Message> messages = StockQuote.getQuote("apple inc", apiKey); assertThat(messages).as("response not empty").isNotEmpty(); - assertThat(messages.get(0).getText()).as("same stock symbol").contains("AAPL").contains("Apple Inc."); + assertThat(messages.get(0).getMsg()).as("same stock symbol").contains("AAPL").contains("Apple Inc."); try { StockQuote.getQuote("blahfoo", apiKey); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java index 04cf36c..11d4123 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -73,6 +73,6 @@ public class TwitterTest extends LocalProperties { getProperty(Twitter.TOKEN_SECRET_PROP), getProperty(Twitter.HANDLE_PROP), msg, - true).getText()).as("twitterPost(" + msg + ')').isEqualTo(msg); + true).getMsg()).as("twitterPost(" + msg + ')').isEqualTo(msg); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index 0c1309e..ffd5c60 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -54,12 +54,12 @@ public class Weather2Test extends LocalProperties { @Test public void testWeather() throws ModuleException { List<Message> messages = Weather2.getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getText()).as("is Everett").contains("Everett").contains("US"); - assertThat(messages.get(messages.size() - 1).getText()).as("is City Search").endsWith("98204%2CUS"); + assertThat(messages.get(0).getMsg()).as("is Everett").contains("Everett").contains("US"); + assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Search").endsWith("98204%2CUS"); messages = Weather2.getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getText()).as("is UK").contains("London").contains("UK"); - assertThat(messages.get(messages.size() - 1).getText()).as("is City Code").endsWith("4517009"); + assertThat(messages.get(0).getMsg()).as("is UK").contains("London").contains("UK"); + assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Code").endsWith("4517009"); assertThatThrownBy(() -> Weather2.getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP))) .as("Montpellier not found").hasCauseInstanceOf(APIException.class); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java index 9c7299a..5f862ca 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java @@ -47,9 +47,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class WordTimeTest { @Test public void testWorldTime() { - assertThat(WorldTime.worldTime("PST").getText()).as("PST").endsWith(Utils.bold("Los Angeles")); + assertThat(WorldTime.worldTime("PST").getMsg()).as("PST").endsWith(Utils.bold("Los Angeles")); assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue(); - assertThat(WorldTime.worldTime("BEATS").getText()).as("BEATS").contains("@"); + assertThat(WorldTime.worldTime("BEATS").getMsg()).as("BEATS").contains("@"); } @Test diff --git a/version.properties b/version.properties index e16403d..3a1fdac 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jun 13 02:01:11 PDT 2020 -version.buildmeta=001 +#Sat Jun 13 03:10:34 PDT 2020 +version.buildmeta=004 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+001 +version.semver=0.8.0-beta+004 From b729eb272900a85c9ba813b13920ecdad4c5ad53 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Jun 2020 14:19:23 -0700 Subject: [PATCH 407/842] Moved Pinboard to kotlin and added tests. --- detekt-baseline.xml | 12 +- .../net/thauvin/erik/mobibot/Mobibot.java | 18 +- .../net/thauvin/erik/mobibot/Pinboard.java | 167 ------------------ .../net/thauvin/erik/mobibot/PinboardUtils.kt | 141 +++++++++++++++ .../{modules => }/LocalProperties.java | 8 +- .../thauvin/erik/mobibot/PinboardUtilsTest.kt | 86 +++++++++ .../mobibot/modules/GoogleSearchTest.java | 1 + .../erik/mobibot/modules/StockQuoteTest.java | 1 + .../erik/mobibot/modules/TwitterTest.java | 1 + .../erik/mobibot/modules/Weather2Test.java | 1 + 10 files changed, 254 insertions(+), 182 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/Pinboard.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt rename src/test/java/net/thauvin/erik/mobibot/{modules => }/LocalProperties.java (93%) create mode 100644 src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt diff --git a/detekt-baseline.xml b/detekt-baseline.xml index d61a8d8..93d9e37 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -1,7 +1,7 @@ <?xml version="1.0" ?> <SmellBaseline> - <Blacklist></Blacklist> - <Whitelist> + <ManuallySuppressedIssues></ManuallySuppressedIssues> + <CurrentIssues> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> @@ -12,8 +12,8 @@ <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> <ID>MagicNumber:Users.kt$Users$8</ID> <ID>MagicNumber:View.kt$View$8</ID> - <ID>NestedBlockDepth:Addons.kt$Addons$add</ID> - <ID>NestedBlockDepth:Comment.kt$Comment$commandResponse</ID> - <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$commandResponse</ID> - </Whitelist> + <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> + <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + </CurrentIssues> </SmellBaseline> diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 945ad1f..df34caa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -70,6 +70,7 @@ import net.thauvin.erik.mobibot.modules.War; import net.thauvin.erik.mobibot.modules.Weather2; import net.thauvin.erik.mobibot.modules.WorldTime; import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.pinboard.PinboardPoster; import net.thauvin.erik.semver.Version; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -96,6 +97,7 @@ import java.nio.file.Paths; import java.util.List; import java.util.Properties; import java.util.Timer; +import java.util.logging.ConsoleHandler; import java.util.regex.Pattern; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -154,7 +156,7 @@ public class Mobibot extends PircBot { // NickServ ident password private String identPwd = ""; // Pinboard posts handler - private Pinboard pinboard; + private PinboardPoster pinboard; // Weblog URL private String weblogUrl = ""; @@ -383,7 +385,7 @@ public class Mobibot extends PircBot { */ public final void addPin(final EntryLink entry) { if (pinboard != null) { - pinboard.addPost(entry); + PinboardUtils.addPin(pinboard, ircServer, entry); } } @@ -423,7 +425,7 @@ public class Mobibot extends PircBot { */ public final void deletePin(final int index, final EntryLink entry) { if (pinboard != null) { - pinboard.deletePost(entry); + PinboardUtils.deletePin(pinboard, entry); } if (twitter.isAutoPost()) { twitter.removeEntry(index); @@ -914,7 +916,13 @@ public class Mobibot extends PircBot { */ final void setPinboardAuth(final String apiToken) { if (isNotBlank(apiToken)) { - pinboard = new Pinboard(this, apiToken); + pinboard = new PinboardPoster(apiToken); + if (LOGGER.isDebugEnabled()) { + final ConsoleHandler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(java.util.logging.Level.FINE); + pinboard.getLogger().addHandler(consoleHandler); + pinboard.getLogger().setLevel(java.util.logging.Level.FINE); + } } } @@ -948,7 +956,7 @@ public class Mobibot extends PircBot { */ public final void updatePin(final String oldUrl, final EntryLink entry) { if (pinboard != null) { - pinboard.updatePost(oldUrl, entry); + PinboardUtils.updatePin(pinboard, ircServer, oldUrl, entry); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java b/src/main/java/net/thauvin/erik/mobibot/Pinboard.java deleted file mode 100644 index 0291ff3..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/Pinboard.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Pinboard.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot; - -import net.thauvin.erik.mobibot.entries.EntryLink; -import net.thauvin.erik.pinboard.PinboardPoster; - -import javax.swing.SwingWorker; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.logging.ConsoleHandler; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * The class to handle posts to pinboard.in. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2017-05-17 - * @since 1.0 - */ -class Pinboard { - private final String ircServer; - private final PinboardPoster pinboard; - - /** - * Creates a new {@link Pinboard} instance. - * - * @param bot The bot's instance. - * @param apiToken The API end point. - */ - Pinboard(final Mobibot bot, final String apiToken) { - pinboard = new PinboardPoster(apiToken); - ircServer = bot.getIrcServer(); - - if (bot.getLogger().isDebugEnabled()) { - final ConsoleHandler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.FINE); - final Logger logger = pinboard.getLogger(); - logger.addHandler(consoleHandler); - logger.setLevel(Level.FINE); - } - } - - /** - * Adds a post to pinboard.in. - * - * @param entry The entry to add. - */ - final void addPost(final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { - @Override - protected Boolean doInBackground() { - return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate())); - } - }; - - worker.execute(); - } - - /** - * Deletes a post to pinboard.in. - * - * @param entry The entry to delete. - */ - final void deletePost(final EntryLink entry) { - final String link = entry.getLink(); - - final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { - @Override - protected Boolean doInBackground() { - return pinboard.deletePin(link); - } - }; - - worker.execute(); - } - - /** - * Format a date to a UTC timestamp. - * - * @param date The date. - * @return The date in {@link DateTimeFormatter#ISO_INSTANT} format. - */ - private String formatDate(final Date date) { - return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT); - } - - /** - * Returns he pinboard.in extended attribution line. - * - * @param entry The entry. - * @return The extended attribution line. - */ - private String postedBy(final EntryLink entry) { - return "Posted by " + entry.getNick() + " on " + entry.getChannel() + " (" + ircServer + ')'; - } - - /** - * Updates a post to pinboard.in. - * - * @param oldUrl The old post URL. - * @param entry The entry to add. - */ - final void updatePost(final String oldUrl, final EntryLink entry) { - final SwingWorker<Boolean, Void> worker = new SwingWorker<>() { - @Override - protected Boolean doInBackground() { - if (!oldUrl.equals(entry.getLink())) { - pinboard.deletePin(oldUrl); - - return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate())); - } else { - return pinboard.addPin(entry.getLink(), - entry.getTitle(), - postedBy(entry), - entry.getPinboardTags(), - formatDate(entry.getDate()), - true, - true); - } - } - }; - - worker.execute(); - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt new file mode 100644 index 0000000..b12a85f --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -0,0 +1,141 @@ +/* + * Pinboard.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.pinboard.PinboardPoster +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.util.* + +/** + * The class to handle posts to pinboard.in. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net) + * @created 2017-05-17 + * @since 1.0 + */ +object PinboardUtils { + /** + * Adds a pin. + * + * @param poster The PinboardPoster instance. + * @param entry The entry to add. + */ + @JvmStatic + fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking { + val add = GlobalScope.async { + poster.addPin( + entry.link, + entry.title, + postedBy(entry, ircServer), + entry.pinboardTags, + formatDate(entry.date) + ) + } + add.await() + } + + /** + * Deletes a pin. + * + * @param poster The PinboardPoster instance. + * @param entry The entry to delete. + */ + @JvmStatic + fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking { + val delete = GlobalScope.async { + poster.deletePin(entry.link) + } + delete.await() + } + + /** + * Updates a pin. + * + * @param poster The PinboardPoster instance. + * @param oldUrl The old post URL. + * @param entry The entry to add. + */ + @JvmStatic + fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { + val update = GlobalScope.async { + if (oldUrl != entry.link) { + poster.deletePin(oldUrl) + poster.addPin( + entry.link, + entry.title, + postedBy(entry, ircServer), + entry.pinboardTags, + formatDate(entry.date) + ) + } else { + poster.addPin( + entry.link, + entry.title, + postedBy(entry, ircServer), + entry.pinboardTags, + formatDate(entry.date), + replace = true, + shared = true + ) + } + } + update.await() + } + + /** + * Format a date to a UTC timestamp. + * + * @param date The date. + * @return The date in [DateTimeFormatter.ISO_INSTANT] format. + */ + private fun formatDate(date: Date): String { + return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT) + } + + /** + * Returns he pinboard.in extended attribution line. + * + * @param entry The entry. + * @return The extended attribution line. + */ + private fun postedBy(entry: EntryLink, ircServer: String): String { + return "Posted by ${entry.nick} on ${entry.channel} ( $ircServer )" + } +} + diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.java similarity index 93% rename from src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java rename to src/test/java/net/thauvin/erik/mobibot/LocalProperties.java index acd9dab..1585681 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.java @@ -1,7 +1,7 @@ /* * LocalProperties.java * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,7 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot.modules; +package net.thauvin.erik.mobibot; import org.apache.commons.lang3.StringUtils; import org.testng.annotations.BeforeSuite; @@ -49,10 +49,10 @@ import java.util.Properties; * @created 2019-04-08 * @since 1.0 */ -class LocalProperties { +public class LocalProperties { private static final Properties localProps = new Properties(); - static String getProperty(final String key) { + public static String getProperty(final String key) { if (localProps.containsKey(key)) { return localProps.getProperty(key); } else { diff --git a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt new file mode 100644 index 0000000..a8eed6c --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -0,0 +1,86 @@ +/* + * PinboardTest.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.pinboard.PinboardPoster +import org.testng.Assert +import org.testng.annotations.Test +import java.io.IOException +import java.net.URI +import java.net.URISyntaxException +import java.net.URLEncoder +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse +import java.nio.charset.StandardCharsets + +class PinboardUtilsTest : LocalProperties() { + @Test + @Throws(InterruptedException::class, IOException::class, URISyntaxException::class) + fun pinboardTest() { + val apiToken = getProperty("pinboard-api-token") + val pinboard = PinboardPoster(apiToken) + val url = "https://www.example.com/" + val ircServer = "irc.test.com" + val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) + + PinboardUtils.addPin(pinboard, ircServer, entry) + Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "add") + entry.link = "https://www.foo.com/" + + PinboardUtils.updatePin(pinboard, ircServer, url, entry) + Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "update") + + PinboardUtils.deletePin(pinboard, entry) + Assert.assertFalse(validatePin(apiToken, url = entry.link), "delete") + } + + @Throws(IOException::class, URISyntaxException::class, InterruptedException::class) + private fun validatePin(apiToken: String, ircServer: String = "", url: String): Boolean { + val request = HttpRequest.newBuilder().uri( + URI( + "https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + + URLEncoder.encode(url, StandardCharsets.UTF_8) + ) + ).GET().build() + + val response = HttpClient.newBuilder() + .build() + .send(request, HttpResponse.BodyHandlers.ofString()) + + return if (response.statusCode() == 200) { + response.body().contains(url) && response.body().contains(ircServer) + } else false + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java index 7633f84..ecf0f0f 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.LocalProperties; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java index 715fe64..a8961c6 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.LocalProperties; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java index 11d4123..88782ad 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import net.thauvin.erik.mobibot.LocalProperties; import org.testng.annotations.Test; import java.net.InetAddress; diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java index ffd5c60..6d8c15c 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.modules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.aksingh.owmjapis.api.APIException; +import net.thauvin.erik.mobibot.LocalProperties; import net.thauvin.erik.mobibot.msg.Message; import org.testng.annotations.Test; From dcd6996c391dc49b40f5b93d4951999a3772f8f5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Jun 2020 14:20:33 -0700 Subject: [PATCH 408/842] Updated dependencies, wrapper, etc. --- .idea/compiler.xml | 2 +- .idea/misc.xml | 2 +- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 9 +++++---- .idea/modules/mobibot.test.iml | 10 ++++++++-- build.gradle | 13 ++++++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 5a0b3ad..1f720ec 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -12,6 +12,6 @@ <module name="mobibot.main" /> </profile> </annotationProcessing> - <bytecodeTargetLevel target="1.9" /> + <bytecodeTargetLevel target="11" /> </component> </project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index c88ae8f..1fa7978 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,5 +6,5 @@ <component name="JavaScriptSettings"> <option name="languageLevel" value="ES6" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_9" default="false" project-jdk-name="14" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="14" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 65c3f72..fbe0f57 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+001" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+034" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index e420b4e..366e847 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,18 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+001" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+034" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> - <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> + <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> <compilerSettings> <option name="additionalArguments" value="-Xallow-no-source-files" /> </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> - <option name="jvmTarget" value="9" /> + <option name="jvmTarget" value="11" /> <option name="languageVersion" value="1.3" /> <option name="apiVersion" value="1.3" /> <option name="pluginOptions"> @@ -66,6 +66,7 @@ <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 6376829..03f4493 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+001" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+034" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/3.3.3/4878395d4e63173f3825e17e5e0690e8054445f1/mockito-core-3.3.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.10.5/d39f2a6c7a3550e03fb12a870e0829b0fa87f036/byte-buddy-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.10.5/d1c949ee74c3421ffd3d9159c867777ded928448/byte-buddy-agent-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.6/639033469776fd37c08358c6b92a4761feb2af4b/objenesis-2.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -47,6 +47,7 @@ <content url="file://$MODULE_DIR$/../../src/test"> <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/../../src/test/kotlin" isTestSource="true" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> @@ -66,10 +67,12 @@ <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.4" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.16.1" level="project" /> + <orderEntry type="library" name="Gradle: org.mockito:mockito-core:3.3.3" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> @@ -86,6 +89,9 @@ <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> + <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy:1.10.5" level="project" /> + <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy-agent:1.10.5" level="project" /> + <orderEntry type="library" name="Gradle: org.objenesis:objenesis:2.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> diff --git a/build.gradle b/build.gradle index 05cf95e..0d779ef 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,9 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.28.0' - id 'com.github.spotbugs' version '4.3.0' + id 'com.github.spotbugs' version '4.4.1' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.9.1' + id 'io.gitlab.arturbosch.detekt' version '1.10.0-RC1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -63,15 +63,18 @@ dependencies { implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib' + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7") testImplementation 'org.testng:testng:7.2.0' testImplementation 'org.assertj:assertj-core:3.16.1' + testImplementation 'org.mockito:mockito-core:3.3.3' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" + } test { @@ -81,13 +84,13 @@ test { } java { - sourceCompatibility = JavaVersion.VERSION_1_9 - targetCompatibility = JavaVersion.VERSION_1_9 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } compileKotlin { kotlinOptions { - jvmTarget = '9' + jvmTarget = '11' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4f0001..622ab64 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From c88be2b4e54cebf8dcb3967d9fec8b86d57b1482 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Jun 2020 14:32:29 -0700 Subject: [PATCH 409/842] JDK 11 min requirements because of LTS. --- .circleci/config.yml | 8 +++--- .idea/compiler.xml | 2 +- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 6 ++--- .idea/modules/mobibot.test.iml | 2 +- .travis.yml | 4 +-- build.gradle | 1 - .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 ++--- .../thauvin/erik/mobibot/PinboardUtilsTest.kt | 25 +++---------------- version.properties | 6 ++--- 10 files changed, 22 insertions(+), 40 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2926e9b..6d2102a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,15 +34,15 @@ jobs: <<: *defaults docker: - - image: cimg/openjdk:14.0 + - image: cimg/openjdk:14.0.1 <<: *defaults_gradle - build_gradle_jdk9: + build_gradle_jdk11: <<: *defaults docker: - - image: circleci/openjdk:9 + - image: circleci/openjdk:11.0.7 <<: *defaults_gradle @@ -50,6 +50,6 @@ workflows: version: 2 gradle: jobs: - - build_gradle_jdk9 + - build_gradle_jdk11 - build_gradle_jdk14 diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 1f720ec..5a0b3ad 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -12,6 +12,6 @@ <module name="mobibot.main" /> </profile> </annotationProcessing> - <bytecodeTargetLevel target="11" /> + <bytecodeTargetLevel target="1.9" /> </component> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index fbe0f57..92add17 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+034" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+043" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 366e847..636f6cf 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+034" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+043" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> - <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> + <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> <compilerSettings> <option name="additionalArguments" value="-Xallow-no-source-files" /> </compilerSettings> @@ -12,7 +12,7 @@ <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> - <option name="jvmTarget" value="11" /> + <option name="jvmTarget" value="9" /> <option name="languageVersion" value="1.3" /> <option name="apiVersion" value="1.3" /> <option name="pluginOptions"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 03f4493..3938301 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+034" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+043" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/.travis.yml b/.travis.yml index 10d46d9..dd5e66d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ addons: organization: "ethauvin-github" jdk: - - oraclejdk9 + - openjdk11 - openjdk14 before_install: @@ -21,6 +21,6 @@ before_install: after_success: - | - if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk9 ]; then + if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == openjdk11 ]; then ./gradlew sonarqube fi diff --git a/build.gradle b/build.gradle index 0d779ef..11a21b5 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,6 @@ dependencies { compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" - } test { diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 06bb921..1cb9672 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1592043035449L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1592615322387L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "004"; - public static final String VERSION = "0.8.0-beta+004"; + public static final String BUILDMETA = "046"; + public static final String VERSION = "0.8.0-beta+046"; /** * Disables the default constructor. diff --git a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index a8eed6c..4c33ece 100644 --- a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -36,18 +36,12 @@ import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.pinboard.PinboardPoster import org.testng.Assert import org.testng.annotations.Test -import java.io.IOException -import java.net.URI -import java.net.URISyntaxException +import java.net.URL import java.net.URLEncoder -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse import java.nio.charset.StandardCharsets class PinboardUtilsTest : LocalProperties() { @Test - @Throws(InterruptedException::class, IOException::class, URISyntaxException::class) fun pinboardTest() { val apiToken = getProperty("pinboard-api-token") val pinboard = PinboardPoster(apiToken) @@ -66,21 +60,10 @@ class PinboardUtilsTest : LocalProperties() { Assert.assertFalse(validatePin(apiToken, url = entry.link), "delete") } - @Throws(IOException::class, URISyntaxException::class, InterruptedException::class) private fun validatePin(apiToken: String, ircServer: String = "", url: String): Boolean { - val request = HttpRequest.newBuilder().uri( - URI( - "https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" - + URLEncoder.encode(url, StandardCharsets.UTF_8) - ) - ).GET().build() + val response = Utils.urlReader(URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + + URLEncoder.encode(url, StandardCharsets.UTF_8))) - val response = HttpClient.newBuilder() - .build() - .send(request, HttpResponse.BodyHandlers.ofString()) - - return if (response.statusCode() == 200) { - response.body().contains(url) && response.body().contains(ircServer) - } else false + return response.contains(url) && response.contains(ircServer) } } diff --git a/version.properties b/version.properties index 3a1fdac..a5f2ba8 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jun 13 03:10:34 PDT 2020 -version.buildmeta=004 +#Fri Jun 19 18:08:41 PDT 2020 +version.buildmeta=046 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+004 +version.semver=0.8.0-beta+046 From 6b615cf1413a592dce7c3929507c8d50b8cdefc7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 19 Jun 2020 19:04:59 -0700 Subject: [PATCH 410/842] OpenJDK 11.0.7 for CircleCI. --- .circleci/config.yml | 2 +- .idea/compiler.xml | 2 +- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 6 +++--- .idea/modules/mobibot.test.iml | 2 +- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6d2102a..6492ce2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,7 +42,7 @@ jobs: <<: *defaults docker: - - image: circleci/openjdk:11.0.7 + - image: cimg/openjdk:11.0.7 <<: *defaults_gradle diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 5a0b3ad..1f720ec 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -12,6 +12,6 @@ <module name="mobibot.main" /> </profile> </annotationProcessing> - <bytecodeTargetLevel target="1.9" /> + <bytecodeTargetLevel target="11" /> </component> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 92add17..366bd5f 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+043" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+046" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 636f6cf..61b4de2 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+043" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+046" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> - <configuration version="3" platform="JVM 9" allPlatforms="JVM [9]" useProjectSettings="false"> + <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> <compilerSettings> <option name="additionalArguments" value="-Xallow-no-source-files" /> </compilerSettings> @@ -12,7 +12,7 @@ <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> - <option name="jvmTarget" value="9" /> + <option name="jvmTarget" value="11" /> <option name="languageVersion" value="1.3" /> <option name="apiVersion" value="1.3" /> <option name="pluginOptions"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 3938301..5ebfad1 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+043" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+046" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 1cb9672..9de1107 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -14,13 +14,13 @@ import java.time.*; public final class ReleaseInfo { public static final String PROJECT = "mobibot"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1592615322387L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1592617191604L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "046"; - public static final String VERSION = "0.8.0-beta+046"; + public static final String BUILDMETA = "047"; + public static final String VERSION = "0.8.0-beta+047"; /** * Disables the default constructor. diff --git a/version.properties b/version.properties index a5f2ba8..beae2a7 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Jun 19 18:08:41 PDT 2020 -version.buildmeta=046 +#Fri Jun 19 18:39:50 PDT 2020 +version.buildmeta=047 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+046 +version.semver=0.8.0-beta+047 From 8d33ddc2523251bb9187574e3372a64058dc0c7a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Jun 2020 11:07:51 -0700 Subject: [PATCH 411/842] Fixed copyright. --- src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt | 4 ++-- src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt index b12a85f..3456a8d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -1,7 +1,7 @@ /* - * Pinboard.java + * PinboardUtils.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index 4c33ece..b79ff3d 100644 --- a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -1,5 +1,5 @@ /* - * PinboardTest.kt + * PinboardUtilsTest.kt * * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -62,7 +62,7 @@ class PinboardUtilsTest : LocalProperties() { private fun validatePin(apiToken: String, ircServer: String = "", url: String): Boolean { val response = Utils.urlReader(URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" - + URLEncoder.encode(url, StandardCharsets.UTF_8))) + + Utils.encodeUrl(url))) return response.contains(url) && response.contains(ircServer) } From 875269f5a60cecb4d07b345e0e5ed4b4b6f6af00 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Jun 2020 11:09:21 -0700 Subject: [PATCH 412/842] Added Utils.encodeUrl() --- src/main/java/net/thauvin/erik/mobibot/Utils.java | 11 +++++++++++ .../thauvin/erik/mobibot/modules/GoogleSearch.java | 6 +----- .../net/thauvin/erik/mobibot/modules/Weather2.java | 13 +++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java index 3d0632a..69bd6a9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.java @@ -42,6 +42,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.ZoneId; @@ -116,6 +117,16 @@ public final class Utils { return colorize(s, Colors.CYAN); } + /** + * URL encodes the given string. + * + * @param s The string to encode. + * @return The encoded string. + */ + public static String encodeUrl(final String s) { + return URLEncoder.encode(s, StandardCharsets.UTF_8); + } + /** * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. * diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java index a36789f..478a64e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java @@ -44,8 +44,6 @@ import org.json.JSONObject; import java.io.IOException; import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -98,15 +96,13 @@ public final class GoogleSearch extends ThreadedModule { if (StringUtils.isNotBlank(query)) { final ArrayList<Message> results = new ArrayList<>(); try { - final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString()); - final URL url = new URL("https://www.googleapis.com/customsearch/v1?key=" + apiKey + "&cx=" + cseKey + "&q=" - + q + + Utils.encodeUrl(query) + "&filter=1&num=5&alt=json"); final JSONObject json = new JSONObject(Utils.urlReader(url)); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index a0894d0..9ef1426 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -44,13 +44,11 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; -import okhttp3.HttpUrl; import org.apache.commons.lang3.StringUtils; import org.jibble.pircbot.Colors; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import static net.thauvin.erik.mobibot.Utils.bold; @@ -185,14 +183,9 @@ public class Weather2 extends ThreadedModule { messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(), Colors.GREEN)); } else { - final HttpUrl url = Objects.requireNonNull(HttpUrl.parse( - "https://openweathermap.org/find")) - .newBuilder() - .addQueryParameter("q", - city + ',' - + StringUtils.upperCase(country)) - .build(); - messages.add(new NoticeMessage(url.toString(), Colors.GREEN)); + final String url = "https://openweathermap.org/find?q=" + + Utils.encodeUrl(city + ',' + StringUtils.upperCase(country)); + messages.add(new NoticeMessage(url, Colors.GREEN)); } } } From 8f35d050dfaac74a2de8cb4f0a9fa5ca16d7c256 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Jun 2020 17:09:58 -0700 Subject: [PATCH 413/842] Removed okhttp3 dependency, using Utils.readUrl() isntead. --- build.gradle | 4 +- .../erik/mobibot/modules/StockQuote.java | 84 +++++++++---------- .../erik/mobibot/modules/Weather2.java | 5 +- 3 files changed, 42 insertions(+), 51 deletions(-) diff --git a/build.gradle b/build.gradle index 11a21b5..5925622 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.6' - implementation 'com.squareup.okhttp3:okhttp:4.7.2' + //implementation 'com.squareup.okhttp3:okhttp:4.7.2' implementation 'com.rometools:rome:1.13.1' @@ -63,7 +63,7 @@ dependencies { implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7") + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' testImplementation 'org.testng:testng:7.2.0' testImplementation 'org.assertj:assertj-core:3.16.1' diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java index 03b6ebb..4e5c1c9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java @@ -39,18 +39,15 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage; import net.thauvin.erik.mobibot.msg.Message; import net.thauvin.erik.mobibot.msg.NoticeMessage; import net.thauvin.erik.mobibot.msg.PublicMessage; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.net.URL; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * The StockQuote module. @@ -88,43 +85,39 @@ public final class StockQuote extends ThreadedModule { @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?") - private static JSONObject getJsonResponse(final Response response, final String debugMessage) - throws IOException, ModuleException { - if (response.isSuccessful()) { - if (response.body() != null) { - final JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string()); + private static JSONObject getJsonResponse(final String response, final String debugMessage) + throws ModuleException { + if (StringUtils.isNotBlank(response)) { + final JSONObject json = new JSONObject(response); - try { - final String info = json.getString("Information"); - if (!info.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(info)); - } - } catch (JSONException ignore) { - // Do nothing + try { + final String info = json.getString("Information"); + if (!info.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(info)); } - - try { - final String error = json.getString("Note"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); - } - } catch (JSONException ignore) { - // Do nothing - } - - try { - final String error = json.getString("Error Message"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); - } - } catch (JSONException ignore) { - // Do nothing - } - - return json; - } else { - throw new ModuleException(debugMessage, "Invalid Response (" + response.code() + ')'); + } catch (JSONException ignore) { + // Do nothing } + + try { + final String error = json.getString("Note"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing + } + + try { + final String error = json.getString("Error Message"); + if (!error.isEmpty()) { + throw new ModuleException(debugMessage, Utils.unescapeXml(error)); + } + } catch (JSONException ignore) { + // Do nothing + } + + return json; } else { throw new ModuleException(debugMessage, "Empty Response."); } @@ -146,13 +139,13 @@ public final class StockQuote extends ThreadedModule { if (StringUtils.isNotBlank(symbol)) { final String debugMessage = "getQuote(" + symbol + ')'; final ArrayList<Message> messages = new ArrayList<>(); - final OkHttpClient client = new OkHttpClient(); + String response; try { // Search for symbol/keywords - Request request = new Request.Builder().url( - ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build(); - Response response = client.newCall(request).execute(); + response = Utils.urlReader(new URL( + ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey=" + + Utils.encodeUrl(apiKey))); JSONObject json = getJsonResponse(response, debugMessage); @@ -165,10 +158,9 @@ public final class StockQuote extends ThreadedModule { final JSONObject symbolInfo = symbols.getJSONObject(0); // Get quote for symbol - request = new Request.Builder().url( - ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey=" - + apiKey).build(); - response = client.newCall(request).execute(); + response = Utils.urlReader(new URL( + ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + Utils.encodeUrl(symbolInfo.getString("1. symbol")) + + "&apikey=" + Utils.encodeUrl(apiKey))); json = getJsonResponse(response, debugMessage); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 9ef1426..e1910ac 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -183,9 +183,8 @@ public class Weather2 extends ThreadedModule { messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(), Colors.GREEN)); } else { - final String url = "https://openweathermap.org/find?q=" + - Utils.encodeUrl(city + ',' + StringUtils.upperCase(country)); - messages.add(new NoticeMessage(url, Colors.GREEN)); + messages.add(new NoticeMessage("https://openweathermap.org/find?q=" + Utils.encodeUrl( + city + ',' + StringUtils.upperCase(country)), Colors.GREEN)); } } } From 52c5e2dba792cb8ac2223e6a734ddab6bfc6a37c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 20 Jun 2020 20:21:56 -0700 Subject: [PATCH 414/842] Cleanup. --- .../net/thauvin/erik/mobibot/commands/Versions.java | 7 +++---- .../java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt | 10 ++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 7b80f3d..37491da 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -40,7 +40,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; public class Versions extends AbstractCommand { - private final List<String> versions = + private final List<String> verList = List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', @@ -64,8 +64,7 @@ public class Versions extends AbstractCommand { @NotNull @Override public List<String> getHelp() { - return List.of("To view the versions data (bot, java, etc.):", - Utils.helpIndent("%s " + getName())); + return List.of("To view the versions data (bot, platform, java, etc.):", Utils.helpIndent("%c " + getName())); } @Override @@ -90,7 +89,7 @@ public class Versions extends AbstractCommand { final boolean isOp, final boolean isPrivate) { if (isOp) { - for (final String v : versions) { + for (final String v : verList) { getBot().send(sender, v, isPrivate); } } else { diff --git a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index b79ff3d..5f58c91 100644 --- a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -37,8 +37,6 @@ import net.thauvin.erik.pinboard.PinboardPoster import org.testng.Assert import org.testng.annotations.Test import java.net.URL -import java.net.URLEncoder -import java.nio.charset.StandardCharsets class PinboardUtilsTest : LocalProperties() { @Test @@ -61,8 +59,12 @@ class PinboardUtilsTest : LocalProperties() { } private fun validatePin(apiToken: String, ircServer: String = "", url: String): Boolean { - val response = Utils.urlReader(URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" - + Utils.encodeUrl(url))) + val response = Utils.urlReader( + URL( + "https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + + Utils.encodeUrl(url) + ) + ) return response.contains(url) && response.contains(ircServer) } From 1749a081deb9dfd404fbbfd9ee06253a48e903f8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 11 Jul 2020 18:29:46 -0700 Subject: [PATCH 415/842] Updated dependencies. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 22 +++++++++++----------- .idea/modules/mobibot.test.iml | 22 +++++++++++----------- build.gradle | 12 ++++++------ config/pmd.xml | 3 +-- gradle/wrapper/gradle-wrapper.properties | 2 +- 6 files changed, 31 insertions(+), 32 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 366bd5f..d9eb8b4 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+046" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+068" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 61b4de2..d3ec72e 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+046" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+068" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.14.1/6e19be853792088096ba147150a6db5ced43e2d2/rome-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.14.1/a0f9e927afe54ef682f1859afed310147bc12612/rome-utils-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -51,34 +51,34 @@ <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.7.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.13.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.14.1" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.14.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.6.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 5ebfad1..9de0d17 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+046" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+068" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.7.2/c9acfd63537db1d7d21d98a7405e22449bb881d6/okhttp-4.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.13.1/3e949b65fa4224e78e3c513a54be56430b18ea6/rome-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.6.0/f06923d428f3c8e6f571043ec29a45d0cd9d2bf/okio-jvm-2.6.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.4/e97331cf473041be64301e6e7ebdc70700fdff24/spotbugs-annotations-4.0.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/3.3.3/4878395d4e63173f3825e17e5e0690e8054445f1/mockito-core-3.3.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.13.1/a59ce341139f0c58901127fab6445bd1c496af9/rome-utils-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.10.5/d39f2a6c7a3550e03fb12a870e0829b0fa87f036/byte-buddy-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.10.5/d1c949ee74c3421ffd3d9159c867777ded928448/byte-buddy-agent-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.6/639033469776fd37c08358c6b92a4761feb2af4b/objenesis-2.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.14.1/6e19be853792088096ba147150a6db5ced43e2d2/rome-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/3.3.3/4878395d4e63173f3825e17e5e0690e8054445f1/mockito-core-3.3.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.14.1/a0f9e927afe54ef682f1859afed310147bc12612/rome-utils-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.10.5/d39f2a6c7a3550e03fb12a870e0829b0fa87f036/byte-buddy-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.10.5/d1c949ee74c3421ffd3d9159c867777ded928448/byte-buddy-agent-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.6/639033469776fd37c08358c6b92a4761feb2af4b/objenesis-2.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -59,28 +59,27 @@ <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.7.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.13.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.14.1" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.4" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.16.1" level="project" /> <orderEntry type="library" name="Gradle: org.mockito:mockito-core:3.3.3" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.14.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.6.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> @@ -92,6 +91,7 @@ <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy:1.10.5" level="project" /> <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy-agent:1.10.5" level="project" /> <orderEntry type="library" name="Gradle: org.objenesis:objenesis:2.6" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> diff --git a/build.gradle b/build.gradle index 5925622..c6b72b7 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,9 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.28.0' - id 'com.github.spotbugs' version '4.4.1' + id 'com.github.spotbugs' version '4.4.4' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.10.0-RC1' + id 'io.gitlab.arturbosch.detekt' version '1.10.0' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -23,7 +23,7 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j : '2.13.3', - spotbugs: '4.0.4' + spotbugs: '4.0.6' ] repositories { @@ -50,7 +50,7 @@ dependencies { implementation 'commons-net:commons-net:3.6' //implementation 'com.squareup.okhttp3:okhttp:4.7.2' - implementation 'com.rometools:rome:1.13.1' + implementation 'com.rometools:rome:1.14.1' implementation 'org.json:json:20200518' implementation 'org.jsoup:jsoup:1.13.1' @@ -67,7 +67,7 @@ dependencies { testImplementation 'org.testng:testng:7.2.0' testImplementation 'org.assertj:assertj-core:3.16.1' - testImplementation 'org.mockito:mockito-core:3.3.3' + testImplementation 'org.mockito:mockito-core:3.4.0' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' @@ -106,7 +106,7 @@ tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { } pmd { - toolVersion = '6.24.0' + toolVersion = '6.25.0' ignoreFailures = true ruleSets = [] ruleSetFiles = files("${projectDir}/config/pmd.xml") diff --git a/config/pmd.xml b/config/pmd.xml index 1233156..8d35af0 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -17,10 +17,9 @@ <rule ref="category/java/bestpractices.xml/MissingOverride"> <properties> <property name="violationSuppressXPath" - value="./MethodDeclarator[@Image='hashCode' or @Image='equals' or @Image='toString']"/> + value="//MethodDeclaration[@Name='hashCode' or @Name='equals' or @Name='toString']"/> </properties> </rule> - <rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons"/> <rule ref="category/java/bestpractices.xml/PreserveStackTrace"/> <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/> <!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 622ab64..bb8b2fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 044f1b85b71c4a2cd7c8b9ea5279ae506d702ef1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 11 Jul 2020 18:32:30 -0700 Subject: [PATCH 416/842] shutdown on autoPost only. --- .../java/net/thauvin/erik/mobibot/modules/Twitter.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java index 7728a9f..87446a5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java @@ -86,7 +86,7 @@ public final class Twitter extends ThreadedModule { help.add(Utils.helpIndent("%c " + TWITTER_CMD + " <message>")); properties.put(AUTOPOST_PROP, "false"); - initProperties(CONSUMER_KEY_PROP,CONSUMER_SECRET_PROP,HANDLE_PROP,TOKEN_PROP,TOKEN_SECRET_PROP); + initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP); } /** @@ -270,8 +270,10 @@ public final class Twitter extends ThreadedModule { * Post all the entries to Twitter on shutdown. */ public final void shutdown() { - for (final int index : entries) { - postEntry(index); + if (isAutoPost()) { + for (final int index : entries) { + postEntry(index); + } } } } From 8b4b0d7dd1f799a98583f2c5b4dce44cefb96792 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 11 Jul 2020 18:32:57 -0700 Subject: [PATCH 417/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt | 2 +- website/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index a313b01..28b57c9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -73,7 +73,7 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { bot.send(EntriesUtils.buildTags(index, entry)) LinksMgr.saveEntries(bot, false) } else { - bot.send(sender, "Please ask a channel op to change the tags for you.",isPrivate) + bot.send(sender, "Please ask a channel op to change the tags for you.", isPrivate) } } else { if (entry.hasTags()) { diff --git a/website/index.html b/website/index.html index a934ecd..6dfd579 100644 --- a/website/index.html +++ b/website/index.html @@ -103,7 +103,7 @@ <div><code>mobibot: joke</code></div> </li> <li>Rolling dice and playing war - <div><code>mobibot: dice</code><div> + <div><code>mobibot: dice</code></div> <div><code>mobibot: war</code></div> </li> <li>Posting to <a href="https://twitter.com/mobitopia">Twitter</a></li> From 62417e76f73ae4b93c6180bfa62c76f9c5d1a458 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 15 Jul 2020 18:34:33 -0700 Subject: [PATCH 418/842] Updated dependencies. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c6b72b7..326c5a2 100644 --- a/build.gradle +++ b/build.gradle @@ -50,7 +50,7 @@ dependencies { implementation 'commons-net:commons-net:3.6' //implementation 'com.squareup.okhttp3:okhttp:4.7.2' - implementation 'com.rometools:rome:1.14.1' + implementation 'com.rometools:rome:1.15.0' implementation 'org.json:json:20200518' implementation 'org.jsoup:jsoup:1.13.1' From aea73acce5ee47a8db4d17f974f13f6b37a5947d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 21 Jul 2020 17:57:58 -0700 Subject: [PATCH 419/842] Reworked info command. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 10 +- .idea/modules/mobibot.test.iml | 16 +-- mobibot.mustache | 9 +- .../net/thauvin/erik/mobibot/ReleaseInfo.java | 11 +- .../net/thauvin/erik/mobibot/Mobibot.java | 21 +--- .../thauvin/erik/mobibot/commands/Info.java | 115 ++++++++++++++++++ .../net/thauvin/erik/mobibot/commands/Info.kt | 86 ------------- version.mustache | 9 +- 9 files changed, 157 insertions(+), 122 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Info.java delete mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Info.kt diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index d9eb8b4..ace356b 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+068" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+071" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index d3ec72e..ec0fc30 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+068" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+071" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.14.1/6e19be853792088096ba147150a6db5ced43e2d2/rome-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.14.1/a0f9e927afe54ef682f1859afed310147bc12612/rome-utils-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -55,10 +55,10 @@ <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.14.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> @@ -69,7 +69,7 @@ <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.14.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 9de0d17..46615ee 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+068" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+071" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.10/e155460aaf5b464062a09c3923f089ce99128a17/commons-lang3-3.10.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.14.1/6e19be853792088096ba147150a6db5ced43e2d2/rome-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/3.3.3/4878395d4e63173f3825e17e5e0690e8054445f1/mockito-core-3.3.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.14.1/a0f9e927afe54ef682f1859afed310147bc12612/rome-utils-1.14.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.10.5/d39f2a6c7a3550e03fb12a870e0829b0fa87f036/byte-buddy-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.10.5/d1c949ee74c3421ffd3d9159c867777ded928448/byte-buddy-agent-1.10.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.6/639033469776fd37c08358c6b92a4761feb2af4b/objenesis-2.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/3.4.4/6213e9c1b25a845167e27a51e6b6886b4306a920/mockito-core-3.4.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.10.13/1426b15be5954246a9a72fd4baae1f42b9a4f45d/byte-buddy-1.10.13.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.10.13/f5b939963d13be46c101a3c12bf49aa23abb191/byte-buddy-agent-1.10.13.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.6/639033469776fd37c08358c6b92a4761feb2af4b/objenesis-2.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -56,10 +56,10 @@ <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.10" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.14.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> @@ -71,9 +71,9 @@ <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.16.1" level="project" /> - <orderEntry type="library" name="Gradle: org.mockito:mockito-core:3.3.3" level="project" /> + <orderEntry type="library" name="Gradle: org.mockito:mockito-core:3.4.4" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.14.1" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> @@ -88,8 +88,8 @@ <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy:1.10.5" level="project" /> - <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy-agent:1.10.5" level="project" /> + <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy:1.10.13" level="project" /> + <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy-agent:1.10.13" level="project" /> <orderEntry type="library" name="Gradle: org.objenesis:objenesis:2.6" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> diff --git a/mobibot.mustache b/mobibot.mustache index 5314f35..a090f01 100644 --- a/mobibot.mustache +++ b/mobibot.mustache @@ -13,14 +13,19 @@ import java.time.*; */ public final class {{className}} { public static final String PROJECT = "{{project}}"; + public static final String VERSION = "{{version}}"; public static final LocalDateTime BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); + public static final int MAJOR = {{major}}; public static final int MINOR = {{minor}}; public static final int PATCH = {{patch}}; - public static final String PRERELEASE = "{{preRelease}}"; public static final String BUILDMETA = "{{buildMeta}}"; - public static final String VERSION = "{{version}}"; + public static final String PRERELEASE = "{{preRelease}}"; + + public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; + public static final String AUTHOR = "Erik C. Thauvin"; + public static final String AUTHOR_URL = "https://erik.thauvin.net/"; /** * Disables the default constructor. diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 9de1107..f1d555b 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,19 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; + public static final String VERSION = "0.8.0-beta+085"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1592617191604L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1595378887339L), ZoneId.systemDefault()); + public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; + public static final String BUILDMETA = "085"; public static final String PRERELEASE = "beta"; - public static final String BUILDMETA = "047"; - public static final String VERSION = "0.8.0-beta+047"; + + public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; + public static final String AUTHOR = "Erik C. Thauvin"; + public static final String AUTHOR_URL = "https://erik.thauvin.net/"; /** * Disables the default constructor. diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index df34caa..7f1bb5d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -114,15 +114,6 @@ import static org.apache.commons.lang3.StringUtils.lowerCase; @Version(properties = "version.properties", className = "ReleaseInfo") public class Mobibot extends PircBot { - /** - * Info Strings. - */ - @SuppressWarnings("indentation") - public static final List<String> INFO = - List.of( - ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION - + " (" + Utils.green("https://www.mobitopia.org/mobibot/") + ')', - "Written by Erik C. Thauvin (" + Utils.green("https://erik.thauvin.net/") + ')'); // Logger private static final Logger LOGGER = LogManager.getLogger(Mobibot.class); // Maximum number of times the bot will try to reconnect, if disconnected @@ -303,9 +294,9 @@ public class Mobibot extends PircBot { // Output the usage new HelpFormatter().printHelp(Mobibot.class.getName(), options); } else if (commandLine.hasOption(Constants.VERSION_ARG.charAt(0))) { - for (final String s : INFO) { - System.out.println(s); - } + System.out.println(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION + + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')'); + System.out.println(ReleaseInfo.WEBSITE); } else { final Properties p = new Properties(); @@ -413,7 +404,7 @@ public class Mobibot extends PircBot { } } } - setVersion(INFO.get(0)); + setVersion(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION); identify(); joinChannel(); } @@ -749,7 +740,7 @@ public class Mobibot extends PircBot { if (cmd.startsWith(Constants.HELP_CMD)) { // help helpResponse(sender, args, true); } else if (isOp && "kill".equals(cmd)) { // kill - twitter.notification("%1$s killed by " + sender + "on %3$s"); + twitter.notification("%1$s killed by " + sender + " on %3$s"); sendRawLine("QUIT : Poof!"); System.exit(0); } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug @@ -763,7 +754,7 @@ public class Mobibot extends PircBot { send(sender + " has just signed my death sentence."); TIMER.cancel(); twitter.shutdown(); - twitter.notification("%1$s stopped by " + sender + "on %3$s"); + twitter.notification("%1$s stopped by " + sender + " on %3$s"); sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java new file mode 100644 index 0000000..2beab74 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -0,0 +1,115 @@ +/* + * InfoJ.java + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands; + +import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.ReleaseInfo; +import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.commands.links.LinksMgr; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.lang.management.ManagementFactory; +import java.util.List; + +public class Info extends AbstractCommand { + private final List<String> version = List.of( + StringUtils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION + " (" + + Utils.green(ReleaseInfo.WEBSITE) + ')', + "Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')'); + + public Info(@NotNull final Mobibot bot) { + super(bot); + } + + @NotNull + @Override + public String getName() { + return "info"; + } + + @NotNull + @Override + public List<String> getHelp() { + return List.of("To view information about the bot:", Utils.helpIndent("%c " + getName())); + } + + @Override + public boolean isOp() { + return false; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public void commandResponse(@NotNull final String sender, + @NotNull final String login, + @NotNull final String args, + final boolean isOp, + final boolean isPrivate) { + for (final String v : version) { + getBot().send(sender, v, isPrivate); + } + + final StringBuilder info = new StringBuilder("Uptime: "); + + info.append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())) + .append(" [Entries: ") + .append(LinksMgr.getEntriesCount()); + + if (isOp) { + if (getBot().getTell().isEnabled()) { + info.append(", Messages: ") + .append(getBot().getTell().size()); + } + if (getBot().getTwitter().isAutoPost()) { + info.append(", Twitter: ") + .append(getBot().getTwitter().entriesCount()); + } + } + + info.append(", Recap: ") + .append(Recap.recapCount()) + .append(']'); + + getBot().send(sender, info.toString(), isPrivate); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt deleted file mode 100644 index 1698028..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Info.kt - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.commands.links.LinksMgr -import java.lang.management.ManagementFactory - -class Info(bot: Mobibot) : AbstractCommand(bot) { - override val name = "info" - override val help = listOf( - "To view information about the bot:", - Utils.helpIndent("%c $name") - ) - override val isOp = false - override val isPublic = true - override val isVisible = true - - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - for (info in Mobibot.INFO) { - bot.send(sender, info, isPrivate) - } - - val info = StringBuilder("Uptime: ") - - with(info) { - append(Utils.uptime(ManagementFactory.getRuntimeMXBean().uptime)) - append(" [Entries: ") - append(LinksMgr.entriesCount) - - if (isOp) { - if (bot.tell.isEnabled()) { - append(", Messages: ") - append(bot.tell.size()) - } - if (bot.twitter.isAutoPost) { - append(", Twitter: ") - append(bot.twitter.entriesCount()) - } - } - - append(", Recap: ") - append(Recap.recapCount()) - append(']') - } - - bot.send(sender, info.toString(), isPrivate) - } -} diff --git a/version.mustache b/version.mustache index 5314f35..a090f01 100644 --- a/version.mustache +++ b/version.mustache @@ -13,14 +13,19 @@ import java.time.*; */ public final class {{className}} { public static final String PROJECT = "{{project}}"; + public static final String VERSION = "{{version}}"; public static final LocalDateTime BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); + public static final int MAJOR = {{major}}; public static final int MINOR = {{minor}}; public static final int PATCH = {{patch}}; - public static final String PRERELEASE = "{{preRelease}}"; public static final String BUILDMETA = "{{buildMeta}}"; - public static final String VERSION = "{{version}}"; + public static final String PRERELEASE = "{{preRelease}}"; + + public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; + public static final String AUTHOR = "Erik C. Thauvin"; + public static final String AUTHOR_URL = "https://erik.thauvin.net/"; /** * Disables the default constructor. From c65204414d7022408866d388fb4450bf74ef61a1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 21 Jul 2020 17:58:11 -0700 Subject: [PATCH 420/842] Updated dependencies. --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 326c5a2..9a91c0c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'application' id 'checkstyle' - id 'com.github.ben-manes.versions' version '0.28.0' + id 'com.github.ben-manes.versions' version '0.29.0' id 'com.github.spotbugs' version '4.4.4' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.10.0' @@ -43,7 +43,7 @@ dependencies { implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" - implementation 'org.apache.commons:commons-lang3:3.10' + implementation 'org.apache.commons:commons-lang3:3.11' //implementation 'org.apache.commons:commons-text:1.8' implementation 'commons-cli:commons-cli:1.4' @@ -67,7 +67,7 @@ dependencies { testImplementation 'org.testng:testng:7.2.0' testImplementation 'org.assertj:assertj-core:3.16.1' - testImplementation 'org.mockito:mockito-core:3.4.0' + testImplementation 'org.mockito:mockito-core:3.4.4' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' From 64d2328618e6ed398ec01f462e6cc81b45019bbe Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jul 2020 02:00:18 -0700 Subject: [PATCH 421/842] Added BitBucket and GitLab CIs. More code cleanup. --- .circleci/config.yml | 2 +- .github/workflows/gradle.yml | 26 +++++++++++++++ .gitlab-ci.yml | 32 +++++++++++++++++++ .travis.yml | 2 +- bitbucket-pipelines.yml | 9 ++++++ .../net/thauvin/erik/mobibot/Mobibot.java | 5 ++- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Info.java | 4 +-- .../erik/mobibot/commands/Versions.java | 11 +++---- .../erik/mobibot/modules/TwitterTest.java | 7 ++-- 10 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/gradle.yml create mode 100644 .gitlab-ci.yml create mode 100644 bitbucket-pipelines.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 6492ce2..20a8cc6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ defaults: &defaults environment: JVM_OPTS: -Xmx3200m TERM: dumb - CI: true + CI_NAME: "CircleCI" defaults_gradle: &defaults_gradle steps: diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..82c3591 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,26 @@ +name: Java CI with Gradle + +on: + push + pull_request + workflow_dispatch + + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Set environment variables + env: + CI_NAME: "GitHub CI" + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Test with Gradle + run: ./gradlew check diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..1e06d44 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,32 @@ +image: openjdk:11-jdk-slim + +variables: + GRADLE_OPTS: "-Dorg.gradle.daemon=false" + CI_NAME: "GitLab CI" + +before_script: + - export GRADLE_USER_HOME=`pwd`/.gradle + +stages: + - build + - test + +build: + stage: build + script: ./gradlew --build-cache assemble + cache: + key: "$CI_COMMIT_REF_NAME" + policy: push + paths: + - build + - .gradle + +test: + stage: test + script: ./gradlew check + cache: + key: "$CI_COMMIT_REF_NAME" + policy: pull + paths: + - build + - .gradle diff --git a/.travis.yml b/.travis.yml index dd5e66d..991c643 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty env: global: - - CI=true + - CI_NAME: "Travis CI" install: - git fetch --unshallow --tags diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml new file mode 100644 index 0000000..219c29f --- /dev/null +++ b/bitbucket-pipelines.yml @@ -0,0 +1,9 @@ +image: openjdk:11 + +pipelines: + default: + - step: + caches: + - gradle + script: + - bash ./gradlew check diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 7f1bb5d..1289405 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -188,12 +188,12 @@ public class Mobibot extends PircBot { setVerbose(true); setAutoNickChange(true); setLogin(p.getProperty("login", getName())); - setVersion(p.getProperty("weblog", "")); + setVersion(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION); // setMessageDelay(1000); setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", "")); // Set the URLs - setWeblogUrl(getVersion()); + setWeblogUrl(p.getProperty("weblog", "")); setBacklogsUrl(Utils.ensureDir(p.getProperty("backlogs", weblogUrl), true)); // Set the pinboard authentication @@ -404,7 +404,6 @@ public class Mobibot extends PircBot { } } } - setVersion(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION); identify(); joinChannel(); } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 1211fcb..f371422 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -62,7 +62,7 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { isPrivate: Boolean ) { with(getProperty(FEED_PROP)) { - if (!this.isNullOrBlank()) { + if (!isNullOrBlank()) { Thread(FeedReader(bot, sender, this)).start() } else { bot.send(sender, "There is no feed setup for this channel.", false) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index 2beab74..aed639c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -44,8 +44,8 @@ import java.util.List; public class Info extends AbstractCommand { private final List<String> version = List.of( - StringUtils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION + " (" - + Utils.green(ReleaseInfo.WEBSITE) + ')', + StringUtils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION + + " (" + Utils.green(ReleaseInfo.WEBSITE) + ')', "Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')'); public Info(@NotNull final Mobibot bot) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 37491da..4b08631 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -42,13 +42,10 @@ import java.util.List; public class Versions extends AbstractCommand { private final List<String> verList = List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', - "Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " - + System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty( - "java.runtime.version") + ')', - "VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") - + ", " - + System.getProperty("java.vm.info") + ')'); + "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + + " (" + System.getProperty("os.arch") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + + ' ' + System.getProperty("java.runtime.version")); public Versions(@NotNull final Mobibot bot) { super(bot); diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java index 88782ad..2c14d2b 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java @@ -51,10 +51,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class TwitterTest extends LocalProperties { @SuppressFBWarnings("MDM") private String getCi() { - if ("true".equals(System.getenv("CIRCLECI"))) { - return "CircleCI"; - } else if ("true".equals(System.getenv("TRAVIS"))) { - return "Travis CI"; + final String ciName = System.getenv("CI_NAME"); + if (ciName != null) { + return ciName; } else { try { return InetAddress.getLocalHost().getHostName(); From fd9a5f83fe2c97354e57054063c2e3d94b15eb53 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jul 2020 16:04:42 -0700 Subject: [PATCH 422/842] Fixed push. --- .github/workflows/gradle.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 82c3591..acb273d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,9 +1,9 @@ name: Java CI with Gradle on: - push - pull_request - workflow_dispatch + push: + pull_request: + workflow_dispatch: jobs: From ec066d62987fa4b446d89ae4a956a41147fbb09b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jul 2020 16:07:48 -0700 Subject: [PATCH 423/842] Fixed push, etc., again. --- .github/workflows/gradle.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index acb273d..2e56d13 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,10 +1,6 @@ name: Java CI with Gradle -on: - push: - pull_request: - workflow_dispatch: - +on: [push, pull_request, workflow_dispatch] jobs: build: From 71a3bb4dba03212c1dcb38b0bf3986625a576193 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jul 2020 16:15:18 -0700 Subject: [PATCH 424/842] Move env section. --- .github/workflows/gradle.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 2e56d13..37ec6ed 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,6 +1,7 @@ name: Java CI with Gradle on: [push, pull_request, workflow_dispatch] + jobs: build: @@ -8,9 +9,6 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set environment variables - env: - CI_NAME: "GitHub CI" - uses: actions/checkout@v2 - name: Set up JDK 11 uses: actions/setup-java@v1 @@ -18,5 +16,7 @@ jobs: java-version: 11 - name: Grant execute permission for gradlew run: chmod +x gradlew + env: + CI_NAME: "GitHub CI" - name: Test with Gradle run: ./gradlew check From 9552dd13734c3b9a81dd8f6d855657666e47256d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jul 2020 16:34:12 -0700 Subject: [PATCH 425/842] Added env secrets. --- .github/workflows/gradle.yml | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 37ec6ed..38cd0c4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,22 +1,30 @@ name: Java CI with Gradle on: [push, pull_request, workflow_dispatch] - jobs: build: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - env: - CI_NAME: "GitHub CI" - - name: Test with Gradle - run: ./gradlew check + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + env: + CI_NAME: "GitHub CI" + ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} + GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} + GOOGLE_CSE_CX: ${{ secrets.GOOGLE_CSE_CX }} + OWM_API_KEY: ${{ secrets.OWM_API_KEY }} + PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} + TWITTER_CONSUMERKEY: ${{ secrets.TWITTER_CONSUMERKEY }} + TWITTER_CONSUMERSECRET: ${{ secrets.TWITTER_CONSUMERSECRET }} + TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} + TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} + TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} + - name: Test with Gradle + run: ./gradlew check From 6d556afa4022622ea36a9101fc892c774c0c41c5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jul 2020 16:42:01 -0700 Subject: [PATCH 426/842] Moved env to test step. --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 38cd0c4..2fff393 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -14,6 +14,8 @@ jobs: java-version: 11 - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Test with Gradle + run: ./gradlew check env: CI_NAME: "GitHub CI" ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -26,5 +28,3 @@ jobs: TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} - - name: Test with Gradle - run: ./gradlew check From 94871a4df8e3848e17f1d5080664bd0e4eceb32d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 26 Jul 2020 17:28:48 -0700 Subject: [PATCH 427/842] Fixed CI_NAME env. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 991c643..83aefb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty env: global: - - CI_NAME: "Travis CI" + - CI_NAME="Travis CI" install: - git fetch --unshallow --tags @@ -21,6 +21,6 @@ before_install: after_success: - | - if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == openjdk11 ]; then - ./gradlew sonarqube - fi + if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == openjdk11 ]; then + ./gradlew sonarqube + fi From 70d99a028dc9523bc58e73419a4e98dde9930492 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 Aug 2020 15:06:56 -0700 Subject: [PATCH 428/842] Added try-with-resources. --- .../net/thauvin/erik/mobibot/FeedReader.java | 22 ++++++++++--------- .../commands/tell/TellMessagesMgr.java | 11 +++++----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java index 8c58b09..41795f3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java @@ -81,17 +81,19 @@ public class FeedReader implements Runnable { public final void run() { try { final SyndFeedInput input = new SyndFeedInput(); - final SyndFeed feed = input.build(new XmlReader(new URL(url))); + try (final XmlReader reader = new XmlReader(new URL(url))) { + final SyndFeed feed = input.build(reader); - final List<SyndEntry> items = feed.getEntries(); - if (items.isEmpty()) { - bot.send(sender, "There is currently nothing to view.", false); - } else { - SyndEntry item; - for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { - item = items.get(i); - bot.send(sender, item.getTitle(), false); - bot.send(sender, Utils.helpIndent(Utils.green(item.getLink()), false), false); + final List<SyndEntry> items = feed.getEntries(); + if (items.isEmpty()) { + bot.send(sender, "There is currently nothing to view.", false); + } else { + SyndEntry item; + for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { + item = items.get(i); + bot.send(sender, item.getTitle(), false); + bot.send(sender, Utils.helpIndent(Utils.green(item.getLink()), false), false); + } } } } catch (MalformedURLException e) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java index b712438..a921c7c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java @@ -90,7 +90,7 @@ final class TellMessagesMgr { public static List<TellMessage> load(final String file, final Logger logger) { try { try (final ObjectInput input = new ObjectInputStream( - new BufferedInputStream(Files.newInputStream(Paths.get(file))))) { + new BufferedInputStream(Files.newInputStream(Paths.get(file))))) { logger.debug("Loading the messages."); return ((List<TellMessage>) input.readObject()); @@ -113,10 +113,11 @@ final class TellMessagesMgr { */ public static void save(final String file, final List<TellMessage> messages, final Logger logger) { try { - try (final ObjectOutput output = new ObjectOutputStream( - new BufferedOutputStream(Files.newOutputStream(Paths.get(file))))) { - logger.debug("Saving the messages."); - output.writeObject(messages); + try (final BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(file)))) { + try (final ObjectOutput output = new ObjectOutputStream(bos)) { + logger.debug("Saving the messages."); + output.writeObject(messages); + } } } catch (IOException e) { logger.error("Unable to save messages queue.", e); From 8cd309e3a389931547a0e5ab0104f58c2aa4545f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 Aug 2020 15:15:41 -0700 Subject: [PATCH 429/842] Updated dependencies. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- build.gradle | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index ace356b..910a915 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+071" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+090" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index ec0fc30..02b91f8 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+071" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+090" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 46615ee..f7f2246 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+071" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+090" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> diff --git a/build.gradle b/build.gradle index 9a91c0c..9aa0214 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,9 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.29.0' - id 'com.github.spotbugs' version '4.4.4' + id 'com.github.spotbugs' version '4.5.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.10.0' + id 'io.gitlab.arturbosch.detekt' version '1.11.0-RC1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -23,7 +23,7 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j : '2.13.3', - spotbugs: '4.0.6' + spotbugs: '4.1.1' ] repositories { @@ -65,9 +65,9 @@ dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' - testImplementation 'org.testng:testng:7.2.0' + testImplementation 'org.testng:testng:7.3.0' testImplementation 'org.assertj:assertj-core:3.16.1' - testImplementation 'org.mockito:mockito-core:3.4.4' + testImplementation 'org.mockito:mockito-core:3.4.6' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' From d7bfd70fa9b1b25645326db251f6444a85af1103 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 9 Aug 2020 21:07:05 -0700 Subject: [PATCH 430/842] Upgraded to Kotlin 1.4.0-rc --- build.gradle | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 9aa0214..270e19b 100644 --- a/build.gradle +++ b/build.gradle @@ -4,11 +4,11 @@ plugins { id 'com.github.ben-manes.versions' version '0.29.0' id 'com.github.spotbugs' version '4.5.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.11.0-RC1' + id 'io.gitlab.arturbosch.detekt' version '1.11.0-RC2' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.3.72' + id 'org.jetbrains.kotlin.jvm' version '1.4.0-rc' id 'org.sonarqube' version '3.0' id 'pmd' } @@ -47,7 +47,7 @@ dependencies { //implementation 'org.apache.commons:commons-text:1.8' implementation 'commons-cli:commons-cli:1.4' - implementation 'commons-net:commons-net:3.6' + implementation 'commons-net:commons-net:3.7' //implementation 'com.squareup.okhttp3:okhttp:4.7.2' implementation 'com.rometools:rome:1.15.0' @@ -61,13 +61,10 @@ dependencies { implementation 'net.aksingh:owm-japis:2.5.3.0' - implementation platform('org.jetbrains.kotlin:kotlin-bom') - implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8-1.4.0-rc-218' testImplementation 'org.testng:testng:7.3.0' testImplementation 'org.assertj:assertj-core:3.16.1' - testImplementation 'org.mockito:mockito-core:3.4.6' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' From 44de253040d1086a606bc75562ce7f546df15e50 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 17 Aug 2020 14:30:24 -0700 Subject: [PATCH 431/842] Upgraded to Kotlin 1.4.0. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 48 +++++++-------- .idea/modules/mobibot.test.iml | 56 +++++++++--------- build.gradle | 53 +++++++++-------- .../detekt/baseline.xml | 0 gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- gradlew.bat | 21 +------ .../net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +- version.properties | 6 +- 11 files changed, 91 insertions(+), 105 deletions(-) rename detekt-baseline.xml => config/detekt/baseline.xml (100%) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 910a915..5d44a1f 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+090" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+097" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 02b91f8..23cf1cb 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+090" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+097" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,24 +8,24 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> + <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/459999be0d6ac844dd3d2ca85c2daf14deb5f7f0/kotlin-stdlib-jdk8-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/bf0edcf669e446e0d903a0681190d1e3df969ac4/kotlin-stdlib-jdk7-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.1/f2c06572ee999f374c0849d696305d203bf9c3e/spotbugs-annotations-4.1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> <option name="jvmTarget" value="11" /> - <option name="languageVersion" value="1.3" /> - <option name="apiVersion" value="1.3" /> + <option name="languageVersion" value="1.4" /> + <option name="apiVersion" value="1.4" /> <option name="pluginOptions"> <array /> </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.72/657d8d34d91e1964b4439378c09933e840bfe8d5/kotlin-script-runtime-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.72/e09990437040879d692655d66f58a64318681ffe/kotlin-scripting-common-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.72/7dde2c909e6f1b80245c7ca100d32a8646b5666d/kotlin-scripting-jvm-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.0/52ac54a56c9121f54fcca387c5a0f441d1af9be8/kotlin-script-runtime-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.0/9c5c5503c8f96b477661789168b3f4a3c61a6c6e/kotlin-scripting-common-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.0/d807bf2ab36765af04b29319989322efd5ad1401/kotlin-scripting-jvm-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> </option> @@ -48,37 +48,37 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.1" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.3.9" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index f7f2246..28d189b 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,36 +1,38 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+090" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+097" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> - <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false"> + <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> <compilerSettings> <option name="additionalArguments" value="-Xallow-no-source-files" /> </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.72/916d54b9eb6442b615e6f1488978f551c0674720/kotlin-stdlib-jdk8-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.72/3adfc2f4ea4243e01204be8081fe63bde6b12815/kotlin-stdlib-jdk7-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.6/23f028bcf9842e9ea63d23eabe408e0fbe7432b4/spotbugs-annotations-4.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.2.0/23ba3a808f4977cebe17e53e784407f672d367fe/testng-7.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/3.4.4/6213e9c1b25a845167e27a51e6b6886b4306a920/mockito-core-3.4.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/6375e521c1e11d6563d4f25a07ce124ccf8cd171/jcommander-1.72.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.10.13/1426b15be5954246a9a72fd4baae1f42b9a4f45d/byte-buddy-1.10.13.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.10.13/f5b939963d13be46c101a3c12bf49aa23abb191/byte-buddy-agent-1.10.13.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.6/639033469776fd37c08358c6b92a4761feb2af4b/objenesis-2.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/459999be0d6ac844dd3d2ca85c2daf14deb5f7f0/kotlin-stdlib-jdk8-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/bf0edcf669e446e0d903a0681190d1e3df969ac4/kotlin-stdlib-jdk7-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.1/f2c06572ee999f374c0849d696305d203bf9c3e/spotbugs-annotations-4.1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> + <option name="jvmTarget" value="11" /> <option name="friendPaths"> <array> <option value="$MODULE_DIR$/../../build/classes/java/main" /> <option value="$MODULE_DIR$/../../build/classes/kotlin/main" /> + <option value="$MODULE_DIR$/../../build/libs/mobibot.jar" /> </array> </option> - <option name="languageVersion" value="1.3" /> - <option name="apiVersion" value="1.3" /> + <option name="languageVersion" value="1.4" /> + <option name="apiVersion" value="1.4" /> <option name="pluginOptions"> <array /> </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.72/657d8d34d91e1964b4439378c09933e840bfe8d5/kotlin-script-runtime-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.72/e09990437040879d692655d66f58a64318681ffe/kotlin-scripting-common-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.72/7dde2c909e6f1b80245c7ca100d32a8646b5666d/kotlin-scripting-jvm-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.72/6ca8bee3d88957eaaaef077c41c908c9940492d8/kotlin-stdlib-common-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.72/8032138f12c0180bc4e51fe139d4c52b46db6109/kotlin-stdlib-1.3.72.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.0/52ac54a56c9121f54fcca387c5a0f441d1af9be8/kotlin-script-runtime-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.0/9c5c5503c8f96b477661789168b3f4a3c61a6c6e/kotlin-scripting-common-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.0/d807bf2ab36765af04b29319989322efd5ad1401/kotlin-scripting-jvm-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> </option> @@ -52,47 +54,43 @@ <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="mobibot.main" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> + <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.testng:testng:7.2.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.1" level="project" /> + <orderEntry type="library" name="Gradle: org.testng:testng:7.3.0" level="project" /> <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.16.1" level="project" /> - <orderEntry type="library" name="Gradle: org.mockito:mockito-core:3.4.4" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.3.9" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.2.2" level="project" /> - <orderEntry type="library" name="Gradle: com.beust:jcommander:1.72" level="project" /> + <orderEntry type="library" name="Gradle: com.beust:jcommander:1.78" level="project" /> <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy:1.10.13" level="project" /> - <orderEntry type="library" name="Gradle: net.bytebuddy:byte-buddy-agent:1.10.13" level="project" /> - <orderEntry type="library" name="Gradle: org.objenesis:objenesis:2.6" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> diff --git a/build.gradle b/build.gradle index 270e19b..302e536 100644 --- a/build.gradle +++ b/build.gradle @@ -4,11 +4,11 @@ plugins { id 'com.github.ben-manes.versions' version '0.29.0' id 'com.github.spotbugs' version '4.5.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.11.0-RC2' + id 'io.gitlab.arturbosch.detekt' version '1.11.0' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.4.0-rc' + id 'org.jetbrains.kotlin.jvm' version '1.4.0' id 'org.sonarqube' version '3.0' id 'pmd' } @@ -36,6 +36,8 @@ dependencies { annotationProcessor semverProcessor compileOnly semverProcessor + implementation(platform("org.jetbrains.kotlin:kotlin-bom")) + implementation 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' @@ -44,11 +46,9 @@ dependencies { implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" implementation 'org.apache.commons:commons-lang3:3.11' - //implementation 'org.apache.commons:commons-text:1.8' implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.7' - //implementation 'com.squareup.okhttp3:okhttp:4.7.2' implementation 'com.rometools:rome:1.15.0' @@ -61,7 +61,7 @@ dependencies { implementation 'net.aksingh:owm-japis:2.5.3.0' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8-1.4.0-rc-218' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9' testImplementation 'org.testng:testng:7.3.0' testImplementation 'org.assertj:assertj-core:3.16.1' @@ -84,7 +84,18 @@ java { targetCompatibility = JavaVersion.VERSION_11 } -compileKotlin { +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") + options.compilerArgs += ["-Asemver.project.dir=$projectDir"] +} + +compileJava { + dependsOn('incrementBuildMeta') + options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation'] +} + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { jvmTarget = '11' } @@ -92,14 +103,17 @@ compileKotlin { spotbugs { toolVersion.set("$versions.spotbugs") -} - -tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { - reports { - xml.enabled = true - html.enabled = true - } excludeFilter.set(file("$projectDir/config/spotbugs/excludeFilter.xml")) + tasks.spotbugsMain { + reports.create("html") { + enabled = true + } + } + tasks.spotbugsTest { + reports.create("html") { + enabled = true + } + } } pmd { @@ -111,18 +125,7 @@ pmd { } detekt { - baseline = file("detekt-baseline.xml") -} - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' - options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") - options.compilerArgs += ["-Asemver.project.dir=$projectDir"] -} - -compileJava { - dependsOn('incrementBuildMeta') - options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation'] + baseline = file("${projectDir}/config/detekt/baseline.xml") } javadoc { diff --git a/detekt-baseline.xml b/config/detekt/baseline.xml similarity index 100% rename from detekt-baseline.xml rename to config/detekt/baseline.xml diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c053550b91381bbd28b1afc82d634bf73a8a..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch delta 6656 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 Yte!%o?r4GTrVtxD61Ym!|5fq-1K$0e!T<mO delta 6475 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bb8b2fc..6c9a224 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index fbd7c51..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -130,7 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 5093609..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -54,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -64,21 +64,6 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line @@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index f1d555b..e67eedf 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,14 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+085"; + public static final String VERSION = "0.8.0-beta+104"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1595378887339L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1597698016009L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String BUILDMETA = "085"; + public static final String BUILDMETA = "104"; public static final String PRERELEASE = "beta"; public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; diff --git a/version.properties b/version.properties index beae2a7..c710651 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Jun 19 18:39:50 PDT 2020 -version.buildmeta=047 +#Mon Aug 17 14:00:15 PDT 2020 +version.buildmeta=104 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+047 +version.semver=0.8.0-beta+104 From 549810b4bbef554d49ac8819bed62d2eb38b0fda Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 1 Dec 2020 00:56:52 -0800 Subject: [PATCH 432/842] Updated dependencies. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 34 ++++++------- .idea/modules/mobibot.test.iml | 36 +++++++------- build.gradle | 63 +++++++++++------------- config/pmd.xml | 27 ++++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- 6 files changed, 83 insertions(+), 81 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 5d44a1f..4e2a94b 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+097" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+122" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 23cf1cb..4c69bb2 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+097" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+122" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/459999be0d6ac844dd3d2ca85c2daf14deb5f7f0/kotlin-stdlib-jdk8-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/bf0edcf669e446e0d903a0681190d1e3df969ac4/kotlin-stdlib-jdk7-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.1/f2c06572ee999f374c0849d696305d203bf9c3e/spotbugs-annotations-4.1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> + <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.0/e3765b66f0610afc92053ff1a93a87a544fca2b/kotlin-stdlib-jdk8-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.0/9cc187c3dfaf6e4001bdf962e3cdadff7690261b/kotlin-stdlib-jdk7-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.2/9b082a619fb7deeac71684462c2804c76e0df694/spotbugs-annotations-4.1.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -48,37 +48,37 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.3.9" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 28d189b..70e2107 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+097" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+122" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.30/459999be0d6ac844dd3d2ca85c2daf14deb5f7f0/kotlin-stdlib-jdk8-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.30/bf0edcf669e446e0d903a0681190d1e3df969ac4/kotlin-stdlib-jdk7-1.3.30.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.1/f2c06572ee999f374c0849d696305d203bf9c3e/spotbugs-annotations-4.1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.16.1/6e772120aff69ca3d1583b3d1b36b7529de43ba1/assertj-core-3.16.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.0/e3765b66f0610afc92053ff1a93a87a544fca2b/kotlin-stdlib-jdk8-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.0/9cc187c3dfaf6e4001bdf962e3cdadff7690261b/kotlin-stdlib-jdk7-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.2/9b082a619fb7deeac71684462c2804c76e0df694/spotbugs-annotations-4.1.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.17.1/a0dbfb44443b97255b775602f15dfa1332ae8c57/assertj-core-3.17.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -54,43 +54,43 @@ <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="mobibot.main" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.2" level="project" /> + <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.17.1" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.3.0" level="project" /> - <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.16.1" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.30" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.3.9" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.2.2" level="project" /> <orderEntry type="library" name="Gradle: com.beust:jcommander:1.78" level="project" /> <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.30" level="project" /> <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> diff --git a/build.gradle b/build.gradle index 302e536..55950b9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ plugins { id 'application' id 'checkstyle' - id 'com.github.ben-manes.versions' version '0.29.0' - id 'com.github.spotbugs' version '4.5.0' + id 'com.github.ben-manes.versions' version '0.36.0' + id 'com.github.spotbugs' version '4.6.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.11.0' + id 'io.gitlab.arturbosch.detekt' version '1.15.0-RC1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.4.0' + id 'org.jetbrains.kotlin.jvm' version '1.4.20' id 'org.sonarqube' version '3.0' id 'pmd' } @@ -22,8 +22,10 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j : '2.13.3', - spotbugs: '4.1.1' + jacoco : '0.8.6', + log4j : '2.14.0', + pmd : '6.29.0', + spotbugs: '4.2.0' ] repositories { @@ -36,41 +38,34 @@ dependencies { annotationProcessor semverProcessor compileOnly semverProcessor - implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' + implementation(platform("org.jetbrains.kotlin:kotlin-bom")) + + implementation 'com.rometools:rome:1.15.0' + implementation 'commons-cli:commons-cli:1.4' + implementation 'commons-net:commons-net:3.7.2' + implementation 'net.aksingh:owm-japis:2.5.3.0' + implementation 'net.objecthunter:exp4j:0.4.8' + implementation 'net.thauvin.erik:pinboard-poster:1.0.1' + implementation 'org.apache.commons:commons-lang3:3.11' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2' + implementation 'org.json:json:20201115' + implementation 'org.jsoup:jsoup:1.13.1' + implementation 'org.twitter4j:twitter4j-core:4.0.7' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" - implementation 'org.apache.commons:commons-lang3:3.11' - implementation 'commons-cli:commons-cli:1.4' - - implementation 'commons-net:commons-net:3.7' - - implementation 'com.rometools:rome:1.15.0' - - implementation 'org.json:json:20200518' - implementation 'org.jsoup:jsoup:1.13.1' - implementation 'net.objecthunter:exp4j:0.4.8' - - implementation 'org.twitter4j:twitter4j-core:4.0.7' - implementation 'net.thauvin.erik:pinboard-poster:1.0.1' - - implementation 'net.aksingh:owm-japis:2.5.3.0' - - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9' - - testImplementation 'org.testng:testng:7.3.0' - testImplementation 'org.assertj:assertj-core:3.16.1' - - spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1' - spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' - compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" + + testImplementation 'org.assertj:assertj-core:3.18.1' + testImplementation 'org.testng:testng:7.3.0' + + spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0' + spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' } test { @@ -117,7 +112,7 @@ spotbugs { } pmd { - toolVersion = '6.25.0' + toolVersion = versions.pmd ignoreFailures = true ruleSets = [] ruleSetFiles = files("${projectDir}/config/pmd.xml") @@ -167,7 +162,7 @@ sonarqube { } jacoco { - toolVersion = '0.8.5' + toolVersion = versions.jacoco } jacocoTestReport { diff --git a/config/pmd.xml b/config/pmd.xml index 8d35af0..dc39dbb 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -1,31 +1,34 @@ <?xml version="1.0"?> <ruleset name="erik" - xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>Erik's Ruleset</description> <!-- BEST PRACTICES --> - <!-- <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/>--> + <rule ref="category/java/bestpractices.xml/AvoidMessageDigestField"/> + <!-- <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/> --> <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/> <rule ref="category/java/bestpractices.xml/CheckResultSet"/> <rule ref="category/java/bestpractices.xml/ConstantsInInterface"/> <rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitchStmt"/> + <rule ref="category/java/bestpractices.xml/DoubleBraceInitialization"/> <rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/> <rule ref="category/java/bestpractices.xml/GuardLogStatement"/> + <rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons" /> <!-- <rule ref="category/java/bestpractices.xml/LooseCoupling"/> --> <rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray"/> <rule ref="category/java/bestpractices.xml/MissingOverride"> <properties> - <property name="violationSuppressXPath" - value="//MethodDeclaration[@Name='hashCode' or @Name='equals' or @Name='toString']"/> + <property name="violationSuppressXPath" value="//MethodDeclaration[@Name='hashCode' or @Name='equals' or @Name='toString']"/> </properties> </rule> + <rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"/> <rule ref="category/java/bestpractices.xml/PreserveStackTrace"/> <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/> <!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/> --> <rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/> <rule ref="category/java/bestpractices.xml/SystemPrintln"/> + <rule ref="category/java/bestpractices.xml/UnusedAssignment"/> <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/> <rule ref="category/java/bestpractices.xml/UnusedImports"/> <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/> @@ -40,11 +43,12 @@ <!-- NAMING CONVENTIONS --> + <!-- <rule ref="category/java/codestyle.xml/ClassNamingConventions"/> --> <rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"/> <rule ref="category/java/codestyle.xml/GenericsNaming"/> <rule ref="category/java/codestyle.xml/LocalVariableNamingConventions"/> <rule ref="category/java/codestyle.xml/MethodNamingConventions"/> - <!-- <rule ref="category/java/codestyle.xml/PackageCase"/>--> + <!-- <rule ref="category/java/codestyle.xml/PackageCase"/> --> <!-- OTHER --> @@ -71,7 +75,9 @@ <rule ref="category/java/codestyle.xml/UnnecessaryConstructor"/> <rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName"/> <rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/> + <!-- <rule ref="category/java/codestyle.xml/UnnecessaryModifier"/> --> <rule ref="category/java/codestyle.xml/UnnecessaryReturn"/> + <!-- <rule ref="category/java/codestyle.xml/UselessParentheses"/> --> <rule ref="category/java/codestyle.xml/UselessQualifiedThis"/> @@ -87,7 +93,7 @@ <rule ref="category/java/design.xml/DataClass"/> <rule ref="category/java/design.xml/DoNotExtendJavaLangError"/> <rule ref="category/java/design.xml/ExceptionAsFlowControl"/> - <!-- <rule ref="category/java/design.xml/ExcessivePublicCount"/>--> + <!-- <rule ref="category/java/design.xml/ExcessivePublicCount"/> --> <rule ref="category/java/design.xml/FinalFieldCouldBeStatic"/> <rule ref="category/java/design.xml/ImmutableField"/> <rule ref="category/java/design.xml/LogicInversion"/> @@ -144,7 +150,7 @@ <rule ref="category/java/errorprone.xml/CloneThrowsCloneNotSupportedException"/> <rule ref="category/java/errorprone.xml/CloseResource"/> <rule ref="category/java/errorprone.xml/CompareObjectsWithEquals"/> - <rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod"/>> + <rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod"/> <rule ref="category/java/errorprone.xml/DoNotCallGarbageCollectionExplicitly"/> <rule ref="category/java/errorprone.xml/DoNotExtendJavaLangThrowable"/> <rule ref="category/java/errorprone.xml/DoNotHardCodeSDCard"/> @@ -171,7 +177,7 @@ <rule ref="category/java/errorprone.xml/MoreThanOneLogger"/> <rule ref="category/java/errorprone.xml/NonCaseLabelInSwitchStatement"/> <rule ref="category/java/errorprone.xml/NonStaticInitializer"/> - <!-- <rule ref="category/java/errorprone.xml/NullAssignment"/>--> + <!-- <rule ref="category/java/errorprone.xml/NullAssignment"/> --> <rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"/> <rule ref="category/java/errorprone.xml/ProperCloneImplementation"/> <rule ref="category/java/errorprone.xml/ProperLogger"/> @@ -221,6 +227,7 @@ <rule ref="category/java/multithreading.xml/DontCallThreadRun"/> <rule ref="category/java/multithreading.xml/DoubleCheckedLocking"/> <rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"/> + <rule ref="category/java/multithreading.xml/UnsynchronizedStaticFormatter"/> <rule ref="category/java/multithreading.xml/UseConcurrentHashMap"/> <rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"/> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6c9a224..4d9ca16 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 3bd66fb1c4c9c0412cd5beb4b94df850a98d9824 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 1 Dec 2020 00:57:35 -0800 Subject: [PATCH 433/842] Cleanup. --- .../thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java | 3 +++ .../java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java index a921c7c..6be49cd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.commands.tell; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.logging.log4j.Logger; import java.io.BufferedInputStream; @@ -49,6 +50,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; + /** * The Tell Messages Manager. * @@ -87,6 +89,7 @@ final class TellMessagesMgr { * @return The {@link TellMessage} array. */ @SuppressWarnings("unchecked") + @SuppressFBWarnings("OBJECT_DESERIALIZATION") public static List<TellMessage> load(final String file, final Logger logger) { try { try (final ObjectInput input = new ObjectInputStream( diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java index 9c32598..68550bc 100644 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java @@ -67,7 +67,7 @@ public class EntryLinkTest { } final SecureRandom r = new SecureRandom(); - for (i = 0; entryLink.getCommentsCount() > 0; i++) { + while (entryLink.getCommentsCount() > 0) { entryLink.deleteComment(r.nextInt(entryLink.getCommentsCount())); } assertThat(entryLink.hasComments()).as("hasComments()").isFalse(); From fa4457a6f6ea738a8e51d243e9c1786a3d9b83ff Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 1 Dec 2020 00:58:21 -0800 Subject: [PATCH 434/842] Version 0.8.0-beta+125 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index e67eedf..b4d9e77 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,14 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+104"; + public static final String VERSION = "0.8.0-beta+125"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1597698016009L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1606812548443L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String BUILDMETA = "104"; + public static final String BUILDMETA = "125"; public static final String PRERELEASE = "beta"; public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; diff --git a/version.properties b/version.properties index c710651..ea8fc92 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Aug 17 14:00:15 PDT 2020 -version.buildmeta=104 +#Tue Dec 01 00:49:06 PST 2020 +version.buildmeta=125 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+104 +version.semver=0.8.0-beta+125 From 9c1ab833dad32e489dd486d30553b139386177b5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 2 Dec 2020 01:48:19 -0800 Subject: [PATCH 435/842] Converted modules to Kotlin. --- .idea/codeStyles/Project.xml | 356 ++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 1 + .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 36 +- .idea/modules/mobibot.test.iml | 38 +- config/detekt/baseline.xml | 46 +++ .../mobibot/{Constants.java => Constants.kt} | 62 +-- .../net/thauvin/erik/mobibot/FeedReader.java | 107 ----- .../net/thauvin/erik/mobibot/FeedReader.kt | 85 ++++ .../net/thauvin/erik/mobibot/Mobibot.java | 12 +- .../thauvin/erik/mobibot/TwitterOAuth.java | 11 +- .../java/net/thauvin/erik/mobibot/Utils.java | 388 ------------------ .../java/net/thauvin/erik/mobibot/Utils.kt | 301 ++++++++++++++ .../erik/mobibot/commands/AbstractCommand.kt | 3 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 5 +- .../thauvin/erik/mobibot/commands/Info.java | 12 +- .../erik/mobibot/commands/tell/Tell.java | 7 +- .../erik/mobibot/entries/EntriesUtils.java | 3 +- .../erik/mobibot/modules/AbstractModule.java | 173 -------- .../erik/mobibot/modules/AbstractModule.kt | 131 ++++++ .../thauvin/erik/mobibot/modules/Calc.java | 99 ----- .../net/thauvin/erik/mobibot/modules/Calc.kt} | 68 +-- .../mobibot/modules/CurrencyConverter.java | 253 ------------ .../erik/mobibot/modules/CurrencyConverter.kt | 234 +++++++++++ .../thauvin/erik/mobibot/modules/Dice.java | 98 ----- .../net/thauvin/erik/mobibot/modules/Dice.kt | 103 +++++ .../erik/mobibot/modules/GoogleSearch.java | 147 ------- .../erik/mobibot/modules/GoogleSearch.kt | 125 ++++++ .../thauvin/erik/mobibot/modules/Joke.java | 110 ----- .../net/thauvin/erik/mobibot/modules/Joke.kt | 100 +++++ .../thauvin/erik/mobibot/modules/Lookup.java | 193 --------- .../thauvin/erik/mobibot/modules/Lookup.kt | 169 ++++++++ .../erik/mobibot/modules/ModuleException.java | 117 ------ .../erik/mobibot/modules/ModuleException.kt | 89 ++++ .../thauvin/erik/mobibot/modules/Ping.java | 95 ----- .../net/thauvin/erik/mobibot/modules/Ping.kt | 90 ++++ .../erik/mobibot/modules/RockPaperScissors.kt | 31 +- .../erik/mobibot/modules/StockQuote.java | 218 ---------- .../erik/mobibot/modules/StockQuote.kt | 214 ++++++++++ ...{ThreadedModule.java => ThreadedModule.kt} | 44 +- .../thauvin/erik/mobibot/modules/Twitter.java | 279 ------------- .../thauvin/erik/mobibot/modules/Twitter.kt | 244 +++++++++++ .../net/thauvin/erik/mobibot/modules/War.java | 18 +- .../erik/mobibot/modules/Weather2.java | 232 ----------- .../thauvin/erik/mobibot/modules/Weather2.kt | 200 +++++++++ .../erik/mobibot/modules/WorldTime.java | 247 ----------- .../thauvin/erik/mobibot/modules/WorldTime.kt | 219 ++++++++++ .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 6 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 10 - .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 6 +- .../erik/mobibot/msg/PrivateMessage.kt | 6 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 6 +- .../net/thauvin/erik/mobibot/UtilsTest.java | 2 +- .../modules/{CalcTest.java => CalcTest.kt} | 40 +- .../modules/CurrencyConverterTest.java | 73 ---- ...okupTest.java => CurrencyConverterTest.kt} | 54 ++- .../thauvin/erik/mobibot/modules/DiceTest.kt | 52 +++ .../mobibot/modules/GoogleSearchTest.java | 90 ---- .../erik/mobibot/modules/GoogleSearchTest.kt | 72 ++++ .../modules/{JokeTest.java => JokeTest.kt} | 31 +- .../{WordTimeTest.java => LookupTest.kt} | 37 +- .../mobibot/modules/ModuleExceptionTest.java | 20 +- .../modules/{PingTest.java => PingTest.kt} | 29 +- .../mobibot/modules/RockPaperScissorsTest.kt | 1 - .../erik/mobibot/modules/StockQuoteTest.java | 90 ---- .../erik/mobibot/modules/StockQuoteTest.kt | 73 ++++ .../{TwitterTest.java => TwitterTest.kt} | 57 ++- .../erik/mobibot/modules/Weather2Test.java | 80 ---- .../erik/mobibot/modules/Weather2Test.kt | 60 +++ .../erik/mobibot/modules/WordTimeTest.kt | 49 +++ 70 files changed, 3313 insertions(+), 3446 deletions(-) create mode 100644 .idea/codeStyles/Project.xml rename src/main/java/net/thauvin/erik/mobibot/{Constants.java => Constants.kt} (65%) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/FeedReader.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/FeedReader.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/Utils.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/Utils.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Calc.java rename src/{test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java => main/java/net/thauvin/erik/mobibot/modules/Calc.kt} (54%) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Dice.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Joke.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Ping.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt rename src/main/java/net/thauvin/erik/mobibot/modules/{ThreadedModule.java => ThreadedModule.kt} (64%) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt rename src/test/java/net/thauvin/erik/mobibot/modules/{CalcTest.java => CalcTest.kt} (63%) delete mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java rename src/test/java/net/thauvin/erik/mobibot/modules/{LookupTest.java => CurrencyConverterTest.kt} (56%) create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt delete mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt rename src/test/java/net/thauvin/erik/mobibot/modules/{JokeTest.java => JokeTest.kt} (69%) rename src/test/java/net/thauvin/erik/mobibot/modules/{WordTimeTest.java => LookupTest.kt} (66%) rename src/test/java/net/thauvin/erik/mobibot/modules/{PingTest.java => PingTest.kt} (75%) delete mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt rename src/test/java/net/thauvin/erik/mobibot/modules/{TwitterTest.java => TwitterTest.kt} (62%) delete mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt create mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..10683d5 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,356 @@ +<component name="ProjectCodeStyleConfiguration"> + <code_scheme name="Project" version="173"> + <JavaCodeStyleSettings> + <option name="ANNOTATION_PARAMETER_WRAP" value="2" /> + <option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" /> + <option name="ALIGN_MULTILINE_TEXT_BLOCKS" value="true" /> + <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> + <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> + <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND"> + <value> + <package name="java.awt" withSubpackages="false" static="false" /> + </value> + </option> + <option name="JD_INDENT_ON_CONTINUATION" value="true" /> + </JavaCodeStyleSettings> + <JetCodeStyleSettings> + <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> + <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> + <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> + </JetCodeStyleSettings> + <codeStyleSettings language="JAVA"> + <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" /> + <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" /> + <option name="LINE_COMMENT_ADD_SPACE" value="true" /> + <option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" /> + <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> + <option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" /> + <option name="ALIGN_MULTILINE_ASSIGNMENT" value="true" /> + <option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" /> + <option name="ALIGN_MULTILINE_THROWS_LIST" value="true" /> + <option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" /> + <option name="ALIGN_MULTILINE_METHOD_BRACKETS" value="true" /> + <option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" /> + <option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" /> + <option name="CALL_PARAMETERS_WRAP" value="5" /> + <option name="METHOD_PARAMETERS_WRAP" value="5" /> + <option name="RESOURCE_LIST_WRAP" value="1" /> + <option name="EXTENDS_LIST_WRAP" value="1" /> + <option name="THROWS_LIST_WRAP" value="1" /> + <option name="EXTENDS_KEYWORD_WRAP" value="1" /> + <option name="THROWS_KEYWORD_WRAP" value="1" /> + <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> + <option name="BINARY_OPERATION_WRAP" value="1" /> + <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" /> + <option name="TERNARY_OPERATION_WRAP" value="1" /> + <option name="FOR_STATEMENT_WRAP" value="1" /> + <option name="ARRAY_INITIALIZER_WRAP" value="1" /> + <option name="ASSIGNMENT_WRAP" value="1" /> + <option name="ASSERT_STATEMENT_WRAP" value="1" /> + <option name="IF_BRACE_FORCE" value="3" /> + <option name="DOWHILE_BRACE_FORCE" value="1" /> + <option name="WHILE_BRACE_FORCE" value="1" /> + <option name="FOR_BRACE_FORCE" value="1" /> + <option name="WRAP_LONG_LINES" value="true" /> + <option name="VARIABLE_ANNOTATION_WRAP" value="2" /> + <option name="ENUM_CONSTANTS_WRAP" value="1" /> + <indentOptions> + <option name="USE_RELATIVE_INDENTS" value="true" /> + </indentOptions> + <arrangement> + <groups> + <group> + <type>OVERRIDDEN_METHODS</type> + <order>BY_NAME</order> + </group> + </groups> + <rules> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PUBLIC>true</PUBLIC> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PROTECTED>true</PROTECTED> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PRIVATE>true</PRIVATE> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PUBLIC>true</PUBLIC> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PROTECTED>true</PROTECTED> + <STATIC>true</STATIC> + <visibility /> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PRIVATE>true</PRIVATE> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> + <STATIC>true</STATIC> + </AND> + </match> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PUBLIC>true</PUBLIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PROTECTED>true</PROTECTED> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <FINAL>true</FINAL> + <PRIVATE>true</PRIVATE> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PUBLIC>true</PUBLIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PROTECTED>true</PROTECTED> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <FIELD>true</FIELD> + <PRIVATE>true</PRIVATE> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <FIELD>true</FIELD> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> + </match> + </rule> + </section> + <section> + <rule> + <match> + <CONSTRUCTOR>true</CONSTRUCTOR> + </match> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <METHOD>true</METHOD> + <STATIC>true</STATIC> + </AND> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <METHOD>true</METHOD> + </match> + <order>BY_NAME</order> + </rule> + </section> + <section> + <rule> + <match> + <ENUM>true</ENUM> + </match> + </rule> + </section> + <section> + <rule> + <match> + <INTERFACE>true</INTERFACE> + </match> + </rule> + </section> + <section> + <rule> + <match> + <AND> + <CLASS>true</CLASS> + <STATIC>true</STATIC> + </AND> + </match> + </rule> + </section> + <section> + <rule> + <match> + <CLASS>true</CLASS> + </match> + </rule> + </section> + </rules> + </arrangement> + </codeStyleSettings> + <codeStyleSettings language="kotlin"> + <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> + <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" /> + <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> + <option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" /> + <indentOptions> + <option name="CONTINUATION_INDENT_SIZE" value="4" /> + </indentOptions> + </codeStyleSettings> + </code_scheme> +</component> \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index d91f848..23f4bb5 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ <component name="ProjectCodeStyleConfiguration"> <state> + <option name="USE_PER_PROJECT_SETTINGS" value="true" /> <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> </state> </component> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 4e2a94b..7d99ee9 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+122" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+147" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 4c69bb2..cc8a457 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+122" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+147" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.0/e3765b66f0610afc92053ff1a93a87a544fca2b/kotlin-stdlib-jdk8-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.0/9cc187c3dfaf6e4001bdf962e3cdadff7690261b/kotlin-stdlib-jdk7-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.2/9b082a619fb7deeac71684462c2804c76e0df694/spotbugs-annotations-4.1.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> + <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -20,11 +20,11 @@ </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.0/52ac54a56c9121f54fcca387c5a0f441d1af9be8/kotlin-script-runtime-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.0/9c5c5503c8f96b477661789168b3f4a3c61a6c6e/kotlin-scripting-common-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.0/d807bf2ab36765af04b29319989322efd5ad1401/kotlin-scripting-jvm-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.20/2e2bf29688a76cec111df56bc5e358c5bbc5057/kotlin-scripting-common-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.20/83704fbbe39946cc2ac6d0f07f41947abeb8dc20/kotlin-scripting-jvm-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> @@ -50,35 +50,35 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.3.9" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 70e2107..b0c7198 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+122" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+147" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.0/e3765b66f0610afc92053ff1a93a87a544fca2b/kotlin-stdlib-jdk8-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.0/9cc187c3dfaf6e4001bdf962e3cdadff7690261b/kotlin-stdlib-jdk7-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.1.2/9b082a619fb7deeac71684462c2804c76e0df694/spotbugs-annotations-4.1.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.17.1/a0dbfb44443b97255b775602f15dfa1332ae8c57/assertj-core-3.17.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.18.1/aaa02680dd92a568a4278bb40aa4a6305f632ec0/assertj-core-3.18.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -27,11 +27,11 @@ </option> <option name="pluginClasspaths"> <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.0/52ac54a56c9121f54fcca387c5a0f441d1af9be8/kotlin-script-runtime-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.0/9c5c5503c8f96b477661789168b3f4a3c61a6c6e/kotlin-scripting-common-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.0/d807bf2ab36765af04b29319989322efd5ad1401/kotlin-scripting-jvm-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.20/2e2bf29688a76cec111df56bc5e358c5bbc5057/kotlin-scripting-common-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.20/83704fbbe39946cc2ac6d0f07f41947abeb8dc20/kotlin-scripting-jvm-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> </array> @@ -56,29 +56,29 @@ <orderEntry type="module" module-name="mobibot.main" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20200518" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.1.2" level="project" /> - <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.17.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> + <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.18.1" level="project" /> <orderEntry type="library" name="Gradle: org.testng:testng:7.3.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.3.9" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> @@ -89,7 +89,7 @@ <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 93d9e37..ed4ec34 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,18 +2,64 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> + <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> + <ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> + <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$33</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID> <ID>MagicNumber:Cycle.kt$Cycle$10</ID> + <ID>MagicNumber:Dice.kt$Dice$7</ID> <ID>MagicNumber:Ignore.kt$Ignore$8</ID> <ID>MagicNumber:Modules.kt$Modules$7</ID> <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> + <ID>MagicNumber:Twitter.kt$Twitter$1000L</ID> + <ID>MagicNumber:Twitter.kt$Twitter$60L</ID> <ID>MagicNumber:Users.kt$Users$8</ID> + <ID>MagicNumber:Utils.kt$Utils.Companion$30</ID> + <ID>MagicNumber:Utils.kt$Utils.Companion$365</ID> + <ID>MagicNumber:Utils.kt$Utils.Companion$7</ID> <ID>MagicNumber:View.kt$View$8</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime$17</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> + <ID>MemberNameEqualsClassName:Calc.kt$Calc.Companion$ @JvmStatic fun calc(query: String): String</ID> + <ID>MemberNameEqualsClassName:Lookup.kt$Lookup.Companion$ @JvmStatic @Throws(UnknownHostException::class) fun lookup(query: String): String</ID> + <ID>MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): Message</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> + <ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID> + <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> + <ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> + <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> + <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> + <ID>ReturnCount:Utils.kt$Utils.Companion$ @JvmStatic fun colorize(s: String?, color: String): String</ID> + <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> + <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> + <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> + <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> + <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> + <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> + <ID>TooManyFunctions:Utils.kt$Utils$Companion</ID> </CurrentIssues> </SmellBaseline> diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.kt similarity index 65% rename from src/main/java/net/thauvin/erik/mobibot/Constants.java rename to src/main/java/net/thauvin/erik/mobibot/Constants.kt index ee86294..4f3d1ae 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.java +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.kt @@ -1,5 +1,5 @@ /* - * Constants.java + * Constants.kt * * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,81 +29,81 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot -package net.thauvin.erik.mobibot; - -import java.util.Locale; +import java.util.* /** - * The <code>Constants</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-19 - * @since 1.0 + * The `Constants` class. */ -public final class Constants { +object Constants { /** * The connect/read timeout in ms. */ - public static final int CONNECT_TIMEOUT = 5000; + const val CONNECT_TIMEOUT = 5000 + /** * Debug command line argument. */ - public static final String DEBUG_ARG = "debug"; + const val DEBUG_ARG = "debug" + /** * The debug command. */ - public static final String DEBUG_CMD = "debug"; + const val DEBUG_CMD = "debug" + /** * Default IRC Port. */ - public static final int DEFAULT_PORT = 6667; + const val DEFAULT_PORT = 6667 + /** * Default IRC Server. */ - public static final String DEFAULT_SERVER = "irc.freenode.net"; + const val DEFAULT_SERVER = "irc.freenode.net" + /** * The die command. */ - public static final String DIE_CMD = "die"; + const val DIE_CMD = "die" + /** * Help command line argument. */ - public static final String HELP_ARG = "help"; + const val HELP_ARG = "help" + /** * The help command. */ - public static final String HELP_CMD = "help"; + const val HELP_CMD = "help" + /** * The link command. */ - public static final String LINK_CMD = "L"; + const val LINK_CMD = "L" + /** * Default locale. */ - public static final Locale LOCALE = Locale.getDefault(); + val LOCALE: Locale = Locale.getDefault() + /** * The empty title string. */ - public static final String NO_TITLE = "No Title"; + const val NO_TITLE = "No Title" + /** * Properties command line argument. */ - public static final String PROPS_ARG = "properties"; + const val PROPS_ARG = "properties" + /** * The timer delay in minutes. */ - public static final long TIMER_DELAY = 10L; + const val TIMER_DELAY = 10L /** * Properties version line argument. */ - public static final String VERSION_ARG = "version"; - - /** - * Disables the default constructor. - */ - private Constants() { - throw new UnsupportedOperationException("Illegal constructor call."); - } + const val VERSION_ARG = "version" } diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java deleted file mode 100644 index 41795f3..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * FeedReader.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot; - -import com.rometools.rome.feed.synd.SyndEntry; -import com.rometools.rome.feed.synd.SyndFeed; -import com.rometools.rome.io.SyndFeedInput; -import com.rometools.rome.io.XmlReader; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; - -/** - * Reads a RSS feed. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Feb 1, 2004 - * @since 1.0 - */ -public class FeedReader implements Runnable { - // Maximum number of feed items to display - private static final int MAX_ITEMS = 5; - - // Bot - private final Mobibot bot; - - // Nick of the person who sent the message - private final String sender; - - // URL to fetch - private final String url; - - /** - * Creates a new {@link FeedReader} instance. - * - * @param bot The bot's instance. - * @param sender The nick of the person who sent the message. - * @param url The URL to fetch. - */ - public FeedReader(final Mobibot bot, final String sender, final String url) { - this.bot = bot; - this.sender = sender; - this.url = url; - } - - /** - * Fetches the Feed's items. - */ - @Override - public final void run() { - try { - final SyndFeedInput input = new SyndFeedInput(); - try (final XmlReader reader = new XmlReader(new URL(url))) { - final SyndFeed feed = input.build(reader); - - final List<SyndEntry> items = feed.getEntries(); - if (items.isEmpty()) { - bot.send(sender, "There is currently nothing to view.", false); - } else { - SyndEntry item; - for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) { - item = items.get(i); - bot.send(sender, item.getTitle(), false); - bot.send(sender, Utils.helpIndent(Utils.green(item.getLink()), false), false); - } - } - } - } catch (MalformedURLException e) { - bot.getLogger().debug("Invalid feed URL.", e); - bot.send(sender, "The feed URL is invalid.", false); - } catch (Exception e) { - bot.getLogger().debug("Unable to fetch the feed.", e); - bot.send(sender, "An error has occurred while fetching the feed: " + e.getMessage(), false); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt new file mode 100644 index 0000000..0dcdbac --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt @@ -0,0 +1,85 @@ +/* + * FeedReader.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import com.rometools.rome.io.SyndFeedInput +import com.rometools.rome.io.XmlReader +import java.net.MalformedURLException +import java.net.URL + +/** + * Reads a RSS feed. + */ +class FeedReader( + // Bot + private val bot: Mobibot, + // Nick of the person who sent the message + private val sender: String, + // URL to fetch + private val url: String +) : Runnable { + /** + * Fetches the Feed's items. + */ + override fun run() { + with(bot) { + try { + val input = SyndFeedInput() + XmlReader(URL(url)).use { reader -> + val feed = input.build(reader) + val items = feed.entries + if (items.isEmpty()) { + send(sender, "There is currently nothing to view.", false) + } else { + var i = 0 + while (i < items.size && i < MAX_ITEMS) { + send(sender, items[i].title, false) + send(sender, Utils.helpIndent(Utils.green(items[i].link), false), false) + i++ + } + } + } + } catch (e: MalformedURLException) { + logger.debug("Invalid feed URL.", e) + send(sender, "The feed URL is invalid.", false) + } catch (e: Exception) { + logger.debug("Unable to fetch the feed.", e) + send(sender, "An error has occurred while fetching the feed: ${e.message}", false) + } + } + } + + companion object { + // Maximum number of feed items to display + private const val MAX_ITEMS = 5 + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 1289405..63ec7cf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -579,7 +579,7 @@ public class Mobibot extends PircBot { */ private boolean helpModules(final String sender, final String topic, final boolean isPrivate) { for (final AbstractModule module : addons.getModules()) { - for (final String cmd : module.getCommands()) { + for (final String cmd : module.commands) { if (topic.equals(cmd)) { module.helpResponse(sender, isPrivate); return true; @@ -646,7 +646,7 @@ public class Mobibot extends PircBot { */ public final void joinChannel() { joinChannel(ircChannel); - twitter.notification("%1$s %2$s has joined %3$s"); + twitter.notification(getName() + " " + ReleaseInfo.VERSION + " has joined " + getChannel()); } /** @@ -695,7 +695,7 @@ public class Mobibot extends PircBot { } // Modules for (final AbstractModule module : addons.getModules()) { // modules - for (final String c : module.getCommands()) { + for (final String c : module.commands) { if (cmd.startsWith(c)) { module.commandResponse(sender, cmd, args, false); return; @@ -739,7 +739,7 @@ public class Mobibot extends PircBot { if (cmd.startsWith(Constants.HELP_CMD)) { // help helpResponse(sender, args, true); } else if (isOp && "kill".equals(cmd)) { // kill - twitter.notification("%1$s killed by " + sender + " on %3$s"); + twitter.notification(getName() + " killed by " + sender + " on " + getChannel()); sendRawLine("QUIT : Poof!"); System.exit(0); } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug @@ -753,7 +753,7 @@ public class Mobibot extends PircBot { send(sender + " has just signed my death sentence."); TIMER.cancel(); twitter.shutdown(); - twitter.notification("%1$s stopped by " + sender + " on %3$s"); + twitter.notification(getName() + " stopped by " + sender + " on " + getChannel()); sleep(3); quitServer("The Bot Is Out There!"); System.exit(0); @@ -766,7 +766,7 @@ public class Mobibot extends PircBot { } for (final AbstractModule module : addons.getModules()) { if (module.isPrivateMsgEnabled()) { - for (final String c : module.getCommands()) { + for (final String c : module.commands) { if (cmd.equals(c)) { module.commandResponse(sender, cmd, args, true); return; diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 7d807d0..dbc7cb9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -69,8 +69,8 @@ public final class TwitterOAuth { * @throws TwitterException If an error occurs. * @throws IOException If an IO error occurs. */ - @SuppressFBWarnings({"DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE"}) - @SuppressWarnings({"PMD.AvoidPrintStackTrace", "PMD.SystemPrintln"}) + @SuppressFBWarnings({ "DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE" }) + @SuppressWarnings({ "PMD.AvoidPrintStackTrace", "PMD.SystemPrintln" }) public static void main(final String[] args) throws TwitterException, IOException { if (args.length == 2) { final twitter4j.Twitter twitter = new TwitterFactory().getInstance(); @@ -91,9 +91,10 @@ public final class TwitterOAuth { } System.out.println( - "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" - + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" - + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken.getTokenSecret()); + "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" + + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" + + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken + .getTokenSecret()); } catch (TwitterException te) { if (401 == te.getStatusCode()) { System.out.println("Unable to get the access token."); diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java deleted file mode 100644 index 69bd6a9..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Utils.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.apache.commons.lang3.StringUtils; -import org.jibble.pircbot.Colors; -import org.jsoup.Jsoup; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -/** - * Miscellaneous utilities class. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-26 - * @since 1.0 - */ -public final class Utils { - private static final String[] searchFlags = { "%c", "%n" }; - - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private Utils() { - throw new UnsupportedOperationException("Illegal constructor call."); - } - - /** - * Makes the given int bold. - * - * @param i The int. - * @return The bold string. - */ - public static String bold(final int i) { - return bold(Integer.toString(i)); - } - - /** - * Makes the given string bold. - * - * @param s The string. - * @return The bold string. - */ - public static String bold(final String s) { - return colorize(s, Colors.BOLD); - } - - /** - * Colorize a string. - * - * @param s The string. - * @param color The color. - * @return The colorized string. - */ - static String colorize(final String s, final String color) { - if (s == null) { - return Colors.NORMAL; - } else if (Colors.BOLD.equals(color) || Colors.REVERSE.equals(color)) { - return color + s + color; - } - - return color + s + Colors.NORMAL; - } - - /** - * Makes the given string cyan. - * - * @param s The string. - * @return The cyan string. - */ - public static String cyan(final String s) { - return colorize(s, Colors.CYAN); - } - - /** - * URL encodes the given string. - * - * @param s The string to encode. - * @return The encoded string. - */ - public static String encodeUrl(final String s) { - return URLEncoder.encode(s, StandardCharsets.UTF_8); - } - - /** - * Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory. - * - * @param location The File or URL location. - * @param isUrl Set to true if the location is a URL - * @return The location ending with a slash. - */ - static String ensureDir(final String location, final boolean isUrl) { - if (isUrl) { - if (location.charAt(location.length() - 1) == '/') { - return location; - } else { - return location + '/'; - } - } else { - if (location.charAt(location.length() - 1) == File.separatorChar) { - return location; - } else { - return location + File.separatorChar; - } - } - } - - /** - * Returns a property as an int. - * - * @param property The property value. - * @param def The default property value. - * @return The port or default value if invalid. - */ - public static int getIntProperty(final String property, final int def) { - int prop; - - try { - prop = Integer.parseInt(property); - } catch (NumberFormatException ignore) { - prop = def; - } - - return prop; - } - - /** - * Makes the given string green. - * - * @param s The string. - * @return The green string. - */ - public static String green(final String s) { - return colorize(s, Colors.DARK_GREEN); - } - - /** - * Formats a help command by replacing {@code %c} with the bot's pub/priv command, and {@code %n} with the bot's - * nick. - * - * @param text The help command text. - * @param botNick The bot's nick. - * @param isPrivate The private flag. - * @return The formatted help command. - */ - public static String helpFormat(final String text, final String botNick, final boolean isPrivate) { - final String[] replace = { (isPrivate) ? "/msg " + botNick : botNick + ':', botNick }; - return StringUtils.replaceEach(text, searchFlags, replace); - } - - /** - * Returns indented help string. - * - * @param help The help string. - * @return The indented help string. - * @see #helpIndent(String, boolean) - */ - public static String helpIndent(final String help) { - return helpIndent(help, true); - } - - /** - * Returns indented help string. - * - * @param help The help string. - * @param isBold The bold flag. - * @return The indented help string. - */ - public static String helpIndent(final String help, final boolean isBold) { - return " " + (isBold ? bold(help) : help); - } - - /** - * Returns the specified date as an ISO local date string. - * - * @param date The date. - * @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format. - */ - public static String isoLocalDate(final Date date) { - return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); - } - - /** - * Returns the specified date as an ISO local date string. - * - * @param date The date. - * @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format. - */ - public static String isoLocalDate(final LocalDateTime date) { - return date.format(DateTimeFormatter.ISO_LOCAL_DATE); - } - - /** - * Obfuscates the given string. - * - * @param s The string. - * @return The obfuscated string. - */ - public static String obfuscate(final String s) { - if (StringUtils.isNotBlank(s)) { - return StringUtils.repeat('x', s.length()); - } - return s; - } - - /** - * Returns the plural form of a word, if count > 1. - * - * @param count The count. - * @param word The word. - * @param plural The plural word. - * @return The plural string. - */ - public static String plural(final long count, final String word, final String plural) { - if (count > 1) { - return plural; - } else { - return word; - } - } - - /** - * Makes the given string red. - * - * @param s The string. - * @return The red string. - */ - public static String red(final String s) { - return colorize(s, Colors.RED); - } - - /** - * Makes the given string reverse color. - * - * @param s The string. - * @return The reverse color string. - */ - public static String reverseColor(final String s) { - return colorize(s, Colors.REVERSE); - } - - /** - * Returns today's date. - * - * @return Today's date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format. - */ - public static String today() { - return isoLocalDate(LocalDateTime.now()); - } - - /** - * Converts XML/XHTML entities to plain text. - * - * @param str The string to unescape. - * @return The unescaped string. - */ - public static String unescapeXml(final String str) { - return Jsoup.parse(str).text(); - } - - /** - * Converts milliseconds to year month week day hour and minutes. - * - * @param uptime The uptime in milliseconds. - * @return The uptime in year month week day hours and minutes. - */ - public static String uptime(final long uptime) { - final StringBuilder info = new StringBuilder(); - - long days = TimeUnit.MILLISECONDS.toDays(uptime); - final long years = days / 365; - days %= 365; - final long months = days / 30; - days %= 30; - final long weeks = days / 7; - days %= 7; - final long hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours( - TimeUnit.MILLISECONDS.toDays(uptime)); - final long minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( - TimeUnit.MILLISECONDS.toHours(uptime)); - - if (years > 0) { - info.append(years).append(plural(years, " year ", " years ")); - } - - if (months > 0) { - info.append(weeks).append(plural(months, " month ", " months ")); - } - - if (weeks > 0) { - info.append(weeks).append(plural(weeks, " week ", " weeks ")); - } - - - if (days > 0) { - info.append(days).append(plural(days, " day ", " days ")); - } - - if (hours > 0) { - info.append(hours).append(plural(hours, " hour ", " hours ")); - } - - info.append(minutes).append(plural(minutes, " minute", " minutes")); - - return info.toString(); - } - - /** - * Reads contents of a URL. - * - * @param url The URL to read. - * @return The URL contents. - * @throws IOException If an IO error occurs. - */ - @SuppressFBWarnings("SECSSSRFUC") - public static String urlReader(final URL url) throws IOException { - try (final BufferedReader reader = new BufferedReader( - new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { - return reader.lines().collect(Collectors.joining(System.lineSeparator())); - } - } - - /** - * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>. - * - * @param date The date. - * @return The formatted date. - */ - public static String utcDateTime(final Date date) { - return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); - } - - /** - * Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>. - * - * @param date The date. - * @return The formatted date. - */ - public static String utcDateTime(final LocalDateTime date) { - return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); - } - -} diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt new file mode 100644 index 0000000..c1cae4b --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -0,0 +1,301 @@ +/* + * Utils.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import org.apache.commons.lang3.StringUtils +import org.jibble.pircbot.Colors +import org.jsoup.Jsoup +import java.io.BufferedReader +import java.io.File +import java.io.IOException +import java.io.InputStreamReader +import java.net.URL +import java.net.URLEncoder +import java.nio.charset.StandardCharsets +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.util.* +import java.util.concurrent.TimeUnit +import java.util.stream.Collectors + +/** + * Miscellaneous utilities class. + */ +class Utils private constructor() { + companion object { + private val searchFlags = arrayOf("%c", "%n") + + /** + * Makes the given int bold. + */ + @JvmStatic + fun bold(i: Int): String { + return bold(i.toString()) + } + + /** + * Makes the given string bold. + */ + @JvmStatic + fun bold(s: String?): String { + return colorize(s, Colors.BOLD) + } + + /** + * Colorize a string. + */ + @JvmStatic + fun colorize(s: String?, color: String): String { + if (s == null) { + return Colors.NORMAL + } else if (Colors.BOLD == color || Colors.REVERSE == color) { + return color + s + color + } + return color + s + Colors.NORMAL + } + + /** + * Makes the given string cyan. + */ + @JvmStatic + fun cyan(s: String?): String { + return colorize(s, Colors.CYAN) + } + + /** + * URL encodes the given string. + */ + fun encodeUrl(s: String?): String { + return URLEncoder.encode(s, StandardCharsets.UTF_8) + } + + /** + * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. + */ + @JvmStatic + fun ensureDir(location: String, isUrl: Boolean): String { + return if (location.isNotEmpty()) { + if (isUrl) { + if (location[location.length - 1] == '/') { + location + } else { + "$location/" + } + } else { + if (location[location.length - 1] == File.separatorChar) { + location + } else { + location + File.separatorChar + } + } + } else { + location + } + } + + /** + * Returns a property as an int. + */ + @JvmStatic + fun getIntProperty(property: String, def: Int): Int { + return try { + property.toInt() + } catch (ignore: NumberFormatException) { + def + } + } + + /** + * Makes the given string green. + */ + @JvmStatic + fun green(s: String?): String { + return colorize(s, Colors.DARK_GREEN) + } + + /** + * Formats a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's + * nick. + */ + @JvmStatic + fun helpFormat(text: String?, botNick: String, isPrivate: Boolean): String { + val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) + return StringUtils.replaceEach(text, searchFlags, replace) + } + + /** + * Returns indented help string. + */ + @JvmStatic + @JvmOverloads + fun helpIndent(help: String?, isBold: Boolean = true): String { + return " " + if (isBold) bold(help) else help + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun isoLocalDate(date: Date): String { + return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun isoLocalDate(date: LocalDateTime): String { + return date.format(DateTimeFormatter.ISO_LOCAL_DATE) + } + + /** + * Obfuscates the given string. + */ + @JvmStatic + fun obfuscate(s: String?): String { + return if (s!!.isNotBlank()) { + StringUtils.repeat('x', s.length) + } else s + } + + /** + * Returns the plural form of a word, if count > 1. + */ + @JvmStatic + fun plural(count: Long, word: String, plural: String): String { + return if (count > 1) { + plural + } else { + word + } + } + + /** + * Makes the given string red. + */ + @JvmStatic + fun red(s: String?): String { + return colorize(s, Colors.RED) + } + + /** + * Makes the given string reverse color. + */ + @JvmStatic + fun reverseColor(s: String?): String { + return colorize(s, Colors.REVERSE) + } + + /** + * Returns today's date. + */ + @JvmStatic + fun today(): String { + return isoLocalDate(LocalDateTime.now()) + } + + /** + * Converts XML/XHTML entities to plain text. + */ + @JvmStatic + fun unescapeXml(str: String?): String { + return Jsoup.parse(str).text() + } + + /** + * Converts milliseconds to year month week day hour and minutes. + */ + @JvmStatic + fun uptime(uptime: Long): String { + val info = StringBuilder() + var days = TimeUnit.MILLISECONDS.toDays(uptime) + val years = days / 365 + days %= 365 + val months = days / 30 + days %= 30 + val weeks = days / 7 + days %= 7 + val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours( + TimeUnit.MILLISECONDS.toDays(uptime) + ) + val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( + TimeUnit.MILLISECONDS.toHours(uptime) + ) + with(info) { + if (years > 0) { + append(years).append(plural(years, " year ", " years ")) + } + if (months > 0) { + append(weeks).append(plural(months, " month ", " months ")) + } + if (weeks > 0) { + append(weeks).append(plural(weeks, " week ", " weeks ")) + } + if (days > 0) { + append(days).append(plural(days, " day ", " days ")) + } + if (hours > 0) { + append(hours).append(plural(hours, " hour ", " hours ")) + } + append(minutes).append(plural(minutes, " minute", " minutes")) + return toString() + } + } + + /** + * Reads contents of a URL. + */ + @JvmStatic + @Throws(IOException::class) + fun urlReader(url: URL): String { + BufferedReader(InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) + .use { reader -> return reader.lines().collect(Collectors.joining(System.lineSeparator())) } + } + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun utcDateTime(date: Date): String { + return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) + } + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun utcDateTime(date: LocalDateTime): String { + return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index df19332..3168dde 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -63,9 +63,10 @@ abstract class AbstractCommand(val bot: Mobibot) { return false } - open fun getProperty(key: String) : String? { + open fun getProperty(key: String): String? { return properties[key] } + open fun getPropertyKeys(): Set<String> { return properties.keys } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index dbccdd6..1ca4ed2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -38,10 +38,7 @@ import net.thauvin.erik.mobibot.Utils class Cycle(bot: Mobibot) : AbstractCommand(bot) { private val wait = 10 override val name = "cycle" - override val help = listOf( - "To have the bot leave the channel and come back:", - Utils.helpIndent("%c $name") - ) + override val help = listOf("To have the bot leave the channel and come back:", Utils.helpIndent("%c $name")) override val isOp = true override val isPublic = false override val isVisible = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index aed639c..1282d78 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -48,7 +48,7 @@ public class Info extends AbstractCommand { + " (" + Utils.green(ReleaseInfo.WEBSITE) + ')', "Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')'); - public Info(@NotNull final Mobibot bot) { + public Info(final Mobibot bot) { super(bot); } @@ -97,18 +97,14 @@ public class Info extends AbstractCommand { if (isOp) { if (getBot().getTell().isEnabled()) { - info.append(", Messages: ") - .append(getBot().getTell().size()); + info.append(", Messages: ").append(getBot().getTell().size()); } if (getBot().getTwitter().isAutoPost()) { - info.append(", Twitter: ") - .append(getBot().getTwitter().entriesCount()); + info.append(", Twitter: ").append(getBot().getTwitter().entriesCount()); } } - info.append(", Recap: ") - .append(Recap.recapCount()) - .append(']'); + info.append(", Recap: ").append(Recap.recapCount()).append(']'); getBot().send(sender, info.toString(), isPrivate); } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index 05efbec..8470320 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -173,7 +173,8 @@ public class Tell extends AbstractCommand { Utils.helpIndent("%c " + TELL_CMD + " <nick> <message>"), "To view queued and sent messages:", Utils.helpIndent("%c " + TELL_CMD + ' ' + View.VIEW_CMD), - "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days.")); + "Messages are kept for " + Utils.bold(maxDays) + + Utils.plural(maxDays, " day.", " days.")); } @Override @@ -383,8 +384,8 @@ public class Tell extends AbstractCommand { isPrivate)), isPrivate); getBot().send(sender, - "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."), - isPrivate); + "Messages are kept for " + Utils.bold(maxDays) + + Utils.plural(maxDays, " day.", " days."), isPrivate); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java index f0485db..e114d66 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java @@ -84,7 +84,8 @@ public final class EntriesUtils { * @param isView Set to true to display the number of comments. * @return The entry's link. */ - @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.") + @SuppressFBWarnings(value = "CE_CLASS_ENVY", + justification = "Yes, it does.") public static String buildLink(final int index, final EntryLink entry, final boolean isView) { final StringBuilder buff = new StringBuilder().append(Constants.LINK_CMD).append(index + 1) .append(": ").append('[').append(entry.getNick()).append(']'); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java deleted file mode 100644 index f8483af..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * AbstractModule.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * The <code>Module</code> abstract class. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2016-07-01 - * @since 1.0 - */ -public abstract class AbstractModule { - final Mobibot bot; - final List<String> commands = new ArrayList<>(); - final List<String> help = new ArrayList<>(); - final Map<String, String> properties = new ConcurrentHashMap<>(); - - AbstractModule(final Mobibot bot) { - this.bot = bot; - } - - /** - * Responds to a command. - * - * @param sender The sender. - * @param cmd The command. - * @param args The command arguments. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - public abstract void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate); - - /** - * Returns the module's commands, if any. - * - * @return The commands. - */ - public List<String> getCommands() { - return commands; - } - - /** - * Returns the module's property keys. - * - * @return The keys. - */ - public Set<String> getPropertyKeys() { - return properties.keySet(); - } - - /** - * Returns <code>true</code> if the module has properties. - * - * @return <code>true</code> or <code>false</code> . - */ - public boolean hasProperties() { - return !properties.isEmpty(); - } - - /** - * Responds with the module's help. - * - * @param sender The sender. - * @param isPrivate Set to <code>true</code> if the response should be sent as a private message. - */ - public void helpResponse(final String sender, final boolean isPrivate) { - for (final String h : help) { - bot.send(sender, Utils.helpFormat(h, bot.getNick(), isPrivateMsgEnabled() && isPrivate), isPrivate); - } - } - - /** - * Initializes the properties. - * - * @param keys The properties keys to initialize. - */ - public void initProperties(final String... keys) { - for (final String key : keys) { - properties.put(key, ""); - } - } - - /** - * Returns <code>true</code> if the module is enabled. - * - * @return <code>true</code> or <code>false</code> - */ - public boolean isEnabled() { - if (hasProperties()) { - return isValidProperties(); - } else { - return true; - } - } - - /** - * Returns <code>true</code> if the module responds to private messages. - * - * @return <code>true</code> or <code>false</code> - */ - public boolean isPrivateMsgEnabled() { - return false; - } - - /** - * Ensures that all properties have values. - * - * @return <code>true</code> if the properties are valid, <code>false</code> otherwise. - */ - boolean isValidProperties() { - for (final String s : getPropertyKeys()) { - if (StringUtils.isBlank(properties.get(s))) { - return false; - } - } - - return true; - } - - /** - * Sets a property key and value. - * - * @param key The key. - * @param value The value. - */ - public void setProperty(final String key, final String value) { - if (StringUtils.isNotBlank(key)) { - properties.put(key, value); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt new file mode 100644 index 0000000..dd40a8c --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -0,0 +1,131 @@ +/* + * AbstractModule.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import org.apache.commons.lang3.StringUtils +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +/** + * The `Module` abstract class. + */ +abstract class AbstractModule(val bot: Mobibot) { + /** + * The module's commands, if any. + */ + @JvmField + val commands: MutableList<String> = ArrayList() + + @JvmField + val help: MutableList<String> = ArrayList() + val properties: MutableMap<String, String> = ConcurrentHashMap() + + /** + * Responds to a command. + */ + abstract fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) + + /** + * Returns the module's property keys. + */ + val propertyKeys: Set<String> + get() = properties.keys + + /** + * Returns `true` if the module has properties. + */ + fun hasProperties(): Boolean { + return properties.isNotEmpty() + } + + /** + * Responds with the module's help. + */ + open fun helpResponse(sender: String, isPrivate: Boolean) { + for (h in help) { + bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) + } + } + + /** + * Initializes the properties. + */ + fun initProperties(vararg keys: String) { + for (key in keys) { + properties[key] = "" + } + } + + /** + * Returns `true` if the module is enabled. + */ + val isEnabled: Boolean + get() = if (hasProperties()) { + isValidProperties + } else { + true + } + + /** + * Returns `true` if the module responds to private messages. + */ + open val isPrivateMsgEnabled: Boolean = false + + /** + * Ensures that all properties have values. + */ + open val isValidProperties: Boolean + get() { + for (s in propertyKeys) { + if (StringUtils.isBlank(properties[s])) { + return false + } + } + return true + } + + /** + * Sets a property key and value. + */ + fun setProperty(key: String, value: String) { + if (StringUtils.isNotBlank(key)) { + properties[key] = value + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java deleted file mode 100644 index 862711f..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Calc.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.objecthunter.exp4j.Expression; -import net.objecthunter.exp4j.ExpressionBuilder; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import org.apache.commons.lang3.StringUtils; - -import java.text.DecimalFormat; - -/** - * The Calc module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2016-07-01 - * @since 1.0 - */ -public class Calc extends AbstractModule { - // Calc command - private static final String CALC_CMD = "calc"; - - /** - * The default constructor. - */ - public Calc(final Mobibot bot) { - super(bot); - - commands.add(CALC_CMD); - - help.add("To solve a mathematical calculation:"); - help.add(Utils.helpIndent("%c " + CALC_CMD + " <calculation>")); - } - - /** - * Performs a calculation. - * - * <p>1 + 1 * 2</p> - * - * @param query The query. - * @return The calculation result. - */ - static String calc(final String query) { - final DecimalFormat decimalFormat = new DecimalFormat("#.##"); - - try { - final Expression calc = new ExpressionBuilder(query).build(); - return query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate())); - } catch (Exception e) { - return "No idea. This is the kind of math I don't get."; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - if (StringUtils.isNotBlank(args)) { - bot.send(calc(args)); - } else { - helpResponse(sender, isPrivate); - } - } -} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt similarity index 54% rename from src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java rename to src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt index 5eab364..22a6c7e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,5 +1,5 @@ /* - * AbstractModuleTest.java + * Calc.kt * * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,41 +29,53 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -import static org.assertj.core.api.Assertions.assertThat; +import net.objecthunter.exp4j.ExpressionBuilder +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import org.apache.commons.lang3.StringUtils +import java.text.DecimalFormat /** - * The <code>AbstractModuleTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 + * The Calc module. */ - -final class AbstractModuleTest { - private AbstractModuleTest() { - throw new UnsupportedOperationException("Illegal constructor call."); +class Calc(bot: Mobibot) : AbstractModule(bot) { + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + if (StringUtils.isNotBlank(args)) { + bot.send(calc(args)) + } else { + helpResponse(sender, isPrivate) + } } - @SuppressFBWarnings("CE_CLASS_ENVY") - static void testAbstractModule(final AbstractModule module) { - final String name = module.getClass().getName(); + companion object { + // Calc command + private const val CALC_CMD = "calc" - assertThat(module.isEnabled()).as(name + ": enabled").isNotEqualTo(module.hasProperties()); - assertThat(module.getCommands().size()).as(name + ": commands > 0").isGreaterThan(0); - if (!module.hasProperties()) { - assertThat(module.getPropertyKeys().size()).as(name + ": no properties").isEqualTo(0); - module.setProperty("test", "test"); - module.setProperty("", "invalid"); + /** + * Performs a calculation. e.g.: 1 + 1 * 2 + */ + @JvmStatic + fun calc(query: String): String { + val decimalFormat = DecimalFormat("#.##") + return try { + val calc = ExpressionBuilder(query).build() + query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate())) + } catch (e: IllegalArgumentException) { + "No idea. This is the kind of math I don't get." + } } + } - assertThat(module.getPropertyKeys().size()).as(name + ": properties > 0").isGreaterThan(0); - - module.setProperty("invalid", ""); - assertThat(module.isValidProperties()).as(name + ": invalid properties").isFalse(); + init { + commands.add(CALC_CMD) + help.add("To solve a mathematical calculation:") + help.add(Utils.helpIndent("%c $CALC_CMD <calculation>")) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java deleted file mode 100644 index 2794395..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * CurrencyConverter.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Constants; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.msg.ErrorMessage; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.PublicMessage; -import org.apache.commons.lang3.StringUtils; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; -import org.jdom2.Namespace; -import org.jdom2.input.SAXBuilder; - -import javax.xml.XMLConstants; -import java.io.IOException; -import java.net.URL; -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import static org.apache.commons.lang3.StringUtils.upperCase; - -/** - * The CurrentConverter module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Feb 11, 2004 - * @since 1.0 - */ -@SuppressWarnings("PMD.UseConcurrentHashMap") -public final class CurrencyConverter extends ThreadedModule { - // Currency command - private static final String CURRENCY_CMD = "currency"; - // Rates keyword - private static final String CURRENCY_RATES_KEYWORD = "rates"; - // Empty rate table. - private static final String EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty."; - // Exchange rates - private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>(); - // Exchange rates table URL - private static final String EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"; - // Last exchange rates table publication date - private static String pubDate = ""; - - /** - * Creates a new {@link CurrencyConverter} instance. - */ - public CurrencyConverter(final Mobibot bot) { - super(bot); - - commands.add(CURRENCY_CMD); - } - - /** - * Converts from a currency to another. - * - * <p>100 USD to EUR</p> - * - * @param query The query. - * @return The {@link Message} contained the converted currency. - */ - static Message convertCurrency(final String query) { - final String[] cmds = query.split(" "); - - if (cmds.length == 4) { - if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) { - return new PublicMessage("You're kidding, right?"); - } else { - final String to = upperCase(cmds[1]); - final String from = upperCase(cmds[3]); - - if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) { - try { - final double amt = Double.parseDouble(cmds[0].replace(",", "")); - final double doubleFrom = Double.parseDouble(EXCHANGE_RATES.get(to)); - final double doubleTo = Double.parseDouble(EXCHANGE_RATES.get(from)); - - return new PublicMessage( - NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1) - + ' ' - + upperCase(cmds[1]) - + " = " - + NumberFormat.getCurrencyInstance(Constants.LOCALE) - .format((amt * doubleTo) / doubleFrom) - .substring(1) - + ' ' - + upperCase(cmds[3])); - } catch (NumberFormatException e) { - return new ErrorMessage("Let's try with some real numbers next time, okay?"); - } - } else { - return new ErrorMessage("Sounds like monopoly money to me!"); - } - } - } - return new ErrorMessage("Invalid query. Let's try again."); - } - - static List<String> currencyRates() { - final List<String> rates = new ArrayList<>(33); - for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) { - rates.add(" " + rate.getKey() + ": " + StringUtils.leftPad(rate.getValue(), 8)); - } - - return rates; - } - - static void loadRates() throws ModuleException { - if (EXCHANGE_RATES.isEmpty()) { - try { - final SAXBuilder builder = new SAXBuilder(); - // See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755 - builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); - builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); - builder.setIgnoringElementContentWhitespace(true); - - final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL)); - final Element root = doc.getRootElement(); - final Namespace ns = root.getNamespace(""); - final Element cubeRoot = root.getChild("Cube", ns); - final Element cubeTime = cubeRoot.getChild("Cube", ns); - - pubDate = cubeTime.getAttribute("time").getValue(); - - final List<Element> cubes = cubeTime.getChildren(); - - for (final Element cube : cubes) { - EXCHANGE_RATES.put( - cube.getAttribute("currency").getValue(), - cube.getAttribute("rate").getValue()); - } - - EXCHANGE_RATES.put("EUR", "1"); - } catch (JDOMException | IOException e) { - throw new ModuleException(e.getMessage(), - "An error has occurred while parsing the exchange rates table.", - e); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - synchronized (this) { - if (!pubDate.equals(Utils.today())) { - EXCHANGE_RATES.clear(); - } - } - - super.commandResponse(sender, cmd, args, isPrivate); - } - - /** - * Converts the specified currencies. - */ - @SuppressFBWarnings("REDOS") - @Override - void run(final String sender, final String cmd, final String query, final boolean isPrivate) { - if (EXCHANGE_RATES.isEmpty()) { - try { - loadRates(); - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - } - } - - if (EXCHANGE_RATES.isEmpty()) { - bot.send(sender, EMPTY_RATE_TABLE, true); - } else if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) { - final Message msg = convertCurrency(query); - bot.send(sender, msg); - if (msg.isError()) { - helpResponse(sender, isPrivate); - } - } else if (query.contains(CURRENCY_RATES_KEYWORD)) { - bot.send(sender, "The currency rates for " + Utils.bold(pubDate) + " are:", isPrivate); - bot.sendList(sender, currencyRates(), 3, isPrivate, false); - } else { - helpResponse(sender, isPrivate); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final String sender, final boolean isPrivate) { - if (EXCHANGE_RATES.isEmpty()) { - try { - loadRates(); - } catch (ModuleException e) { - bot.getLogger().debug(e.getDebugMessage(), e); - } - } - if (EXCHANGE_RATES.isEmpty()) { - bot.send(sender, EMPTY_RATE_TABLE, isPrivate); - } else { - bot.send(sender, "To convert from one currency to another:", isPrivate); - bot.send(sender, - Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR", - bot.getNick(), isPrivateMsgEnabled())), isPrivate); - bot.send(sender, "For a listing of current rates:", isPrivate); - bot.send(sender, - Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + ' ' + CURRENCY_RATES_KEYWORD, - bot.getNick(), isPrivateMsgEnabled())), isPrivate); - bot.send(sender, "The supported currencies are: ", isPrivate); - bot.sendList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt new file mode 100644 index 0000000..c72e73d --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -0,0 +1,234 @@ +/* + * CurrencyConverter.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.apache.commons.lang3.StringUtils +import org.jdom2.JDOMException +import org.jdom2.input.SAXBuilder +import java.io.IOException +import java.net.URL +import java.text.NumberFormat +import java.util.* +import javax.xml.XMLConstants + +/** + * The CurrentConverter module. + */ +class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + synchronized(this) { + if (pubDate != Utils.today()) { + EXCHANGE_RATES.clear() + } + } + super.commandResponse(sender, cmd, args, isPrivate) + } + + /** + * Converts the specified currencies. + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + with(bot) { + if (EXCHANGE_RATES.isEmpty()) { + try { + loadRates() + } catch (e: ModuleException) { + logger.warn(e.debugMessage, e) + } + } + + if (EXCHANGE_RATES.isEmpty()) { + send(sender, EMPTY_RATE_TABLE, true) + } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { + val msg = convertCurrency(args) + send(sender, msg) + if (msg.isError) { + helpResponse(sender, isPrivate) + } + } else if (args.contains(CURRENCY_RATES_KEYWORD)) { + send(sender, "The currency rates for ${Utils.bold(pubDate)} are:", isPrivate) + sendList(sender, currencyRates(), 3, isPrivate, false) + } else { + helpResponse(sender, isPrivate) + } + } + } + + override fun helpResponse(sender: String, isPrivate: Boolean) { + with(bot) { + if (EXCHANGE_RATES.isEmpty()) { + try { + loadRates() + } catch (e: ModuleException) { + logger.debug(e.debugMessage, e) + } + } + if (EXCHANGE_RATES.isEmpty()) { + send(sender, EMPTY_RATE_TABLE, isPrivate) + } else { + send(sender, "To convert from one currency to another:", isPrivate) + send( + sender, + Utils.helpIndent( + Utils.helpFormat("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled) + ), + isPrivate + ) + send(sender, "For a listing of current rates:", isPrivate) + send( + sender, + Utils.helpIndent( + Utils.helpFormat("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) + ), + isPrivate + ) + send(sender, "The supported currencies are: ", isPrivate) + sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, false) + } + } + } + + companion object { + // Currency command + private const val CURRENCY_CMD = "currency" + + // Rates keyword + private const val CURRENCY_RATES_KEYWORD = "rates" + + // Empty rate table. + private const val EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty." + + // Exchange rates + private val EXCHANGE_RATES: MutableMap<String, String> = TreeMap() + + // Exchange rates table URL + private const val EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml" + + // Last exchange rates table publication date + private var pubDate = "" + + /** + * Converts from a currency to another. + */ + @JvmStatic + fun convertCurrency(query: String): Message { + val cmds = query.split(" ").toTypedArray() + return if (cmds.size == 4) { + if (cmds[3] == cmds[1] || "0" == cmds[0]) { + PublicMessage("You're kidding, right?") + } else { + val to = StringUtils.upperCase(cmds[1]) + val from = StringUtils.upperCase(cmds[3]) + if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) { + try { + val amt = cmds[0].replace(",", "").toDouble() + val doubleFrom = EXCHANGE_RATES[to]!!.toDouble() + val doubleTo = EXCHANGE_RATES[from]!!.toDouble() + PublicMessage( + NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1) + + " ${StringUtils.upperCase(cmds[1])} = " + + NumberFormat.getCurrencyInstance(Constants.LOCALE) + .format(amt * doubleTo / doubleFrom).substring(1) + + " ${StringUtils.upperCase(cmds[3])}" + ) + } catch (e: NumberFormatException) { + ErrorMessage("Let's try with some real numbers next time, okay?") + } + } else { + ErrorMessage("Sounds like monopoly money to me!") + } + } + } else ErrorMessage("Invalid query. Let's try again.") + } + + @JvmStatic + fun currencyRates(): List<String> { + val rates = ArrayList<String>(33) + for ((key, value) in EXCHANGE_RATES) { + rates.add(" $key: ${StringUtils.leftPad(value, 8)}") + } + return rates + } + + @JvmStatic + @Throws(ModuleException::class) + fun loadRates() { + if (EXCHANGE_RATES.isEmpty()) { + try { + val builder = SAXBuilder() + // See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755 + builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "") + builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "") + builder.ignoringElementContentWhitespace = true + val doc = builder.build(URL(EXCHANGE_TABLE_URL)) + val root = doc.rootElement + val ns = root.getNamespace("") + val cubeRoot = root.getChild("Cube", ns) + val cubeTime = cubeRoot.getChild("Cube", ns) + pubDate = cubeTime.getAttribute("time").value + val cubes = cubeTime.children + for (cube in cubes) { + EXCHANGE_RATES[cube.getAttribute("currency").value] = cube.getAttribute("rate").value + } + EXCHANGE_RATES["EUR"] = "1" + } catch (e: JDOMException) { + throw ModuleException( + e.message, + "An JDOM parsing error has occurred while parsing the exchange rates table.", + e + ) + } catch (e: IOException) { + throw ModuleException( + e.message, + "An IO error has occurred while parsing the exchange rates table.", + e + ) + } + } + } + } + + init { + commands.add(CURRENCY_CMD) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java deleted file mode 100644 index 861a244..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Dice.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; - -import java.security.SecureRandom; - -import static net.thauvin.erik.mobibot.Utils.bold; - -/** - * The Dice module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-28 - * @since 1.0 - */ -public final class Dice extends AbstractModule { - // Dice command - private static final String DICE_CMD = "dice"; - - /** - * The default constructor. - */ - public Dice(final Mobibot bot) { - super(bot); - - commands.add(DICE_CMD); - - help.add("To roll the dice:"); - help.add(Utils.helpIndent("%c " + DICE_CMD)); - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - final SecureRandom r = new SecureRandom(); - - int i = r.nextInt(6) + 1; - int y = r.nextInt(6) + 1; - final int playerTotal = i + y; - - bot.send(bot.getChannel(), - sender + " rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " - + bold(playerTotal), isPrivate); - - i = r.nextInt(6) + 1; - y = r.nextInt(6) + 1; - final int total = i + y; - - bot.action( - "rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " + bold(total)); - - if (playerTotal < total) { - bot.action("wins."); - } else if (playerTotal > total) { - bot.action("lost."); - } else { - bot.action("tied."); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt new file mode 100644 index 0000000..4f6a314 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt @@ -0,0 +1,103 @@ +/* + * Dice.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import kotlin.random.Random + +/** + * The Dice module. + */ +class Dice(bot: Mobibot) : AbstractModule(bot) { + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + val roll = roll() + val playerRoll = roll() + val total = roll.first + roll.second + val playerTotal = playerRoll.first + playerRoll.second + with(bot) { + send( + channel, + "$sender rolled two dice: ${Utils.bold(playerRoll.first)} and ${Utils.bold(playerRoll.second)}" + + " for a total of ${Utils.bold(playerTotal)}", + isPrivate + ) + action( + "rolled two dice: ${Utils.bold(roll.first)} and ${Utils.bold(roll.second)}" + + " for a total of ${Utils.bold(total)}" + ) + when (winLoseOrTie(total, playerTotal)) { + Result.WIN -> action("wins.") + Result.LOSE -> action("lost.") + else -> action("tied.") + } + } + } + + enum class Result { + WIN, LOSE, TIE + } + + private fun roll(): Pair<Int, Int> { + return Pair(Random.nextInt(1, 7), Random.nextInt(1, 7)) + } + + companion object { + // Dice command + private const val DICE_CMD = "dice" + + fun winLoseOrTie(bot: Int, player: Int): Result { + return when { + bot > player -> { + Result.WIN + } + bot < player -> { + Result.LOSE + } + else -> { + Result.TIE + } + } + } + } + + init { + commands.add(DICE_CMD) + help.add("To roll the dice:") + help.add(Utils.helpIndent("%c $DICE_CMD")) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java deleted file mode 100644 index 478a64e..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * GoogleSearch.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.NoticeMessage; -import org.apache.commons.lang3.StringUtils; -import org.jibble.pircbot.Colors; -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -/** - * The GoogleSearch module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Feb 7, 2004 - * @since 1.0 - */ -public final class GoogleSearch extends ThreadedModule { - // Google API Key property - static final String GOOGLE_API_KEY_PROP = "google-api-key"; - // Google Custom Search Engine ID property - static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx"; - // Google command - private static final String GOOGLE_CMD = "google"; - - /** - * Creates a new {@link GoogleSearch} instance. - */ - public GoogleSearch(final Mobibot bot) { - super(bot); - - commands.add(GOOGLE_CMD); - - help.add("To search Google:"); - help.add(Utils.helpIndent("%c " + GOOGLE_CMD + " <query>")); - - initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP); - } - - /** - * Performs a search on Google. - * - * @param query The search query. - * @param apiKey The Google API key. - * @param cseKey The Google CSE key. - * @return The {@link Message} array containing the search results. - * @throws ModuleException If an error occurs while searching. - */ - @SuppressFBWarnings({ "URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION" }) - @SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops")) - static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey) - throws ModuleException { - if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) { - throw new ModuleException(StringUtils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing."); - } - - if (StringUtils.isNotBlank(query)) { - final ArrayList<Message> results = new ArrayList<>(); - try { - final URL url = - new URL("https://www.googleapis.com/customsearch/v1?key=" - + apiKey - + "&cx=" - + cseKey - + "&q=" - + Utils.encodeUrl(query) - + "&filter=1&num=5&alt=json"); - - final JSONObject json = new JSONObject(Utils.urlReader(url)); - final JSONArray ja = json.getJSONArray("items"); - - for (int i = 0; i < ja.length(); i++) { - final JSONObject j = ja.getJSONObject(i); - results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title")))); - results.add( - new NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN)); - } - } catch (IOException e) { - throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e); - } - - return results; - } else { - throw new ModuleException("Invalid query. Please try again."); - } - } - - /** - * Searches Google. - */ - @Override - void run(final String sender, final String cmd, final String query, final boolean isPrivate) { - if (StringUtils.isNotBlank(query)) { - try { - final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP), - properties.get(GOOGLE_CSE_KEY_PROP)); - for (final Message msg : results) { - bot.send(sender, msg); - } - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage(), isPrivate); - } - } else { - helpResponse(sender, isPrivate); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt new file mode 100644 index 0000000..b05a0cb --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -0,0 +1,125 @@ +/* + * GoogleSearch.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import org.apache.commons.lang3.StringUtils +import org.jibble.pircbot.Colors +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.net.URL +import java.util.* + +/** + * The GoogleSearch module. + */ +class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { + /** + * Searches Google. + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + with(bot) { + if (StringUtils.isNotBlank(args)) { + try { + val results = searchGoogle( + args, properties[GOOGLE_API_KEY_PROP], + properties[GOOGLE_CSE_KEY_PROP] + ) + for (msg in results) { + send(sender, msg) + } + } catch (e: ModuleException) { + logger.warn(e.debugMessage, e) + send(sender, e.message, isPrivate) + } + } else { + helpResponse(sender, isPrivate) + } + } + } + + companion object { + // Google API Key property + const val GOOGLE_API_KEY_PROP = "google-api-key" + + // Google Custom Search Engine ID property + const val GOOGLE_CSE_KEY_PROP = "google-cse-cx" + + // Google command + private const val GOOGLE_CMD = "google" + + /** + * Performs a search on Google. + */ + @JvmStatic + @Throws(ModuleException::class) + fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message> { + if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) { + throw ModuleException("${StringUtils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.") + } + return if (StringUtils.isNotBlank(query)) { + val results = ArrayList<Message>() + try { + val url = URL( + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + + "&q=${Utils.encodeUrl(query)}&filter=1&num=5&alt=json" + ) + val json = JSONObject(Utils.urlReader(url)) + val ja = json.getJSONArray("items") + for (i in 0 until ja.length()) { + val j = ja.getJSONObject(i) + results.add(NoticeMessage(Utils.unescapeXml(j.getString("title")))) + results.add(NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN)) + } + } catch (e: IOException) { + throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e) + } catch (e: JSONException) { + throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e) + } + results + } else { + throw ModuleException("Invalid query. Please try again.") + } + } + } + + init { + commands.add(GOOGLE_CMD) + help.add("To search Google:") + help.add(Utils.helpIndent("%c $GOOGLE_CMD <query>")) + initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java deleted file mode 100644 index 23ed888..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Joke.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.PublicMessage; -import org.json.JSONObject; - -import java.net.URL; - -/** - * The Joke module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-20 - * @since 1.0 - */ -public final class Joke extends ThreadedModule { - // Joke command - private static final String JOKE_CMD = "joke"; - // ICNDB URL - private static final String JOKE_URL = - "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"; - - /** - * Creates a new {@link Joke} instance. - */ - public Joke(final Mobibot bot) { - super(bot); - - commands.add(JOKE_CMD); - - help.add("To retrieve a random joke:"); - help.add(Utils.helpIndent("%c " + JOKE_CMD)); - } - - /** - * Retrieves a random joke. - * - * @return The {@link Message} containing the new joke. - * @throws ModuleException If an error occurs while retrieving a new joke. - */ - static Message randomJoke() throws ModuleException { - try { - final URL url = new URL(JOKE_URL); - final JSONObject json = new JSONObject(Utils.urlReader(url)); - return new PublicMessage( - json.getJSONObject("value").get("joke").toString().replace("\\'", "'") - .replace("\\\"", "\"")); - } catch (Exception e) { - throw new ModuleException("randomJoke()", "An error has occurred retrieving a random joke.", e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - new Thread(() -> run(sender, cmd, args, isPrivate)).start(); - } - - /** - * Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>. - */ - @Override - void run(final String sender, final String cmd, final String arg, final boolean isPrivate) { - try { - bot.send(Utils.cyan(randomJoke().getMsg())); - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage(), isPrivate); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt new file mode 100644 index 0000000..722ab75 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt @@ -0,0 +1,100 @@ +/* + * Joke.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.net.URL + +/** + * The Joke module. + */ +class Joke(bot: Mobibot) : ThreadedModule(bot) { + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + Thread { run(sender, cmd, args, isPrivate) }.start() + } + + /** + * Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/). + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) = try { + bot.send(Utils.cyan(randomJoke().msg)) + } catch (e: ModuleException) { + bot.logger.warn(e.debugMessage, e) + bot.send(sender, e.message, isPrivate) + } + + companion object { + // Joke command + private const val JOKE_CMD = "joke" + + // ICNDB URL + private const val JOKE_URL = + "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]" + + /** + * Retrieves a random joke. + */ + @JvmStatic + @Throws(ModuleException::class) + fun randomJoke(): Message { + return try { + val url = URL(JOKE_URL) + val json = JSONObject(Utils.urlReader(url)) + PublicMessage( + json.getJSONObject("value")["joke"].toString().replace("\\'", "'") + .replace("\\\"", "\"") + ) + } catch (e: IOException) { + throw ModuleException("randomJoke()", "An IO error has occurred retrieving a random joke.", e) + } catch (e: JSONException) { + throw ModuleException("randomJoke()", "An JSON error has occurred retrieving a random joke.", e) + } + } + } + + init { + commands.add(JOKE_CMD) + help.add("To retrieve a random joke:") + help.add(Utils.helpIndent("%c $JOKE_CMD")) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java deleted file mode 100644 index f97284d..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Lookup.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Constants; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import org.apache.commons.net.whois.WhoisClient; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * The Lookup module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-26 - * @since 1.0 - */ -public final class Lookup extends AbstractModule { - /** - * The whois default host. - */ - static final String WHOIS_HOST = "whois.arin.net"; - - // Lookup command - private static final String LOOKUP_CMD = "lookup"; - - /** - * The default constructor. - */ - public Lookup(final Mobibot bot) { - super(bot); - - commands.add(LOOKUP_CMD); - - help.add("To perform a DNS lookup query:"); - help.add(Utils.helpIndent("%c " + LOOKUP_CMD + " <ip address or hostname>")); - } - - /** - * Performs a DNS lookup on the specified query. - * - * @param query The IP address or hostname. - * @return The lookup query result string. - * @throws java.net.UnknownHostException If the host is unknown. - */ - public static String lookup(final String query) - throws UnknownHostException { - final StringBuilder buffer = new StringBuilder(); - - final InetAddress[] results = InetAddress.getAllByName(query); - String hostInfo; - - for (final InetAddress result : results) { - if (result.getHostAddress().equals(query)) { - hostInfo = result.getHostName(); - - if (hostInfo.equals(query)) { - throw new UnknownHostException(); - } - } else { - hostInfo = result.getHostAddress(); - } - - if (buffer.length() > 0) { - buffer.append(", "); - } - - buffer.append(hostInfo); - } - - return buffer.toString(); - } - - /** - * Performs a whois IP query. - * - * @param query The IP address. - * @return The IP whois data, if any. - * @throws java.io.IOException If a connection error occurs. - */ - private static String[] whois(final String query) - throws IOException { - return whois(query, WHOIS_HOST); - } - - /** - * Performs a whois IP query. - * - * @param query The IP address. - * @param host The whois host. - * @return The IP whois data, if any. - * @throws java.io.IOException If a connection error occurs. - */ - public static String[] whois(final String query, final String host) - throws IOException { - final WhoisClient whoisClient = new WhoisClient(); - final String[] lines; - - try { - whoisClient.setDefaultTimeout(Constants.CONNECT_TIMEOUT); - whoisClient.connect(host); - whoisClient.setSoTimeout(Constants.CONNECT_TIMEOUT); - whoisClient.setSoLinger(false, 0); - - if (WHOIS_HOST.equals(host)) { - lines = whoisClient.query("n - " + query).split("\n"); - } else { - lines = whoisClient.query(query).split("\n"); - } - } finally { - whoisClient.disconnect(); - } - - return lines; - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - if (args.matches("(\\S.)+(\\S)+")) { - try { - bot.send(Lookup.lookup(args)); - } catch (UnknownHostException ignore) { - if (args.matches( - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." - + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) { - try { - final String[] lines = Lookup.whois(args); - - if ((lines != null) && (lines.length > 0)) { - String line; - - for (final String rawLine : lines) { - line = rawLine.trim(); - - if ((line.length() > 0) && (line.charAt(0) != '#')) { - bot.send(line); - } - } - } else { - bot.send("Unknown host."); - } - } catch (IOException ioe) { - bot.getLogger().debug("Unable to perform whois IP lookup: {}", args, ioe); - bot.send("Unable to perform whois IP lookup: " + ioe.getMessage()); - } - } else { - bot.send("Unknown host."); - } - } - } else { - helpResponse(sender, true); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt new file mode 100644 index 0000000..cca419e --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -0,0 +1,169 @@ +/* + * Lookup.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import org.apache.commons.net.whois.WhoisClient +import java.io.IOException +import java.net.InetAddress +import java.net.UnknownHostException + +/** + * The Lookup module. + */ +class Lookup(bot: Mobibot) : AbstractModule(bot) { + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + if (args.matches("(\\S.)+(\\S)+".toRegex())) { + with(bot) { + try { + send(lookup(args)) + } catch (ignore: UnknownHostException) { + if (args.matches( + ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex() + ) + ) { + try { + val lines = whois(args) + if (lines.isNotEmpty()) { + var line: String + for (rawLine in lines) { + line = rawLine.trim() + if (line.isNotEmpty() && line[0] != '#') { + send(line) + } + } + } else { + send("Unknown host.") + } + } catch (ioe: IOException) { + logger.debug("Unable to perform whois IP lookup: {}", args, ioe) + send("Unable to perform whois IP lookup: ${ioe.message}") + } + } else { + send("Unknown host.") + } + } + } + } else { + helpResponse(sender, true) + } + } + + companion object { + /** + * The whois default host. + */ + const val WHOIS_HOST = "whois.arin.net" + + // Lookup command + private const val LOOKUP_CMD = "lookup" + + /** + * Performs a DNS lookup on the specified query. + */ + @JvmStatic + @Throws(UnknownHostException::class) + fun lookup(query: String): String { + val buffer = StringBuilder() + val results = InetAddress.getAllByName(query) + var hostInfo: String + for (result in results) { + if (result.hostAddress == query) { + hostInfo = result.hostName + if (hostInfo == query) { + throw UnknownHostException() + } + } else { + hostInfo = result.hostAddress + } + if (buffer.isNotEmpty()) { + buffer.append(", ") + } + buffer.append(hostInfo) + } + return buffer.toString() + } + + /** + * Performs a whois IP query. + * + * @param query The IP address. + * @return The IP whois data, if any. + * @throws java.io.IOException If a connection error occurs. + */ + @Throws(IOException::class) + private fun whois(query: String): Array<String> { + return whois(query, WHOIS_HOST) + } + + /** + * Performs a whois IP query. + */ + @JvmStatic + @Throws(IOException::class) + fun whois(query: String, host: String): Array<String> { + val whoisClient = WhoisClient() + val lines: Array<String> + with(whoisClient) { + try { + defaultTimeout = Constants.CONNECT_TIMEOUT + connect(host) + soTimeout = Constants.CONNECT_TIMEOUT + setSoLinger(false, 0) + lines = if (WHOIS_HOST == host) { + query("n - $query").split("\n").toTypedArray() + } else { + query(query).split("\n").toTypedArray() + } + } finally { + disconnect() + } + } + return lines + } + } + + init { + commands.add(LOOKUP_CMD) + help.add("To perform a DNS lookup query:") + help.add(Utils.helpIndent("%c $LOOKUP_CMD <ip address or hostname>")) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java deleted file mode 100644 index 13ab2fa..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * ModuleException.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Utils; -import org.apache.commons.lang3.StringUtils; - -/** - * The <code>ModuleException</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 - */ -public class ModuleException extends Exception { - private static final long serialVersionUID = 1L; - - private final String debugMessage; - - /** - * Creates a new exception. - * - * @param message The exception message. - */ - ModuleException(final String message) { - super(message); - this.debugMessage = message; - } - - /** - * Creates a new exception. - * - * @param debugMessage The debug message. - * @param message The exception message. - * @param cause The cause. - */ - ModuleException(final String debugMessage, final String message, final Throwable cause) { - super(message, cause); - this.debugMessage = debugMessage; - } - - /** - * Creates a new exception. - * - * @param debugMessage The debug message. - * @param message The exception message. - */ - ModuleException(final String debugMessage, final String message) { - super(message); - this.debugMessage = debugMessage; - } - - /** - * Returns the debug message. - * - * @return The debug message. - */ - String getDebugMessage() { - return debugMessage; - } - - /** - * Return the sanitized message (e.g. remove API keys, etc.) - * - * @param sanitize The words to sanitize. - * @return The sanitized message. - */ - String getSanitizedMessage(final String... sanitize) { - final String[] obfuscate = new String[sanitize.length]; - for (int i = 0; i < sanitize.length; i++) { - obfuscate[i] = Utils.obfuscate(sanitize[i]); - } - return getCause().getClass().getName() + ": " + StringUtils.replaceEach(getCause().getMessage(), - sanitize, - obfuscate); - } - - /** - * Return <code>true</code> if the exception has a cause. - * - * @return <code>true</code> or <code>false</code> - */ - @SuppressWarnings("unused") - boolean hasCause() { - return getCause() != null; - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt new file mode 100644 index 0000000..3b10ada --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -0,0 +1,89 @@ +/* + * ModuleException.kit + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils +import org.apache.commons.lang3.StringUtils + +/** + * The `ModuleException` class. + */ +class ModuleException : Exception { + /** + * Returns the debug message. + */ + val debugMessage: String? + + /** + * Creates a new exception. + */ + constructor(message: String?) : super(message) { + debugMessage = message + } + + /** + * Creates a new exception. + */ + constructor(debugMessage: String?, message: String?, cause: Throwable?) : super(message, cause) { + this.debugMessage = debugMessage + } + + /** + * Creates a new exception. + */ + constructor(debugMessage: String?, message: String?) : super(message) { + this.debugMessage = debugMessage + } + + /** + * Return the sanitized message (e.g. remove API keys, etc.) + */ + fun getSanitizedMessage(vararg sanitize: String?): String { + val obfuscate = arrayOfNulls<String>(sanitize.size) + for (i in sanitize.indices) { + obfuscate[i] = Utils.obfuscate(sanitize[i]) + } + return cause!!.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate) + } + + /** + * Return `true` if the exception has a cause. + */ + @Suppress("unused") + fun hasCause(): Boolean { + return cause != null + } + + companion object { + private const val serialVersionUID = 1L + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java deleted file mode 100644 index 2e10363..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Ping.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; - -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.List; - -/** - * The Ping module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2016-07-02 - * @since 1.0 - */ -public class Ping extends AbstractModule { - /** - * The ping responses. - */ - static final List<String> PINGS = - Arrays.asList( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!"); - /** - * The ping command. - */ - private static final String PING_CMD = "ping"; - - /** - * The default constructor. - */ - public Ping(final Mobibot bot) { - super(bot); - - commands.add(PING_CMD); - - help.add("To ping the bot:"); - help.add(Utils.helpIndent("%c " + PING_CMD)); - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - final SecureRandom r = new SecureRandom(); - bot.action(PINGS.get(r.nextInt(PINGS.size()))); - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt new file mode 100644 index 0000000..980ece9 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt @@ -0,0 +1,90 @@ +/* + * Ping.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import kotlin.random.Random + +/** + * The Ping module. + */ +class Ping(bot: Mobibot) : AbstractModule(bot) { + /** + * {@inheritDoc} + */ + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + bot.action(randomPing()) + } + + companion object { + /** + * The ping responses. + */ + @JvmField + val PINGS = listOf( + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!" + ) + + @JvmStatic + fun randomPing(): String { + return PINGS[Random.nextInt(PINGS.size)] + } + + /** + * The ping command. + */ + private const val PING_CMD = "ping" + } + + init { + commands.add(PING_CMD) + help.add("To ping the bot:") + help.add(Utils.helpIndent("%c $PING_CMD")) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 68bf1e3..9bcc1ad 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -34,9 +34,6 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.green -import net.thauvin.erik.mobibot.Utils.red import kotlin.random.Random @@ -96,21 +93,23 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { } } - override fun commandResponse(sender: String, cmd: String, args: String?, isPrivate: Boolean) { + override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) { val hand = Hands.valueOf(cmd.toUpperCase()) val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] - when { - hand == botHand -> { - bot.send("${green(hand.name)} vs. ${green(botHand.name)}") - bot.action("tied.") - } - hand.beats(botHand) -> { - bot.send("${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)}") - bot.action("lost.") - } - else -> { - bot.send("${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)}") - bot.action("wins.") + with(bot) { + when { + hand == botHand -> { + send("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)}") + action("tied.") + } + hand.beats(botHand) -> { + send("${Utils.green(hand.name)} ${Utils.bold(hand.action)} ${Utils.red(botHand.name)}") + action("lost.") + } + else -> { + send("${Utils.green(botHand.name)} ${Utils.bold(botHand.action)} ${Utils.red(hand.name)}") + action("wins.") + } } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java deleted file mode 100644 index 4e5c1c9..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * StockQuote.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.msg.ErrorMessage; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.NoticeMessage; -import net.thauvin.erik.mobibot.msg.PublicMessage; -import org.apache.commons.lang3.StringUtils; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -/** - * The StockQuote module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Feb 7, 2004 - * @since 1.0 - */ -public final class StockQuote extends ThreadedModule { - /** - * The Alpha Advantage property key. - */ - static final String ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key"; - /** - * The Invalid Symbol error string. - */ - static final String INVALID_SYMBOL = "Invalid symbol."; - // Alpha Advantage URL - private static final String ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function="; - // Quote command - private static final String STOCK_CMD = "stock"; - - /** - * Creates a new {@link StockQuote} instance. - */ - public StockQuote(final Mobibot bot) { - super(bot); - commands.add(STOCK_CMD); - - help.add("To retrieve a stock quote:"); - help.add(Utils.helpIndent("%c " + STOCK_CMD + " <symbol|keywords>")); - - initProperties(ALPHAVANTAGE_API_KEY_PROP); - } - - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", - justification = "false positive?") - private static JSONObject getJsonResponse(final String response, final String debugMessage) - throws ModuleException { - if (StringUtils.isNotBlank(response)) { - final JSONObject json = new JSONObject(response); - - try { - final String info = json.getString("Information"); - if (!info.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(info)); - } - } catch (JSONException ignore) { - // Do nothing - } - - try { - final String error = json.getString("Note"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); - } - } catch (JSONException ignore) { - // Do nothing - } - - try { - final String error = json.getString("Error Message"); - if (!error.isEmpty()) { - throw new ModuleException(debugMessage, Utils.unescapeXml(error)); - } - } catch (JSONException ignore) { - // Do nothing - } - - return json; - } else { - throw new ModuleException(debugMessage, "Empty Response."); - } - } - - /** - * Retrieves a stock quote. - * - * @param symbol The stock symbol. - * @return The {@link Message} array containing the stock quote. - * @throws ModuleException If an errors occurs. - */ - @SuppressWarnings({ "PMD.CloseResource" }) - static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException { - if (StringUtils.isBlank(apiKey)) { - throw new ModuleException(StringUtils.capitalize(STOCK_CMD) + " is disabled. The API key is missing."); - } - - if (StringUtils.isNotBlank(symbol)) { - final String debugMessage = "getQuote(" + symbol + ')'; - final ArrayList<Message> messages = new ArrayList<>(); - - String response; - try { - // Search for symbol/keywords - response = Utils.urlReader(new URL( - ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey=" - + Utils.encodeUrl(apiKey))); - - JSONObject json = getJsonResponse(response, debugMessage); - - final JSONArray symbols = json.getJSONArray("bestMatches"); - if (symbols.isEmpty()) { - messages.add(new ErrorMessage(INVALID_SYMBOL)); - return messages; - } - - final JSONObject symbolInfo = symbols.getJSONObject(0); - - // Get quote for symbol - response = Utils.urlReader(new URL( - ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + Utils.encodeUrl(symbolInfo.getString("1. symbol")) - + "&apikey=" + Utils.encodeUrl(apiKey))); - - json = getJsonResponse(response, debugMessage); - - final JSONObject quote = json.getJSONObject("Global Quote"); - - if (quote.isEmpty()) { - messages.add(new ErrorMessage(INVALID_SYMBOL)); - return messages; - } - - messages.add(new PublicMessage( - "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils.unescapeXml( - symbolInfo.getString("2. name") + ']'))); - messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))); - messages.add(new PublicMessage( - " Previous: " - + Utils.unescapeXml(quote.getString("08. previous close")))); - messages.add(new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))); - messages.add(new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))); - messages.add(new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))); - messages.add(new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume")))); - messages.add(new NoticeMessage( - " Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day")))); - messages.add(new NoticeMessage( - " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils.unescapeXml( - quote.getString("10. change percent")) + ']')); - } catch (IOException | NullPointerException e) { - throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e); - } - return messages; - } else { - throw new ModuleException(INVALID_SYMBOL); - } - } - - /** - * Returns the specified stock quote from Alpha Avantage. - */ - @Override - void run(final String sender, final String cmd, final String symbol, final boolean isPrivate) { - if (StringUtils.isNotBlank(symbol)) { - try { - final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP)); - for (final Message msg : messages) { - bot.send(sender, msg); - } - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(e.getMessage()); - } - } else { - helpResponse(sender, isPrivate); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt new file mode 100644 index 0000000..bcf1838 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -0,0 +1,214 @@ +/* + * StockQuote.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.apache.commons.lang3.StringUtils +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.net.URL +import java.util.* + +/** + * The StockQuote module. + */ +class StockQuote(bot: Mobibot) : ThreadedModule(bot) { + /** + * Returns the specified stock quote from Alpha Avantage. + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + with(bot) { + if (StringUtils.isNotBlank(args)) { + try { + val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) + for (msg in messages) { + send(sender, msg) + } + } catch (e: ModuleException) { + logger.warn(e.debugMessage, e) + send(e.message) + } + } else { + helpResponse(sender, isPrivate) + } + } + } + + companion object { + /** + * The Alpha Advantage property key. + */ + const val ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key" + + /** + * The Invalid Symbol error string. + */ + const val INVALID_SYMBOL = "Invalid symbol." + + // Alpha Advantage URL + private const val ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=" + + // Quote command + private const val STOCK_CMD = "stock" + + @Throws(ModuleException::class) + private fun getJsonResponse(response: String, debugMessage: String): JSONObject { + return if (StringUtils.isNotBlank(response)) { + val json = JSONObject(response) + try { + val info = json.getString("Information") + if (info.isNotEmpty()) { + throw ModuleException(debugMessage, Utils.unescapeXml(info)) + } + } catch (ignore: JSONException) { + // Do nothing + } + try { + var error = json.getString("Note") + if (error.isNotEmpty()) { + throw ModuleException(debugMessage, Utils.unescapeXml(error)) + } + error = json.getString("Error Message") + if (error.isNotEmpty()) { + throw ModuleException(debugMessage, Utils.unescapeXml(error)) + } + } catch (ignore: JSONException) { + // Do nothing + } + json + } else { + throw ModuleException(debugMessage, "Empty Response.") + } + } + + /** + * Retrieves a stock quote. + */ + @JvmStatic + @Throws(ModuleException::class) + fun getQuote(symbol: String, apiKey: String?): List<Message> { + if (StringUtils.isBlank(apiKey)) { + throw ModuleException("${STOCK_CMD.capitalize()} is disabled. The API key is missing.") + } + return if (StringUtils.isNotBlank(symbol)) { + val debugMessage = "getQuote($symbol)" + val messages = ArrayList<Message>() + var response: String + try { + with(messages) { + // Search for symbol/keywords + response = Utils.urlReader( + URL( + "${ALAPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey=" + + Utils.encodeUrl(apiKey) + ) + ) + var json = getJsonResponse(response, debugMessage) + val symbols = json.getJSONArray("bestMatches") + if (symbols.isEmpty) { + messages.add(ErrorMessage(INVALID_SYMBOL)) + } else { + val symbolInfo = symbols.getJSONObject(0) + + // Get quote for symbol + response = Utils.urlReader( + URL( + "${ALAPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" + + Utils.encodeUrl(symbolInfo.getString("1. symbol")) + + "&apikey=" + Utils.encodeUrl(apiKey) + ) + ) + json = getJsonResponse(response, debugMessage) + val quote = json.getJSONObject("Global Quote") + if (quote.isEmpty) { + add(ErrorMessage(INVALID_SYMBOL)) + return messages + } + add( + PublicMessage( + "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + + " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']' + ) + ) + add(PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))) + add( + PublicMessage( + " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")) + ) + ) + add(NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))) + add(NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))) + add(NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))) + add( + NoticeMessage( + " Volume: " + Utils.unescapeXml(quote.getString("06. volume")) + ) + ) + add( + NoticeMessage( + " Latest: " + + Utils.unescapeXml(quote.getString("07. latest trading day")) + ) + ) + add( + NoticeMessage( + " Change: " + Utils.unescapeXml(quote.getString("09. change")) + + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']' + ) + ) + } + } + } catch (e: IOException) { + throw ModuleException(debugMessage, "An IO error has occurred retrieving a stock quote.", e) + } catch (e: NullPointerException) { + throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e) + } + messages + } else { + throw ModuleException(INVALID_SYMBOL) + } + } + } + + init { + commands.add(STOCK_CMD) + help.add("To retrieve a stock quote:") + help.add(Utils.helpIndent("%c $STOCK_CMD <symbol|keywords>")) + initProperties(ALPHAVANTAGE_API_KEY_PROP) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt similarity index 64% rename from src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java rename to src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt index 11d3cec..97a07e5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt @@ -29,40 +29,34 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Mobibot; +import net.thauvin.erik.mobibot.Mobibot /** - * The <code>ThreadedModule</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-03 - * @since 1.0 + * The `ThreadedModule` class. */ -public abstract class ThreadedModule extends AbstractModule { - ThreadedModule(final Mobibot bot) { - super(bot); - } - - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - if (isEnabled() && args.length() > 0) { - new Thread(() -> run(sender, cmd, args, isPrivate)).start(); +abstract class ThreadedModule(bot: Mobibot) : AbstractModule(bot) { + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + if (isEnabled && args.isNotEmpty()) { + Thread { run(sender, cmd, args, isPrivate) }.start() } else { - helpResponse(sender, isPrivate); + helpResponse(sender, isPrivate) } } /** * Runs the thread. */ - abstract void run(String sender, - @SuppressWarnings("unused") String cmd, - String args, - boolean isPrivate); + abstract fun run( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java deleted file mode 100644 index 87446a5..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Twitter.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Constants; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.ReleaseInfo; -import net.thauvin.erik.mobibot.TwitterTimer; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.commands.links.LinksMgr; -import net.thauvin.erik.mobibot.entries.EntryLink; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.NoticeMessage; -import org.apache.commons.lang3.StringUtils; -import twitter4j.DirectMessage; -import twitter4j.Status; -import twitter4j.TwitterFactory; -import twitter4j.conf.ConfigurationBuilder; - -import java.util.HashSet; -import java.util.Set; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -/** - * The Twitter module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Sept 10, 2008 - * @since 1.0 - */ -public final class Twitter extends ThreadedModule { - // Property keys - static final String AUTOPOST_PROP = "twitter-auto-post"; - static final String CONSUMER_KEY_PROP = "twitter-consumerKey"; - static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret"; - static final String HANDLE_PROP = "twitter-handle"; - static final String TOKEN_PROP = "twitter-token"; - static final String TOKEN_SECRET_PROP = "twitter-tokenSecret"; - // Twitter command - private static final String TWITTER_CMD = "twitter"; - - // Twitter auto-posts. - private final Set<Integer> entries = new HashSet<>(); - - /** - * Creates a new {@link Twitter} instance. - */ - public Twitter(final Mobibot bot) { - super(bot); - - commands.add(TWITTER_CMD); - - help.add("To post to Twitter:"); - help.add(Utils.helpIndent("%c " + TWITTER_CMD + " <message>")); - - properties.put(AUTOPOST_PROP, "false"); - initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP); - } - - /** - * Posts on Twitter. - * - * @param consumerKey The consumer key. - * @param consumerSecret The consumer secret. - * @param token The token. - * @param tokenSecret The token secret. - * @param handle The Twitter handle (dm) or nickname. - * @param message The message to post. - * @param isDm The direct message flag. - * @return The confirmation {@link Message}. - * @throws ModuleException If an error occurs while posting. - */ - static Message twitterPost(final String consumerKey, - final String consumerSecret, - final String token, - final String tokenSecret, - final String handle, - final String message, - final boolean isDm) throws ModuleException { - try { - final ConfigurationBuilder cb = new ConfigurationBuilder(); - cb.setDebugEnabled(true).setOAuthConsumerKey(consumerKey).setOAuthConsumerSecret(consumerSecret) - .setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret); - - final TwitterFactory tf = new TwitterFactory(cb.build()); - final twitter4j.Twitter twitter = tf.getInstance(); - - if (!isDm) { - final Status status = twitter.updateStatus(message); - return new NoticeMessage("You message was posted to https://twitter.com/" + twitter.getScreenName() - + "/statuses/" + status.getId()); - } else { - final DirectMessage dm = twitter.sendDirectMessage(handle, message); - return new NoticeMessage(dm.getText()); - } - } catch (Exception e) { - throw new ModuleException("twitterPost(" + message + ")", "An error has occurred: " + e.getMessage(), e); - } - } - - /** - * Add an entry to be posted on Twitter. - * - * @param index The entry index. - */ - public final void addEntry(final int index) { - entries.add(index); - } - - public final int entriesCount() { - return entries.size(); - } - - public String getHandle() { - return properties.get(HANDLE_PROP); - } - - public final boolean hasEntry(final int index) { - return entries.contains(index); - } - - public boolean isAutoPost() { - return isEnabled() && Boolean.parseBoolean(properties.get(AUTOPOST_PROP)); - } - - @Override - boolean isValidProperties() { - for (final String s : getPropertyKeys()) { - if (!AUTOPOST_PROP.equals(s) && !HANDLE_PROP.equals(s) && StringUtils.isBlank(properties.get(s))) { - return false; - } - } - return true; - } - - /** - * Send a notification to the registered Twitter handle. - * - * @param msg The twitter message. - */ - public final void notification(final String msg) { - if (isEnabled() && isNotBlank(getHandle())) { - new Thread(() -> { - try { - post(String.format(msg, bot.getName(), ReleaseInfo.VERSION, bot.getChannel()), true); - } catch (ModuleException e) { - bot.getLogger().warn("Failed to notify @{}: {}", getHandle(), msg, e); - } - }).start(); - } - } - - /** - * Posts on Twitter. - * - * @param message The message to post. - * @param isDm The direct message flag. - * @throws ModuleException If an error occurs while posting. - */ - public void post(final String message, final boolean isDm) - throws ModuleException { - post(properties.get(HANDLE_PROP), message, isDm); - } - - /** - * Posts on Twitter. - * - * @param handle The Twitter handle (dm) or nickname. - * @param message The message to post. - * @param isDm The direct message flag. - * @return The {@link Message} to send back. - * @throws ModuleException If an error occurs while posting. - */ - public Message post(final String handle, final String message, final boolean isDm) - throws ModuleException { - return twitterPost(properties.get(CONSUMER_KEY_PROP), - properties.get(CONSUMER_SECRET_PROP), - properties.get(TOKEN_PROP), - properties.get(TOKEN_SECRET_PROP), - handle, - message, - isDm); - } - - /** - * Post an entry to twitter. - * - * @param index The post entry index. - */ - @SuppressFBWarnings("SUI_CONTAINS_BEFORE_REMOVE") - public final void postEntry(final int index) { - if (isAutoPost() && hasEntry(index) && LinksMgr.getEntriesCount() >= index) { - final EntryLink entry = LinksMgr.getEntry(index); - final String msg = - entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + bot.getChannel(); - new Thread(() -> { - try { - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1); - } - post(msg, false); - } catch (ModuleException e) { - bot.getLogger().warn("Failed to post entry on Twitter.", e); - } - }).start(); - removeEntry(index); - } - } - - public void queueEntry(final int index) { - if (isAutoPost()) { - addEntry(index); - bot.getLogger().debug("Scheduling ${Constants.LINK_CMD}${index + 1} for posting on Twitter."); - bot.getTimer().schedule(new TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L); - } - } - - public final void removeEntry(final int index) { - entries.remove(index); - } - - /** - * Posts to twitter. - */ - @Override - void run(final String sender, final String cmd, final String message, final boolean isPrivate) { - try { - bot.send(sender, - post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMsg(), - isPrivate); - } catch (ModuleException e) { - bot.getLogger().warn(e.getDebugMessage(), e); - bot.send(sender, e.getMessage(), isPrivate); - } - } - - /** - * Post all the entries to Twitter on shutdown. - */ - public final void shutdown() { - if (isAutoPost()) { - for (final int index : entries) { - postEntry(index); - } - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt new file mode 100644 index 0000000..07de9f9 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -0,0 +1,244 @@ +/* + * Twitter.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.TwitterTimer +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entriesCount +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getEntry +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import org.apache.commons.lang3.StringUtils +import twitter4j.TwitterException +import twitter4j.TwitterFactory +import twitter4j.conf.ConfigurationBuilder +import java.util.* + +/** + * The Twitter module. + */ +class Twitter(bot: Mobibot) : ThreadedModule(bot) { + // Twitter auto-posts. + private val entries: MutableSet<Int> = HashSet() + + /** + * Add an entry to be posted on Twitter. + */ + private fun addEntry(index: Int) { + entries.add(index) + } + + fun entriesCount(): Int { + return entries.size + } + + private val handle: String? + get() = properties[HANDLE_PROP] + + private fun hasEntry(index: Int): Boolean { + return entries.contains(index) + } + + val isAutoPost: Boolean + get() = isEnabled && properties[AUTOPOST_PROP].toBoolean() + + override val isValidProperties: Boolean + get() { + for (s in propertyKeys) { + if (AUTOPOST_PROP != s && HANDLE_PROP != s && properties[s]!!.isBlank()) { + return false + } + } + return true + } + + /** + * Send a notification to the registered Twitter handle. + */ + fun notification(msg: String) { + with(bot) { + if (isEnabled && StringUtils.isNotBlank(handle)) { + Thread { + try { + post(message = msg, isDm = true) + if (logger.isDebugEnabled) { + logger.debug("Notified @{}: {}", handle, msg) + } + } catch (e: ModuleException) { + logger.warn("Failed to notify @{}: {}", handle, msg, e) + } + }.start() + } + } + } + + /** + * Posts on Twitter. + */ + @Throws(ModuleException::class) + fun post(handle: String = "${properties[HANDLE_PROP]}", message: String, isDm: Boolean): Message { + return twitterPost( + properties[CONSUMER_KEY_PROP], + properties[CONSUMER_SECRET_PROP], + properties[TOKEN_PROP], + properties[TOKEN_SECRET_PROP], + handle, + message, + isDm + ) + } + + /** + * Post an entry to twitter. + */ + fun postEntry(index: Int) { + with(bot) { + if (isAutoPost && hasEntry(index) && entriesCount >= index) { + val entry = getEntry(index) + val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel" + Thread { + try { + if (logger.isDebugEnabled) { + logger.debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1) + } + post(message = msg, isDm = false) + } catch (e: ModuleException) { + logger.warn("Failed to post entry on Twitter.", e) + } + }.start() + removeEntry(index) + } + } + } + + fun queueEntry(index: Int) { + if (isAutoPost) { + addEntry(index) + if (bot.logger.isDebugEnabled) { + bot.logger.debug("Scheduling {}{} for posting on Twitter.", Constants.LINK_CMD, index + 1) + } + bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) + } + } + + fun removeEntry(index: Int) { + entries.remove(index) + } + + /** + * Posts to twitter. + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + with(bot) { + try { + send( + sender, + post(sender, "$args (by $sender on $channel)", false).msg, + isPrivate + ) + } catch (e: ModuleException) { + logger.warn(e.debugMessage, e) + send(sender, e.message, isPrivate) + } + } + } + + /** + * Post all the entries to Twitter on shutdown. + */ + fun shutdown() { + if (isAutoPost) { + for (index in entries) { + postEntry(index) + } + } + } + + companion object { + // Property keys + const val AUTOPOST_PROP = "twitter-auto-post" + const val CONSUMER_KEY_PROP = "twitter-consumerKey" + const val CONSUMER_SECRET_PROP = "twitter-consumerSecret" + const val HANDLE_PROP = "twitter-handle" + const val TOKEN_PROP = "twitter-token" + const val TOKEN_SECRET_PROP = "twitter-tokenSecret" + + // Twitter command + private const val TWITTER_CMD = "twitter" + + /** + * Posts on Twitter. + */ + @JvmStatic + @Throws(ModuleException::class) + fun twitterPost( + consumerKey: String?, + consumerSecret: String?, + token: String?, + tokenSecret: String?, + handle: String?, + message: String, + isDm: Boolean + ): Message { + return try { + val cb = ConfigurationBuilder() + cb.setDebugEnabled(true) + .setOAuthConsumerKey(consumerKey) + .setOAuthConsumerSecret(consumerSecret) + .setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret) + val tf = TwitterFactory(cb.build()) + val twitter = tf.instance + if (!isDm) { + val status = twitter.updateStatus(message) + NoticeMessage( + "You message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}" + ) + } else { + val dm = twitter.sendDirectMessage(handle, message) + NoticeMessage(dm.text) + } + } catch (e: TwitterException) { + throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e) + } + } + } + + init { + commands.add(TWITTER_CMD) + help.add("To post to Twitter:") + help.add(Utils.helpIndent("%c $TWITTER_CMD <message>")) + properties[AUTOPOST_PROP] = "false" + initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index b1be81e..fe43ef5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.modules; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import org.jetbrains.annotations.NotNull; import java.security.SecureRandom; @@ -71,9 +72,9 @@ public final class War extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(final String sender, - final String cmd, - final String args, + public void commandResponse(@NotNull final String sender, + @NotNull final String cmd, + @NotNull final String args, final boolean isPrivate) { final SecureRandom r = new SecureRandom(); @@ -84,20 +85,21 @@ public final class War extends AbstractModule { i = r.nextInt(WAR_DECK.length); y = r.nextInt(WAR_DECK.length); - bot.send(sender + " drew the " + bold(WAR_DECK[i]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); - bot.action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); + getBot().send(sender + " drew the " + bold(WAR_DECK[i]) + " of " + + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); + getBot().action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); if (i != y) { break; } - bot.send("This means " + bold("WAR") + '!'); + getBot().send("This means " + bold("WAR") + '!'); } if (i < y) { - bot.action("lost."); + getBot().action("lost."); } else { - bot.action("wins."); + getBot().action("wins."); } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java deleted file mode 100644 index e1910ac..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Weather2.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.aksingh.owmjapis.api.APIException; -import net.aksingh.owmjapis.core.OWM; -import net.aksingh.owmjapis.model.CurrentWeather; -import net.aksingh.owmjapis.model.param.Main; -import net.aksingh.owmjapis.model.param.Weather; -import net.aksingh.owmjapis.model.param.Wind; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.msg.ErrorMessage; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.NoticeMessage; -import net.thauvin.erik.mobibot.msg.PublicMessage; -import org.apache.commons.lang3.StringUtils; -import org.jibble.pircbot.Colors; - -import java.util.ArrayList; -import java.util.List; - -import static net.thauvin.erik.mobibot.Utils.bold; - -/** - * The <code>Weather2</code> module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2017-04-02 - * @since 1.0 - */ -public class Weather2 extends ThreadedModule { - /** - * The OpenWeatherMap API Key property. - */ - static final String OWM_API_KEY_PROP = "owm-api-key"; - - // Weather command - private static final String WEATHER_CMD = "weather"; - - /** - * Creates a new {@link Weather2} instance. - */ - public Weather2(final Mobibot bot) { - super(bot); - - commands.add(WEATHER_CMD); - - help.add("To display weather information:"); - help.add(Utils.helpIndent("%c " + WEATHER_CMD + " <city> [, <country code>]")); - help.add("For example:"); - help.add(Utils.helpIndent("%c " + WEATHER_CMD + " paris, fr")); - help.add("The default ISO 3166 country code is " + bold("US") + ". Zip codes supported in most countries."); - - initProperties(OWM_API_KEY_PROP); - } - - private static OWM.Country getCountry(final String countryCode) { - for (final OWM.Country c : OWM.Country.values()) { - if (c.name().equalsIgnoreCase(countryCode)) { - return c; - } - } - - return OWM.Country.UNITED_STATES; - } - - @SuppressWarnings("AvoidEscapedUnicodeCharacters") - private static String getTemps(final Double d) { - final double c = (d - 32) * 5 / 9; - return Math.round(d) + " °F, " + Math.round(c) + " °C"; - } - - /** - * Retrieves the weather data. - * - * <ul> - * <li>98204</li> - * <li>London, UK</li> - * </ul> - * - * @param query The query. - * @param apiKey The API key. - * @return The {@link Message} array containing the weather data. - * @throws ModuleException If an error occurs while retrieving the weather data. - */ - static List<Message> getWeather(final String query, final String apiKey) throws ModuleException { - if (StringUtils.isBlank(apiKey)) { - throw new ModuleException(StringUtils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing."); - } - - final OWM owm = new OWM(apiKey); - final ArrayList<Message> messages = new ArrayList<>(); - - owm.setUnit(OWM.Unit.IMPERIAL); - - if (StringUtils.isNotBlank(query)) { - final String[] argv = query.split(","); - - if (argv.length >= 1 && argv.length <= 2) { - final String country; - final String city = argv[0].trim(); - if (argv.length > 1 && StringUtils.isNotBlank(argv[1])) { - country = argv[1].trim(); - } else { - country = "US"; - } - - try { - final CurrentWeather cwd; - if (city.matches("\\d+")) { - cwd = owm.currentWeatherByZipCode(Integer.parseInt(city), getCountry(country)); - } else { - cwd = owm.currentWeatherByCityName(city, getCountry(country)); - } - if (cwd.hasCityName()) { - messages.add(new PublicMessage( - "City: " + cwd.getCityName() + " [" + StringUtils.upperCase(country) + "]")); - - final Main main = cwd.getMainData(); - if (main != null) { - if (main.hasTemp()) { - messages.add(new PublicMessage("Temperature: " + getTemps(main.getTemp()))); - } - - if (main.hasHumidity() && (main.getHumidity() != null)) { - messages.add(new NoticeMessage("Humidity: " + Math.round(main.getHumidity()) + "%")); - } - } - - if (cwd.hasWindData()) { - final Wind w = cwd.getWindData(); - if (w != null && w.hasSpeed()) { - messages.add(new NoticeMessage("Wind: " + wind(w.getSpeed()))); - } - } - - if (cwd.hasWeatherList()) { - final StringBuilder condition = new StringBuilder("Condition:"); - final List<Weather> list = cwd.getWeatherList(); - if (list != null) { - for (final Weather w : list) { - condition.append(' ') - .append(StringUtils.capitalize(w.getDescription())) - .append('.'); - } - messages.add(new NoticeMessage(condition.toString())); - } - } - - if (cwd.hasCityId() && cwd.getCityId() != null) { - if (cwd.getCityId() > 0) { - messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(), - Colors.GREEN)); - } else { - messages.add(new NoticeMessage("https://openweathermap.org/find?q=" + Utils.encodeUrl( - city + ',' + StringUtils.upperCase(country)), Colors.GREEN)); - } - } - } - } catch (APIException | NullPointerException e) { - throw new ModuleException("getWeather(" + query + ')', "Unable to perform weather lookup.", e); - } - } - } - - if (messages.isEmpty()) { - messages.add(new ErrorMessage("Invalid syntax.")); - } - - return messages; - } - - private static String wind(final Double w) { - final double kmh = w * 1.60934; - return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; - } - - /** - * Fetches the weather data from a specific city. - */ - @Override - void run(final String sender, final String cmd, final String args, final boolean isPrivate) { - if (StringUtils.isNotBlank(args)) { - try { - final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); - if (messages.get(0).isError()) { - helpResponse(sender, isPrivate); - } else { - for (final Message msg : messages) { - bot.send(sender, msg); - } - } - } catch (ModuleException e) { - bot.getLogger().debug(e.getDebugMessage(), e); - bot.send(e.getMessage()); - } - } else { - helpResponse(sender, isPrivate); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt new file mode 100644 index 0000000..2dea6c7 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -0,0 +1,200 @@ +/* + * Weather2.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.aksingh.owmjapis.api.APIException +import net.aksingh.owmjapis.core.OWM +import net.aksingh.owmjapis.core.OWM.Country +import net.aksingh.owmjapis.model.CurrentWeather +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.apache.commons.lang3.StringUtils +import org.jibble.pircbot.Colors +import java.util.* +import kotlin.math.roundToInt + +/** + * The `Weather2` module. + */ +class Weather2(bot: Mobibot) : ThreadedModule(bot) { + /** + * Fetches the weather data from a specific city. + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + if (StringUtils.isNotBlank(args)) { + try { + val messages = getWeather(args, properties[OWM_API_KEY_PROP]) + if (messages[0].isError) { + helpResponse(sender, isPrivate) + } else { + for (msg in messages) { + bot.send(sender, msg) + } + } + } catch (e: ModuleException) { + bot.logger.debug(e.debugMessage, e) + bot.send(e.message) + } + } else { + helpResponse(sender, isPrivate) + } + } + + companion object { + /** + * The OpenWeatherMap API Key property. + */ + const val OWM_API_KEY_PROP = "owm-api-key" + + // Weather command + private const val WEATHER_CMD = "weather" + private fun getCountry(countryCode: String): Country { + for (c in Country.values()) { + if (c.name.equals(countryCode, ignoreCase = true)) { + return c + } + } + return Country.UNITED_STATES + } + + private fun getTemps(d: Double?): String { + val c = (d!! - 32) * 5 / 9 + return "${d.roundToInt()} °F, ${c.roundToInt()} °C" + } + + /** + * Retrieves the weather data. + */ + @JvmStatic + @Throws(ModuleException::class) + fun getWeather(query: String, apiKey: String?): List<Message> { + if (StringUtils.isBlank(apiKey)) { + throw ModuleException("${WEATHER_CMD.capitalize()} is disabled. The API key is missing.") + } + val owm = OWM(apiKey!!) + val messages = ArrayList<Message>() + owm.unit = OWM.Unit.IMPERIAL + if (StringUtils.isNotBlank(query)) { + val argv = query.split(",").toTypedArray() + if (argv.size in 1..2) { + val city = argv[0].trim() + val country: String = if (argv.size > 1 && StringUtils.isNotBlank(argv[1])) { + argv[1].trim() + } else { + "US" + } + try { + val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) { + owm.currentWeatherByZipCode(city.toInt(), getCountry(country)) + } else { + owm.currentWeatherByCityName(city, getCountry(country)) + } + if (cwd.hasCityName()) { + messages.add( + PublicMessage("City: ${cwd.cityName} [${country.toUpperCase()}]") + ) + with(cwd.mainData) { + if (this != null) { + if (hasTemp()) { + messages.add(PublicMessage("Temperature: ${getTemps(temp)}")) + } + if (hasHumidity() && humidity != null) { + messages.add(NoticeMessage("Humidity: ${(humidity!!).roundToInt()}%")) + } + } + } + if (cwd.hasWindData()) { + with(cwd.windData) { + if (this != null && hasSpeed()) { + messages.add(NoticeMessage("Wind: ${wind(speed)}")) + } + } + } + if (cwd.hasWeatherList()) { + val condition = StringBuilder("Condition:") + val list = cwd.weatherList + if (list != null) { + for (w in list) { + condition.append(' ').append(w!!.getDescription().capitalize()).append('.') + } + messages.add(NoticeMessage(condition.toString())) + } + } + if (cwd.hasCityId() && cwd.cityId != null) { + if (cwd.cityId!! > 0) { + messages.add( + NoticeMessage("https://openweathermap.org/city/${cwd.cityId}", Colors.GREEN) + ) + } else { + messages.add( + NoticeMessage( + "https://openweathermap.org/find?q=" + + Utils.encodeUrl("$city,${country.toUpperCase()}"), + Colors.GREEN + ) + ) + } + } + } + } catch (e: APIException) { + throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e) + } catch (e: NullPointerException) { + throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e) + } + } + } + if (messages.isEmpty()) { + messages.add(ErrorMessage("Invalid syntax.")) + } + return messages + } + + private fun wind(w: Double?): String { + val kmh = w!! * 1.60934 + return "${Math.round(w)} mph, ${kmh.roundToInt()} km/h" + } + } + + init { + commands.add(WEATHER_CMD) + help.add("To display weather information:") + help.add(Utils.helpIndent("%c $WEATHER_CMD <city> [, <country code>]")) + help.add("For example:") + help.add(Utils.helpIndent("%c $WEATHER_CMD paris, fr")) + help.add("The default ISO 3166 country code is ${Utils.bold("US")}. Zip codes supported in most countries.") + initProperties(OWM_API_KEY_PROP) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java deleted file mode 100644 index 05bcc22..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * WorldTime.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.msg.ErrorMessage; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.mobibot.msg.PublicMessage; -import org.apache.commons.lang3.StringUtils; - -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoField; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -/** - * The WorldTime module. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-27 - * @since 1.0 - */ -@SuppressWarnings("PMD.UseConcurrentHashMap") -public final class WorldTime extends AbstractModule { - // Beats (Internet Time) keyword - private static final String BEATS_KEYWORD = ".beats"; - // Supported countries - private static final Map<String, String> COUNTRIES_MAP; - - // The Time command - private static final String TIME_CMD = "time"; - - static { - // Initialize the countries map - final Map<String, String> countries = new TreeMap<>(); - countries.put("AE", "Asia/Dubai"); - countries.put("AF", "Asia/Kabul"); - countries.put("AQ", "Antarctica/South_Pole"); - countries.put("AT", "Europe/Vienna"); - countries.put("AU", "Australia/Sydney"); - countries.put("AKST", "America/Anchorage"); - countries.put("AKDT", "America/Anchorage"); - countries.put("BE", "Europe/Brussels"); - countries.put("BR", "America/Sao_Paulo"); - countries.put("CA", "America/Montreal"); - countries.put("CDT", "America/Chicago"); - countries.put("CET", "CET"); - countries.put("CH", "Europe/Zurich"); - countries.put("CN", "Asia/Shanghai"); - countries.put("CST", "America/Chicago"); - countries.put("CU", "Cuba"); - countries.put("DE", "Europe/Berlin"); - countries.put("DK", "Europe/Copenhagen"); - countries.put("EDT", "America/New_York"); - countries.put("EG", "Africa/Cairo"); - countries.put("ER", "Africa/Asmara"); - countries.put("ES", "Europe/Madrid"); - countries.put("EST", "America/New_York"); - countries.put("FI", "Europe/Helsinki"); - countries.put("FR", "Europe/Paris"); - countries.put("GB", "Europe/London"); - countries.put("GMT", "GMT"); - countries.put("GR", "Europe/Athens"); - countries.put("HK", "Asia/Hong_Kong"); - countries.put("HST", "Pacific/Honolulu"); - countries.put("IE", "Europe/Dublin"); - countries.put("IL", "Asia/Tel_Aviv"); - countries.put("IN", "Asia/Kolkata"); - countries.put("IQ", "Asia/Baghdad"); - countries.put("IR", "Asia/Tehran"); - countries.put("IS", "Atlantic/Reykjavik"); - countries.put("IT", "Europe/Rome"); - countries.put("JM", "Jamaica"); - countries.put("JP", "Asia/Tokyo"); - countries.put("LY", "Africa/Tripoli"); - countries.put("MA", "Africa/Casablanca"); - countries.put("MDT", "America/Denver"); - countries.put("MH", "Kwajalein"); - countries.put("MQ", "America/Martinique"); - countries.put("MST", "America/Denver"); - countries.put("MX", "America/Mexico_City"); - countries.put("NL", "Europe/Amsterdam"); - countries.put("NO", "Europe/Oslo"); - countries.put("NP", "Asia/Katmandu"); - countries.put("NZ", "Pacific/Auckland"); - countries.put("PDT", "America/Los_Angeles"); - countries.put("PH", "Asia/Manila"); - countries.put("PK", "Asia/Karachi"); - countries.put("PL", "Europe/Warsaw"); - countries.put("PST", "America/Los_Angeles"); - countries.put("PT", "Europe/Lisbon"); - countries.put("PR", "America/Puerto_Rico"); - countries.put("RU", "Europe/Moscow"); - countries.put("SE", "Europe/Stockholm"); - countries.put("SG", "Asia/Singapore"); - countries.put("TH", "Asia/Bangkok"); - countries.put("TM", "Asia/Ashgabat"); - countries.put("TN", "Africa/Tunis"); - countries.put("TR", "Europe/Istanbul"); - countries.put("TW", "Asia/Taipei"); - countries.put("UK", "Europe/London"); - countries.put("US", "America/New_York"); - countries.put("UTC", "UTC"); - countries.put("VA", "Europe/Vatican"); - countries.put("VE", "America/Caracas"); - countries.put("VN", "Asia/Ho_Chi_Minh"); - countries.put("ZA", "Africa/Johannesburg"); - countries.put("ZULU", "Zulu"); - countries.put("INTERNET", BEATS_KEYWORD); - countries.put("BEATS", BEATS_KEYWORD); - - ZoneId.getAvailableZoneIds().stream() - .filter(tz -> !tz.contains("/") && tz.length() == 3 && !countries.containsKey(tz)) - .forEach(tz -> countries.put(tz, tz)); - - COUNTRIES_MAP = Collections.unmodifiableMap(countries); - } - - /** - * Creates a new {@link WorldTime} instance. - */ - public WorldTime(final Mobibot bot) { - super(bot); - - help.add("To display a country's current date/time:"); - help.add(Utils.helpIndent("%c " + TIME_CMD) + " [<country code>]"); - help.add("For a listing of the supported countries:"); - help.add(Utils.helpIndent("%c " + TIME_CMD)); - - commands.add(TIME_CMD); - } - - - /** - * Returns the current Internet (beat) Time. - * - * @return The Internet Time string. - */ - private static String internetTime() { - final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")); - final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) - + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); - return String.format("%c%03d", '@', beats); - } - - /** - * Returns the world time. - * - * <ul> - * <li>PST</li> - * <li>BEATS</li> - * </ul> - * - * @param query The query. - * @return The {@link Message} containing the world time. - */ - @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") - static Message worldTime(final String query) { - final String tz = (COUNTRIES_MAP.get((StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim())))); - final String response; - - if (tz != null) { - if (BEATS_KEYWORD.equals(tz)) { - response = ("The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD)); - } else { - response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format( - DateTimeFormatter - .ofPattern("'The time is " + Utils.bold("'HH:mm'") + " on " + Utils.bold( - "'EEEE, d MMMM yyyy'") + " in '")) - + Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' ')); - } - } else { - return new ErrorMessage("Unsupported country/zone. Please try again."); - } - - return new PublicMessage(response); - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final String sender, - final String cmd, - final String args, - final boolean isPrivate) { - if (args.length() == 0) { - bot.send(sender, "The supported countries/zones are: ", isPrivate); - bot.sendList(sender, new ArrayList<>(COUNTRIES_MAP.keySet()), 17, false, false); - } else { - final Message msg = worldTime(args); - if (isPrivate) { - bot.send(sender, msg.getMsg(), true); - } else { - if (msg.isError()) { - bot.send(sender, msg.getMsg(), false); - } else { - bot.send(msg.getMsg()); - } - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isPrivateMsgEnabled() { - return true; - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt new file mode 100644 index 0000000..8ecc469 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -0,0 +1,219 @@ +/* + * WorldTime.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.apache.commons.lang3.StringUtils +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField +import java.util.* + +/** + * The WorldTime module. + */ +class WorldTime(bot: Mobibot) : AbstractModule(bot) { + companion object { + // Beats (Internet Time) keyword + private const val BEATS_KEYWORD = ".beats" + + // Supported countries + private var COUNTRIES_MAP: Map<String, String> + + // The Time command + private const val TIME_CMD = "time" + + /** + * Returns the current Internet (beat) Time. + */ + private fun internetTime(): String { + val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) + val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 + + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() + return String.format("%c%03d", '@', beats) + } + + /** + * Returns the world time. + */ + @JvmStatic + fun worldTime(query: String): Message { + val tz = COUNTRIES_MAP[StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim())] + val response: String = if (tz != null) { + if (BEATS_KEYWORD == tz) { + "The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD) + } else { + (ZonedDateTime.now() + .withZoneSameInstant(ZoneId.of(tz)) + .format( + DateTimeFormatter.ofPattern( + "'The time is ${Utils.bold("'HH:mm'")} on ${Utils.bold("'EEEE, d MMMM yyyy'")} in '" + ) + ) + + Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' ')) + ) + } + } else { + return ErrorMessage("Unsupported country/zone. Please try again.") + } + return PublicMessage(response) + } + + init { + // Initialize the countries map + val countries = TreeMap<String, String>() + countries["AE"] = "Asia/Dubai" + countries["AF"] = "Asia/Kabul" + countries["AQ"] = "Antarctica/South_Pole" + countries["AT"] = "Europe/Vienna" + countries["AU"] = "Australia/Sydney" + countries["AKST"] = "America/Anchorage" + countries["AKDT"] = "America/Anchorage" + countries["BE"] = "Europe/Brussels" + countries["BR"] = "America/Sao_Paulo" + countries["CA"] = "America/Montreal" + countries["CDT"] = "America/Chicago" + countries["CET"] = "CET" + countries["CH"] = "Europe/Zurich" + countries["CN"] = "Asia/Shanghai" + countries["CST"] = "America/Chicago" + countries["CU"] = "Cuba" + countries["DE"] = "Europe/Berlin" + countries["DK"] = "Europe/Copenhagen" + countries["EDT"] = "America/New_York" + countries["EG"] = "Africa/Cairo" + countries["ER"] = "Africa/Asmara" + countries["ES"] = "Europe/Madrid" + countries["EST"] = "America/New_York" + countries["FI"] = "Europe/Helsinki" + countries["FR"] = "Europe/Paris" + countries["GB"] = "Europe/London" + countries["GMT"] = "GMT" + countries["GR"] = "Europe/Athens" + countries["HK"] = "Asia/Hong_Kong" + countries["HST"] = "Pacific/Honolulu" + countries["IE"] = "Europe/Dublin" + countries["IL"] = "Asia/Tel_Aviv" + countries["IN"] = "Asia/Kolkata" + countries["IQ"] = "Asia/Baghdad" + countries["IR"] = "Asia/Tehran" + countries["IS"] = "Atlantic/Reykjavik" + countries["IT"] = "Europe/Rome" + countries["JM"] = "Jamaica" + countries["JP"] = "Asia/Tokyo" + countries["LY"] = "Africa/Tripoli" + countries["MA"] = "Africa/Casablanca" + countries["MDT"] = "America/Denver" + countries["MH"] = "Kwajalein" + countries["MQ"] = "America/Martinique" + countries["MST"] = "America/Denver" + countries["MX"] = "America/Mexico_City" + countries["NL"] = "Europe/Amsterdam" + countries["NO"] = "Europe/Oslo" + countries["NP"] = "Asia/Katmandu" + countries["NZ"] = "Pacific/Auckland" + countries["PDT"] = "America/Los_Angeles" + countries["PH"] = "Asia/Manila" + countries["PK"] = "Asia/Karachi" + countries["PL"] = "Europe/Warsaw" + countries["PST"] = "America/Los_Angeles" + countries["PT"] = "Europe/Lisbon" + countries["PR"] = "America/Puerto_Rico" + countries["RU"] = "Europe/Moscow" + countries["SE"] = "Europe/Stockholm" + countries["SG"] = "Asia/Singapore" + countries["TH"] = "Asia/Bangkok" + countries["TM"] = "Asia/Ashgabat" + countries["TN"] = "Africa/Tunis" + countries["TR"] = "Europe/Istanbul" + countries["TW"] = "Asia/Taipei" + countries["UK"] = "Europe/London" + countries["US"] = "America/New_York" + countries["UTC"] = "UTC" + countries["VA"] = "Europe/Vatican" + countries["VE"] = "America/Caracas" + countries["VN"] = "Asia/Ho_Chi_Minh" + countries["ZA"] = "Africa/Johannesburg" + countries["ZULU"] = "Zulu" + countries["INTERNET"] = BEATS_KEYWORD + countries["BEATS"] = BEATS_KEYWORD + ZoneId.getAvailableZoneIds().stream() + .filter { tz: String -> + !tz.contains("/") && tz.length == 3 && !countries.containsKey(tz) + } + .forEach { tz: String -> + countries[tz] = tz + } + COUNTRIES_MAP = Collections.unmodifiableMap(countries) + } + } + + override fun commandResponse( + sender: String, + cmd: String, + args: String, + isPrivate: Boolean + ) { + with(bot) { + if (args.isEmpty()) { + send(sender, "The supported countries/zones are: ", isPrivate) + sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, false, false) + } else { + val msg = worldTime(args) + if (isPrivate) { + send(sender, msg.msg, true) + } else { + if (msg.isError) { + send(sender, msg.msg, false) + } else { + send(msg.msg) + } + } + } + } + } + + override val isPrivateMsgEnabled = true + + init { + help.add("To display a country's current date/time:") + help.add(Utils.helpIndent("%c $TIME_CMD") + " [<country code>]") + help.add("For a listing of the supported countries:") + help.add(Utils.helpIndent("%c $TIME_CMD")) + commands.add(TIME_CMD) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index b353fe8..96ef549 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,5 +1,5 @@ /* - * ErrorMessage.java + * ErrorMessage.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg /** * The `ErrorMessage` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-07 - * @since 1.0 */ class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { init { diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt index d3fe758..a6f66f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt @@ -35,10 +35,6 @@ import org.jibble.pircbot.Colors /** * The `Message` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-07 - * @since 1.0 */ open class Message { companion object { @@ -67,12 +63,6 @@ open class Message { /** * Creates a new message. - * - * @param msg The message. - * @param color The color. - * @param isNotice The notice flag. - * @param isError The error flag. - * @param isPrivate The private flag. */ @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR, isNotice: Boolean, isError: Boolean, isPrivate: Boolean) { diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 3286d60..126fd91 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,5 +1,5 @@ /* - * NoticeMessage.java + * NoticeMessage.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg /** * The `NoticeMessage` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-07 - * @since 1.0 */ class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { init { diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 8eb4281..6bbade8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,5 +1,5 @@ /* - * PrivateMessage.java + * PrivateMessage.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg /** * The `PrivateMessage` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-09 - * @since 1.0 */ @Suppress("unused") class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 3d2ac0f..384bc05 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,5 +1,5 @@ /* - * PublicMessage.java + * PublicMessage.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg /** * The `PublicMessage` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-07 - * @since 1.0 */ class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { init { diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java index b754fbf..bde9d77 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java @@ -66,7 +66,7 @@ public class UtilsTest { @Test public void testBold() { - assertThat(Utils.bold(1)).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD); + assertThat(Utils.bold(Integer.toString(1))).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD); assertThat(Utils.bold(ASCII)).as("bold(ascii").isEqualTo(Colors.BOLD + ASCII + Colors.BOLD); } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt similarity index 63% rename from src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt index 2f9b21e..643dbd6 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,5 +1,5 @@ /* - * CalcTest.java + * CalcTest.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,32 +29,26 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Utils; -import org.testng.annotations.Test; - -import static org.assertj.core.api.Assertions.assertThat; +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.modules.Calc.Companion.calc +import org.assertj.core.api.Assertions +import org.testng.annotations.Test /** - * The <code>CalcTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 + * The `CalcTest` class. */ -public class CalcTest { +class CalcTest { @Test - public void testCalc() { - assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)); - assertThat(Calc.calc("1 -3")).as("calc(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)); - assertThat(Calc.calc("pi+π+e+φ")).as("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")); - assertThat(Calc.calc("one + one")).as("calc(one + one)").startsWith("No idea."); - } - - @Test - public void testCalcImpl() { - AbstractModuleTest.testAbstractModule(new Calc(null)); + fun testCalc() { + Assertions.assertThat(calc("1 + 1")).`as`("calc(1+1)") + .isEqualTo("1+1 = %s", Utils.bold(2)) + Assertions.assertThat(calc("1 -3")).`as`("calc(1 -3)") + .isEqualTo("1-3 = %s", Utils.bold(-2)) + Assertions.assertThat(calc("pi+π+e+φ")).`as`("calc(pi+π+e+φ)") + .isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) + Assertions.assertThat(calc("one + one")).`as`("calc(one + one)") + .startsWith("No idea.") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java deleted file mode 100644 index b67fd6f..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * CurrencyConverterTest.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The <code>CurrencyConvertTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 - */ -public class CurrencyConverterTest { - @BeforeClass - public void before() throws ModuleException { - CurrencyConverter.loadRates(); - } - - @Test - @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") - public void testConvertCurrency() { - assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMsg()) - .as("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR"); - assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getMsg()) - .as("100 USD to USD").contains("You're kidding, right?"); - assertThat(CurrencyConverter.convertCurrency("100 USD").getMsg()) - .as("100 USD").contains("Invalid query."); - assertThat(CurrencyConverter.currencyRates().size()) - .as("currencyRates().size() == 33").isEqualTo(33); - assertThat(CurrencyConverter.currencyRates()) - .as("currencyRates().get(EUR)").contains(" EUR: 1"); - } - - @Test - public void testCurrencyConvertererImpl() { - AbstractModuleTest.testAbstractModule(new CurrencyConverter(null)); - } -} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt similarity index 56% rename from src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 6b1b7c4..edcbb65 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,5 +1,5 @@ /* - * LookupTest.java + * CurrencyConverterTest.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,40 +29,36 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import org.testng.annotations.Test; - -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; +import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency +import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates +import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates +import org.assertj.core.api.Assertions +import org.testng.annotations.BeforeClass +import org.testng.annotations.Test /** - * The <code>Lookup Test</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2017-05-30 - * @since 1.0 + * The `CurrencyConvertTest` class. */ -@SuppressWarnings("PMD.AvoidUsingHardCodedIP") -public class LookupTest { - @Test - public void testLookupImpl() { - AbstractModuleTest.testAbstractModule(new Lookup(null)); +class CurrencyConverterTest { + @BeforeClass + @Throws(ModuleException::class) + fun before() { + loadRates() } @Test - public void testLookup() throws Exception { - final String result = Lookup.lookup("erik.thauvin.net"); - assertThat(result).as("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12"); - } - - @Test - public void testWhois() throws Exception { - final String[] result = Lookup.whois("17.178.96.59", Lookup.WHOIS_HOST); - - assertThat(Arrays.stream(result).anyMatch(m -> m.contains("Apple Inc."))) - .as("whois(17.178.96.59/Apple Inc.").isTrue(); + fun testConvertCurrency() { + Assertions.assertThat(convertCurrency("100 USD to EUR").msg) + .`as`("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR") + Assertions.assertThat(convertCurrency("100 USD to USD").msg) + .`as`("100 USD to USD").contains("You're kidding, right?") + Assertions.assertThat(convertCurrency("100 USD").msg) + .`as`("100 USD").contains("Invalid query.") + Assertions.assertThat(currencyRates().size) + .`as`("currencyRates().size() == 33").isEqualTo(33) + Assertions.assertThat(currencyRates()) + .`as`("currencyRates().get(EUR)").contains(" EUR: 1") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt new file mode 100644 index 0000000..6cc59a5 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -0,0 +1,52 @@ +/* + * DiceTest.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + + +import org.assertj.core.api.Assertions.assertThat +import org.testng.annotations.Test + +class DiceTest { + @Test + fun testWinLoseOrTie() { + assertThat( + Dice.winLoseOrTie(6, 6) + ).`as`("6 vs. 6").isEqualTo(Dice.Result.TIE) + assertThat( + Dice.winLoseOrTie(6, 5) + ).`as`("6 vs. 5").isEqualTo(Dice.Result.WIN) + assertThat( + Dice.winLoseOrTie(5, 6) + ).`as`("5 vs. 6").isEqualTo(Dice.Result.LOSE) + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java deleted file mode 100644 index ecf0f0f..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * GoogleSearchTest.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.LocalProperties; -import net.thauvin.erik.mobibot.msg.Message; -import org.testng.annotations.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * The <code>GoogleSearchTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-08 - * @since 1.0 - */ -public class GoogleSearchTest extends LocalProperties { - @Test - public void testGoogleSearchImpl() { - AbstractModuleTest.testAbstractModule(new GoogleSearch(null)); - } - - @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE") - @SuppressWarnings("PMD.PreserveStackTrace") - @Test - public void testSearchGoogle() throws ModuleException { - final String apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP); - final String cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP); - try { - List<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey); - assertThat(messages).as("mobibot results not empty").isNotEmpty(); - assertThat(messages.get(0).getMsg()).as("found mobitopia").contains("mobibot"); - - messages = GoogleSearch.searchGoogle("aapl", apiKey, cseKey); - assertThat(messages).as("aapl results not empty").isNotEmpty(); - assertThat(messages.get(0).getMsg()).as("found apple").containsIgnoringCase("apple"); - - assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "", "apiKey")).as("no API key").isInstanceOf( - ModuleException.class).hasNoCause(); - - assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "apiKey", "")).as("no CSE API key").isInstanceOf( - ModuleException.class).hasNoCause(); - - assertThatThrownBy(() -> GoogleSearch.searchGoogle("", "apikey", "apiKey")).as("no query").isInstanceOf( - ModuleException.class).hasNoCause(); - } catch (ModuleException e) { - // Avoid displaying api keys in CI logs - if ("true".equals(System.getenv("CI"))) { - throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey, cseKey)); - } else { - throw e; - } - } - } -} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt new file mode 100644 index 0000000..b934b02 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -0,0 +1,72 @@ +/* + * GoogleSearchTest.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle +import org.assertj.core.api.Assertions +import org.testng.annotations.Test + +/** + * The `GoogleSearchTest` class. + */ +class GoogleSearchTest : LocalProperties() { + @Test + @Throws(ModuleException::class) + fun testSearchGoogle() { + val apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP) + val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) + try { + var messages = searchGoogle("mobibot site:github.com", apiKey, cseKey) + Assertions.assertThat(messages).`as`("mobibot results not empty").isNotEmpty + Assertions.assertThat(messages[0].msg).`as`("found mobitopia").contains("mobibot") + messages = searchGoogle("aapl", apiKey, cseKey) + Assertions.assertThat(messages).`as`("aapl results not empty").isNotEmpty + Assertions.assertThat(messages[0].msg).`as`("found apple").containsIgnoringCase("apple") + Assertions.assertThatThrownBy { searchGoogle("test", "", "apiKey") } + .`as`("no API key") + .isInstanceOf(ModuleException::class.java).hasNoCause() + Assertions.assertThatThrownBy { searchGoogle("test", "apiKey", "") } + .`as`("no CSE API key") + .isInstanceOf(ModuleException::class.java).hasNoCause() + Assertions.assertThatThrownBy { searchGoogle("", "apikey", "apiKey") } + .`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause() + } catch (e: ModuleException) { + // Avoid displaying api keys in CI logs + if ("true" == System.getenv("CI")) { + throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey)) + } else { + throw e + } + } + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt similarity index 69% rename from src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt index 9880241..5a2f221 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,5 +1,5 @@ /* - * JokeTest.java + * JokeTest.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,29 +29,20 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import org.testng.annotations.Test; - -import static org.assertj.core.api.Assertions.assertThat; +import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke +import org.assertj.core.api.Assertions +import org.testng.annotations.Test /** - * The <code>JokeTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 + * The `JokeTest` class. */ -public class JokeTest { +class JokeTest { @Test - public void testJokeImpl() { - AbstractModuleTest.testAbstractModule(new Joke(null)); - } - - @Test - public void testRamdomJoke() throws ModuleException { - assertThat(Joke.randomJoke().getMsg().length() > 0).as("randomJoke() > 0").isTrue(); - assertThat(Joke.randomJoke().getMsg()).as("randomJoke()").containsIgnoringCase("chuck"); + @Throws(ModuleException::class) + fun testRamdomJoke() { + Assertions.assertThat(randomJoke().msg.isNotEmpty()).`as`("randomJoke() > 0").isTrue + Assertions.assertThat(randomJoke().msg).`as`("randomJoke()").containsIgnoringCase("chuck") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt similarity index 66% rename from src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt index 5f862ca..303a377 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,5 +1,5 @@ /* - * WordTimeTest.java + * LookupTest.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,31 +29,30 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Utils; -import org.testng.annotations.Test; - -import static org.assertj.core.api.Assertions.assertThat; +import net.thauvin.erik.mobibot.modules.Lookup.Companion.lookup +import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois +import org.assertj.core.api.Assertions +import org.testng.annotations.Test +import java.util.* /** - * The <code>WordTimeTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 + * The `Lookup Test` class. */ -public class WordTimeTest { +class LookupTest { @Test - public void testWorldTime() { - assertThat(WorldTime.worldTime("PST").getMsg()).as("PST").endsWith(Utils.bold("Los Angeles")); - assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue(); - assertThat(WorldTime.worldTime("BEATS").getMsg()).as("BEATS").contains("@"); + @Throws(Exception::class) + fun testLookup() { + val result = lookup("erik.thauvin.net") + Assertions.assertThat(result).`as`("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12") } @Test - public void testWorldTimeImpl() { - AbstractModuleTest.testAbstractModule(new Lookup(null)); + @Throws(Exception::class) + fun testWhois() { + val result = whois("17.178.96.59", Lookup.WHOIS_HOST) + Assertions.assertThat(Arrays.stream(result).anyMatch { m: String -> m.contains("Apple Inc.") }) + .`as`("whois(17.178.96.59/Apple Inc.").isTrue } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java index 46199c9..b7feb89 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java @@ -42,10 +42,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** * The <code>ModuleExceptionTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-09 - * @since 1.0 */ public class ModuleExceptionTest { static final String debugMessage = "debugMessage"; @@ -53,13 +49,13 @@ public class ModuleExceptionTest { @DataProvider(name = "dp") Object[][] createData(final Method m) { - return new Object[][]{new Object[]{new ModuleException(debugMessage, - message, - new IOException("URL http://foobar.com"))}, - new Object[]{new ModuleException(debugMessage, - message, - new IOException("URL http://foobar.com?"))}, - new Object[]{new ModuleException(debugMessage, message)}}; + return new Object[][]{ new Object[]{ new ModuleException(debugMessage, + message, + new IOException("URL http://foobar.com")) }, + new Object[]{ new ModuleException(debugMessage, + message, + new IOException("URL http://foobar.com?")) }, + new Object[]{ new ModuleException(debugMessage, message) } }; } @Test(dataProvider = "dp") @@ -78,7 +74,7 @@ public class ModuleExceptionTest { final ModuleException e = new ModuleException(debugMessage, message, new IOException( - "URL http://foo.com?apiKey=" + apiKey + "&userID=me")); + "URL http://foo.com?apiKey=" + apiKey + "&userID=me")); assertThat(e.getSanitizedMessage(apiKey)).as("sanitized url").contains("xxxxxxxxxx").doesNotContain(apiKey); } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt similarity index 75% rename from src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt index 564f111..1b0c460 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,5 +1,5 @@ /* - * PingTest.java + * PingTest.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,28 +29,25 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import org.testng.annotations.Test; - -import static org.assertj.core.api.Assertions.assertThat; +import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing +import org.assertj.core.api.Assertions +import org.testng.annotations.Test /** - * The <code>PingTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 + * The `PingTest` class. */ -public class PingTest { +class PingTest { @Test - public void testPingImpl() { - AbstractModuleTest.testAbstractModule(new Ping(null)); + fun testPingsArray() { + Assertions.assertThat(Ping.PINGS).`as`("Pings array is not empty.").isNotEmpty } @Test - public void testPingsArray() { - assertThat(Ping.PINGS).as("Pings array is not empty.").isNotEmpty(); + fun testRandomPing() { + for (i in 0..9) { + Assertions.assertThat(randomPing()).`as`("Random ping $i").isIn(Ping.PINGS) + } } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 0429945..1c71cb0 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules - import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java deleted file mode 100644 index a8961c6..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * StockQuoteTest.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.LocalProperties; -import net.thauvin.erik.mobibot.msg.Message; -import org.testng.annotations.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * The <code>StockQuoteTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-07 - * @since 1.0 - */ - -public class StockQuoteTest extends LocalProperties { - @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE") - @SuppressWarnings("PMD.PreserveStackTrace") - @Test - public void testGetQuote() throws ModuleException { - final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP); - try { - final List<Message> messages = StockQuote.getQuote("apple inc", apiKey); - assertThat(messages).as("response not empty").isNotEmpty(); - assertThat(messages.get(0).getMsg()).as("same stock symbol").contains("AAPL").contains("Apple Inc."); - - try { - StockQuote.getQuote("blahfoo", apiKey); - } catch (ModuleException e) { - assertThat(e.getMessage()).as("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL); - } - - assertThatThrownBy(() -> StockQuote.getQuote("test", "")).as("no API key").isInstanceOf( - ModuleException.class).hasNoCause(); - - assertThatThrownBy(() -> StockQuote.getQuote("", "apikey")).as("no symbol").isInstanceOf( - ModuleException.class).hasNoCause(); - - } catch (ModuleException e) { - // Avoid displaying api keys in CI logs - if ("true".equals(System.getenv("CI"))) { - throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey)); - } else { - throw e; - } - } - } - - @Test - public void testStockQuoteImpl() { - AbstractModuleTest.testAbstractModule(new StockQuote(null)); - } -} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt new file mode 100644 index 0000000..96cc1c1 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -0,0 +1,73 @@ +/* + * StockQuoteTest.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote +import org.assertj.core.api.Assertions +import org.testng.annotations.Test + +/** + * The `StockQuoteTest` class. + */ +class StockQuoteTest : LocalProperties() { + @Test + @Throws(ModuleException::class) + fun testGetQuote() { + val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP) + try { + val messages = getQuote("apple inc", apiKey) + Assertions.assertThat(messages).`as`("response not empty").isNotEmpty + Assertions.assertThat(messages[0].msg).`as`("same stock symbol") + .isEqualTo("Symbol: AAPL [Apple Inc.]") + Assertions.assertThat(messages[1].msg).`as`("price label") + .startsWith(" Price: ") + try { + getQuote("blahfoo", apiKey) + } catch (e: ModuleException) { + Assertions.assertThat(e.message).`as`("invalid symbol") + .containsIgnoringCase(StockQuote.INVALID_SYMBOL) + } + Assertions.assertThatThrownBy { getQuote("test", "") }.`as`("no API key") + .isInstanceOf(ModuleException::class.java).hasNoCause() + Assertions.assertThatThrownBy { getQuote("", "apikey") }.`as`("no symbol") + .isInstanceOf(ModuleException::class.java).hasNoCause() + } catch (e: ModuleException) { + // Avoid displaying api keys in CI logs + if ("true" == System.getenv("CI")) { + throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey)) + } else { + throw e + } + } + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt similarity index 62% rename from src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java rename to src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 2c14d2b..da9b26a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -1,5 +1,5 @@ /* - * TwitterTest.java + * TwitterTest.kt * * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,50 +29,43 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot.modules -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.LocalProperties; -import org.testng.annotations.Test; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -import static org.assertj.core.api.Assertions.assertThat; +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Twitter.Companion.twitterPost +import org.assertj.core.api.Assertions +import org.testng.annotations.Test +import java.net.InetAddress +import java.net.UnknownHostException /** - * The <code>TwitterTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-19 - * @since 1.0 + * The `TwitterTest` class. */ -public class TwitterTest extends LocalProperties { - @SuppressFBWarnings("MDM") - private String getCi() { - final String ciName = System.getenv("CI_NAME"); - if (ciName != null) { - return ciName; - } else { - try { - return InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - return "Unknown Host"; +class TwitterTest : LocalProperties() { + private val ci: String + get() { + val ciName = System.getenv("CI_NAME") + return ciName ?: try { + InetAddress.getLocalHost().hostName + } catch (e: UnknownHostException) { + "Unknown Host" } } - } @Test - public void testPostTwitter() throws ModuleException { - final String msg = "Testing Twitter API from " + getCi(); - assertThat(Twitter.twitterPost( + @Throws(ModuleException::class) + fun testPostTwitter() { + val msg = "Testing Twitter API from $ci" + Assertions.assertThat( + twitterPost( getProperty(Twitter.CONSUMER_KEY_PROP), getProperty(Twitter.CONSUMER_SECRET_PROP), getProperty(Twitter.TOKEN_PROP), getProperty(Twitter.TOKEN_SECRET_PROP), getProperty(Twitter.HANDLE_PROP), msg, - true).getMsg()).as("twitterPost(" + msg + ')').isEqualTo(msg); + true + ).msg + ).`as`("twitterPost($msg)").isEqualTo(msg) } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java deleted file mode 100644 index 6d8c15c..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Weather2Test.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.aksingh.owmjapis.api.APIException; -import net.thauvin.erik.mobibot.LocalProperties; -import net.thauvin.erik.mobibot.msg.Message; -import org.testng.annotations.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * The <code>Weather2Test</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-09 - * @since 1.0 - */ -public class Weather2Test extends LocalProperties { - @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") - @Test - public void testWeather() throws ModuleException { - List<Message> messages = Weather2.getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getMsg()).as("is Everett").contains("Everett").contains("US"); - assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Search").endsWith("98204%2CUS"); - - messages = Weather2.getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP)); - assertThat(messages.get(0).getMsg()).as("is UK").contains("London").contains("UK"); - assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Code").endsWith("4517009"); - - assertThatThrownBy(() -> Weather2.getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP))) - .as("Montpellier not found").hasCauseInstanceOf(APIException.class); - - assertThatThrownBy(() -> Weather2.getWeather("test", "")) - .as("no API key").isInstanceOf(ModuleException.class).hasNoCause(); - - messages = Weather2.getWeather("", "apikey"); - assertThat(messages.get(0).isError()).as("no query").isTrue(); - - } - - @Test - public void testWeather2Impl() { - AbstractModuleTest.testAbstractModule(new Weather2(null)); - } -} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt new file mode 100644 index 0000000..4364585 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -0,0 +1,60 @@ +/* + * Weather2Test.kt + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.aksingh.owmjapis.api.APIException +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather +import org.assertj.core.api.Assertions +import org.testng.annotations.Test + +/** + * The `Weather2Test` class. + */ +class Weather2Test : LocalProperties() { + @Test + @Throws(ModuleException::class) + fun testWeather() { + var messages = getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP)) + Assertions.assertThat(messages[0].msg).`as`("is Everett").contains("Everett").contains("US") + Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Search").endsWith("98204%2CUS") + messages = getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP)) + Assertions.assertThat(messages[0].msg).`as`("is UK").contains("London").contains("UK") + Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Code").endsWith("4517009") + Assertions.assertThatThrownBy { getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)) } + .`as`("Montpellier not found").hasCauseInstanceOf(APIException::class.java) + Assertions.assertThatThrownBy { getWeather("test", "") } + .`as`("no API key").isInstanceOf(ModuleException::class.java).hasNoCause() + messages = getWeather("", "apikey") + Assertions.assertThat(messages[0].isError).`as`("no query").isTrue + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt new file mode 100644 index 0000000..b6a40aa --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -0,0 +1,49 @@ +/* + * WordTimeTest.kt + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.modules.WorldTime.Companion.worldTime +import org.assertj.core.api.Assertions +import org.testng.annotations.Test + +/** + * The `WordTimeTest` class. + */ +class WordTimeTest { + @Test + fun testWorldTime() { + Assertions.assertThat(worldTime("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles")) + Assertions.assertThat(worldTime("BLAH").isError).`as`("BLAH").isTrue + Assertions.assertThat(worldTime("BEATS").msg).`as`("BEATS").contains("@") + } +} From 550e7a0bfb92b398f3ed5103ecaa47207af3c83b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 2 Dec 2020 01:49:01 -0800 Subject: [PATCH 436/842] Version 0.8.0-beta+219 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index b4d9e77..b3c36ca 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,14 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+125"; + public static final String VERSION = "0.8.0-beta+219"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1606812548443L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1606902256759L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String BUILDMETA = "125"; + public static final String BUILDMETA = "219"; public static final String PRERELEASE = "beta"; public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; diff --git a/version.properties b/version.properties index ea8fc92..7e1d074 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Dec 01 00:49:06 PST 2020 -version.buildmeta=125 +#Wed Dec 02 01:44:16 PST 2020 +version.buildmeta=219 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+125 +version.semver=0.8.0-beta+219 From d83ce652e705daed76cd27206687f738ae2688d0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 2 Dec 2020 14:20:53 -0800 Subject: [PATCH 437/842] Cleanup. --- .../java/net/thauvin/erik/mobibot/Utils.kt | 26 +++++++++++-------- .../erik/mobibot/modules/GoogleSearch.kt | 4 +-- .../thauvin/erik/mobibot/modules/Lookup.kt | 4 ++- .../erik/mobibot/modules/ModuleException.kt | 15 +++++++---- .../erik/mobibot/modules/StockQuote.kt | 4 +-- .../thauvin/erik/mobibot/modules/Weather2.kt | 8 +++--- .../thauvin/erik/mobibot/modules/WorldTime.kt | 3 +-- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- 9 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index c1cae4b..e14f1eb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -76,7 +76,7 @@ class Utils private constructor() { */ @JvmStatic fun colorize(s: String?, color: String): String { - if (s == null) { + if (s.isNullOrBlank()) { return Colors.NORMAL } else if (Colors.BOLD == color || Colors.REVERSE == color) { return color + s + color @@ -95,7 +95,7 @@ class Utils private constructor() { /** * URL encodes the given string. */ - fun encodeUrl(s: String?): String { + fun encodeUrl(s: String): String { return URLEncoder.encode(s, StandardCharsets.UTF_8) } @@ -127,11 +127,15 @@ class Utils private constructor() { * Returns a property as an int. */ @JvmStatic - fun getIntProperty(property: String, def: Int): Int { - return try { - property.toInt() - } catch (ignore: NumberFormatException) { + fun getIntProperty(property: String?, def: Int): Int { + return if (property == null) { def + } else { + try { + property.toInt() + } catch (ignore: NumberFormatException) { + def + } } } @@ -148,7 +152,7 @@ class Utils private constructor() { * nick. */ @JvmStatic - fun helpFormat(text: String?, botNick: String, isPrivate: Boolean): String { + fun helpFormat(text: String, botNick: String, isPrivate: Boolean): String { val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) return StringUtils.replaceEach(text, searchFlags, replace) } @@ -158,7 +162,7 @@ class Utils private constructor() { */ @JvmStatic @JvmOverloads - fun helpIndent(help: String?, isBold: Boolean = true): String { + fun helpIndent(help: String, isBold: Boolean = true): String { return " " + if (isBold) bold(help) else help } @@ -182,8 +186,8 @@ class Utils private constructor() { * Obfuscates the given string. */ @JvmStatic - fun obfuscate(s: String?): String { - return if (s!!.isNotBlank()) { + fun obfuscate(s: String): String { + return if (s.isNotBlank()) { StringUtils.repeat('x', s.length) } else s } @@ -228,7 +232,7 @@ class Utils private constructor() { * Converts XML/XHTML entities to plain text. */ @JvmStatic - fun unescapeXml(str: String?): String { + fun unescapeXml(str: String): String { return Jsoup.parse(str).text() } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index b05a0cb..891a6f2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -87,10 +87,10 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message> { - if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) { + if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { throw ModuleException("${StringUtils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.") } - return if (StringUtils.isNotBlank(query)) { + return if (query.isNotBlank()) { val results = ArrayList<Message>() try { val url = URL( diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt index cca419e..c46baf0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -52,7 +52,9 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { if (args.matches("(\\S.)+(\\S)+".toRegex())) { with(bot) { try { - send(lookup(args)) + lookup(args).split(',').forEach { + send(it.trim()) + } } catch (ignore: UnknownHostException) { if (args.matches( ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt index 3b10ada..f9a3d81 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -67,12 +67,17 @@ class ModuleException : Exception { /** * Return the sanitized message (e.g. remove API keys, etc.) */ - fun getSanitizedMessage(vararg sanitize: String?): String { - val obfuscate = arrayOfNulls<String>(sanitize.size) - for (i in sanitize.indices) { - obfuscate[i] = Utils.obfuscate(sanitize[i]) + fun getSanitizedMessage(vararg sanitize: String): String { + val obfuscate = sanitize.map { Utils.obfuscate(it) }.toTypedArray() + return when { + cause != null -> { + cause.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate) + } + message != null -> { + message.javaClass.name + ": " + StringUtils.replaceEach(message, sanitize, obfuscate) + } + else -> "" } - return cause!!.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate) } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index bcf1838..434ca54 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -122,10 +122,10 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> { - if (StringUtils.isBlank(apiKey)) { + if (apiKey.isNullOrBlank()) { throw ModuleException("${STOCK_CMD.capitalize()} is disabled. The API key is missing.") } - return if (StringUtils.isNotBlank(symbol)) { + return if (symbol.isNotBlank()) { val debugMessage = "getQuote($symbol)" val messages = ArrayList<Message>() var response: String diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index 2dea6c7..d1dcf25 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -101,17 +101,17 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> { - if (StringUtils.isBlank(apiKey)) { + if (apiKey.isNullOrBlank()) { throw ModuleException("${WEATHER_CMD.capitalize()} is disabled. The API key is missing.") } - val owm = OWM(apiKey!!) + val owm = OWM(apiKey) val messages = ArrayList<Message>() owm.unit = OWM.Unit.IMPERIAL - if (StringUtils.isNotBlank(query)) { + if (query.isNotBlank()) { val argv = query.split(",").toTypedArray() if (argv.size in 1..2) { val city = argv[0].trim() - val country: String = if (argv.size > 1 && StringUtils.isNotBlank(argv[1])) { + val country: String = if (argv.size > 1 && argv[1].isNotBlank()) { argv[1].trim() } else { "US" diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index 8ecc469..cafe903 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -36,7 +36,6 @@ import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage -import org.apache.commons.lang3.StringUtils import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter @@ -72,7 +71,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { */ @JvmStatic fun worldTime(query: String): Message { - val tz = COUNTRIES_MAP[StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim())] + val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).toUpperCase()] val response: String = if (tz != null) { if (BEATS_KEYWORD == tz) { "The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index b934b02..019538f 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -62,7 +62,7 @@ class GoogleSearchTest : LocalProperties() { .`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI")) { + if ("true" == System.getenv("CI") && !apiKey.isNullOrBlank() && !cseKey.isNullOrBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey)) } else { throw e diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 96cc1c1..ea78c34 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -63,7 +63,7 @@ class StockQuoteTest : LocalProperties() { .isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI")) { + if ("true" == System.getenv("CI") && !apiKey.isNullOrBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey)) } else { throw e From 310ffb91daeb12852322dfa54ad9bc3fddb30a06 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 2 Dec 2020 14:21:22 -0800 Subject: [PATCH 438/842] Version 0.8.0-beta+234 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index b3c36ca..b548cec 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,14 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+219"; + public static final String VERSION = "0.8.0-beta+234"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1606902256759L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1606946086512L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String BUILDMETA = "219"; + public static final String BUILDMETA = "234"; public static final String PRERELEASE = "beta"; public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; diff --git a/version.properties b/version.properties index 7e1d074..418f0b7 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Dec 02 01:44:16 PST 2020 -version.buildmeta=219 +#Wed Dec 02 13:54:44 PST 2020 +version.buildmeta=234 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+219 +version.semver=0.8.0-beta+234 From 7759e278e89aa879ee8023aa045c8d0d817ce25e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 2 Dec 2020 22:57:25 -0800 Subject: [PATCH 439/842] Converter Entry Links manipulation classes to Kotlin. --- config/detekt/baseline.xml | 5 + .../net/thauvin/erik/mobibot/PinboardUtils.kt | 62 +-- .../thauvin/erik/mobibot/commands/Cycle.kt | 18 +- .../thauvin/erik/mobibot/commands/Modules.kt | 16 +- .../erik/mobibot/commands/links/Comment.kt | 4 +- .../erik/mobibot/commands/links/LinksMgr.kt | 3 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/tell/Tell.java | 4 +- .../commands/tell/TellMessagesMgr.java | 9 +- .../erik/mobibot/entries/EntriesMgr.java | 331 -------------- .../erik/mobibot/entries/EntriesMgr.kt | 261 +++++++++++ .../erik/mobibot/entries/EntriesUtils.java | 120 ----- .../erik/mobibot/entries/EntriesUtils.kt | 83 ++++ .../erik/mobibot/entries/EntryComment.java | 130 ------ .../erik/mobibot/entries/EntryComment.kt | 54 +++ .../erik/mobibot/entries/EntryLink.java | 418 ------------------ .../thauvin/erik/mobibot/entries/EntryLink.kt | 223 ++++++++++ .../thauvin/erik/mobibot/modules/Twitter.kt | 7 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 14 +- ...ocalProperties.java => LocalProperties.kt} | 75 ++-- .../erik/mobibot/entries/EntryLinkTest.java | 100 ----- .../erik/mobibot/entries/EntryLinkTest.kt | 92 ++++ 22 files changed, 818 insertions(+), 1213 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt rename src/test/java/net/thauvin/erik/mobibot/{LocalProperties.java => LocalProperties.kt} (57%) delete mode 100644 src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java create mode 100644 src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index ed4ec34..07b0938 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,7 +2,9 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> + <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>LongMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> @@ -43,6 +45,9 @@ <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> + <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: ArrayList<EntryLink>): String</ID> + <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ fun setTags(tags: List<String?>)</ID> <ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt index 3456a8d..1dad880 100644 --- a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -44,17 +44,10 @@ import java.util.* /** * The class to handle posts to pinboard.in. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net) - * @created 2017-05-17 - * @since 1.0 */ object PinboardUtils { /** * Adds a pin. - * - * @param poster The PinboardPoster instance. - * @param entry The entry to add. */ @JvmStatic fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking { @@ -72,9 +65,6 @@ object PinboardUtils { /** * Deletes a pin. - * - * @param poster The PinboardPoster instance. - * @param entry The entry to delete. */ @JvmStatic fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking { @@ -86,33 +76,31 @@ object PinboardUtils { /** * Updates a pin. - * - * @param poster The PinboardPoster instance. - * @param oldUrl The old post URL. - * @param entry The entry to add. */ @JvmStatic fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { val update = GlobalScope.async { - if (oldUrl != entry.link) { - poster.deletePin(oldUrl) - poster.addPin( - entry.link, - entry.title, - postedBy(entry, ircServer), - entry.pinboardTags, - formatDate(entry.date) - ) - } else { - poster.addPin( - entry.link, - entry.title, - postedBy(entry, ircServer), - entry.pinboardTags, - formatDate(entry.date), - replace = true, - shared = true - ) + with(entry) { + if (oldUrl != link) { + poster.deletePin(oldUrl) + poster.addPin( + link, + title, + postedBy(entry, ircServer), + pinboardTags, + formatDate(date) + ) + } else { + poster.addPin( + link, + title, + postedBy(entry, ircServer), + pinboardTags, + formatDate(date), + replace = true, + shared = true + ) + } } } update.await() @@ -120,19 +108,13 @@ object PinboardUtils { /** * Format a date to a UTC timestamp. - * - * @param date The date. - * @return The date in [DateTimeFormatter.ISO_INSTANT] format. */ private fun formatDate(date: Date): String { return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT) } /** - * Returns he pinboard.in extended attribution line. - * - * @param entry The entry. - * @return The extended attribution line. + * Returns the pinboard.in extended attribution line. */ private fun postedBy(entry: EntryLink, ircServer: String): String { return "Posted by ${entry.nick} on ${entry.channel} ( $ircServer )" diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index 1ca4ed2..525ad33 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -51,14 +51,16 @@ class Cycle(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - if (isOp) { - bot.send("$sender has just asked me to leave. I'll be back!") - bot.sleep(wait) - bot.partChannel(bot.channel) - bot.sleep(wait) - bot.joinChannel(bot.channel) - } else { - bot.helpDefault(sender, isOp, isPrivate) + with(bot) { + if (isOp) { + send("$sender has just asked me to leave. I'll be back!") + sleep(wait) + partChannel(channel) + sleep(wait) + joinChannel(channel) + } else { + helpDefault(sender, isOp, isPrivate) + } } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index af2f629..447552c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -52,17 +52,17 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - if (isOp) { - with(bot.modulesNames) { - if (isEmpty()) { - bot.send(sender, "There are no enabled modules.", isPrivate) + with(bot) { + if (isOp) { + if (modulesNames.isEmpty()) { + send(sender, "There are no enabled modules.", isPrivate) } else { - bot.send(sender, "The enabled modules are: ", isPrivate) - bot.sendList(sender, this, 7, isPrivate, false) + send(sender, "The enabled modules are: ", isPrivate) + sendList(sender, modulesNames, 7, isPrivate, false) } + } else { + helpDefault(sender, isOp, isPrivate) } - } else { - bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 63f5ffa..00b718a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -71,7 +71,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { if (index < LinksMgr.entriesCount) { val entry: EntryLink = LinksMgr.getEntry(index) val commentIndex = cmds[1].toInt() - 1 - if (commentIndex < entry.commentsCount) { + if (commentIndex < entry.comments.size) { when (val cmd = cmds[2].trim()) { "" -> showComment(bot, entry, index, commentIndex) // L1.1: "-" -> deleteComment(bot, sender, isOp, entry, index, commentIndex) // L11:- @@ -140,7 +140,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { ) { if (isOp || sender == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) - bot.send("Comment ${Constants.LINK_CMD}${index + 1}.${commentIndex + 1} removed.") + bot.send("Comment ${EntriesUtils.buildLinkCmd(index)}.${commentIndex + 1} removed.") LinksMgr.saveEntries(bot, false) } else { bot.send(sender, "Please ask a channel op to delete this comment for you.", false) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 6db470a..ac5257d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -40,6 +40,7 @@ import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.entries.EntriesMgr import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink +import org.apache.logging.log4j.LogManager import org.jsoup.Jsoup import java.io.IOException @@ -166,7 +167,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { bot.send(sender, "Please specify a title, by typing:", isPrivate) bot.send( sender, - Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title"), + Utils.helpIndent("${EntriesUtils.buildLinkCmd(index)}:|This is the title"), isPrivate ) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 26a74ce..bf275f5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -137,7 +137,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { if (entry.login == login || isOp) { bot.deletePin(index, entry) LinksMgr.removeEntry(index) - bot.send("Entry ${Constants.LINK_CMD}${index + 1} removed.") + bot.send("Entry ${EntriesUtils.buildLinkCmd(index)} removed.") LinksMgr.saveEntries(bot, false) } else { bot.send(sender, "Please ask a channel op to remove this entry for you.", false) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java index 8470320..d3fa026 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java @@ -104,7 +104,9 @@ public class Tell extends AbstractCommand { */ @SuppressWarnings("WeakerAccess") final boolean clean() { - getBot().getLogger().debug("Cleaning the messages."); + if (getBot().getLogger().isDebugEnabled()) { + getBot().getLogger().debug("Cleaning the messages."); + } return TellMessagesMgr.clean(messages, maxDays); } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java index 6be49cd..98eed8a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java @@ -94,8 +94,9 @@ final class TellMessagesMgr { try { try (final ObjectInput input = new ObjectInputStream( new BufferedInputStream(Files.newInputStream(Paths.get(file))))) { - logger.debug("Loading the messages."); - + if (logger.isDebugEnabled()) { + logger.debug("Loading the messages."); + } return ((List<TellMessage>) input.readObject()); } } catch (FileNotFoundException ignore) { @@ -118,7 +119,9 @@ final class TellMessagesMgr { try { try (final BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(file)))) { try (final ObjectOutput output = new ObjectOutputStream(bos)) { - logger.debug("Saving the messages."); + if (logger.isDebugEnabled()) { + logger.debug("Saving the messages."); + } output.writeObject(messages); } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java deleted file mode 100644 index 42c1fb9..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * EntriesMgr.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries; - -import com.rometools.rome.feed.synd.SyndContent; -import com.rometools.rome.feed.synd.SyndContentImpl; -import com.rometools.rome.feed.synd.SyndEntry; -import com.rometools.rome.feed.synd.SyndEntryImpl; -import com.rometools.rome.feed.synd.SyndFeed; -import com.rometools.rome.feed.synd.SyndFeedImpl; -import com.rometools.rome.io.FeedException; -import com.rometools.rome.io.SyndFeedInput; -import com.rometools.rome.io.SyndFeedOutput; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import org.apache.commons.lang3.StringUtils; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.List; - -/** - * Manages the feed entries. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-28 - * @since 1.0 - */ -public final class EntriesMgr { - /** - * The name of the file containing the current entries. - */ - public static final String CURRENT_XML = "current.xml"; - - /** - * The name of the file containing the backlog entries. - */ - public static final String NAV_XML = "nav.xml"; - - /** - * The .xml extension - */ - public static final String XML_EXT = ".xml"; - - // Maximum number of backlogs to keep - private static final int MAX_BACKLOGS = 10; - - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private EntriesMgr() { - throw new UnsupportedOperationException("Illegal constructor call."); - } - - /** - * Loads the backlogs. - * - * @param file The file containing the backlogs. - * @param history The history list. - * @throws IOException If the file was not found or could not be read. - * @throws FeedException If an error occurred while reading the feed. - */ - public static void loadBacklogs(final String file, final Collection<String> history) - throws IOException, FeedException { - history.clear(); - - final SyndFeedInput input = new SyndFeedInput(); - - try (final InputStreamReader reader = - new InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { - - final SyndFeed feed = input.build(reader); - - final List<SyndEntry> items = feed.getEntries(); - SyndEntry item; - - for (int i = items.size() - 1; i >= 0; i--) { - item = items.get(i); - history.add(item.getTitle()); - } - } - } - - /** - * Loads the current entries. - * - * @param file The file containing the current entries. - * @param channel The channel - * @param entries The entries. - * @return The feed's last published date. - * @throws IOException If the file was not found or could not be read. - * @throws FeedException If an error occurred while reading the feed. - */ - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public static String loadEntries(final String file, final String channel, final Collection<EntryLink> entries) - throws IOException, FeedException { - entries.clear(); - - final SyndFeedInput input = new SyndFeedInput(); - - final String today; - - try (final InputStreamReader reader = new InputStreamReader( - Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) { - final SyndFeed feed = input.build(reader); - - today = Utils.isoLocalDate(feed.getPublishedDate()); - - final List<SyndEntry> items = feed.getEntries(); - SyndEntry item; - SyndContent description; - String[] comments; - String author; - EntryLink entry; - - for (int i = items.size() - 1; i >= 0; i--) { - item = items.get(i); - author = item.getAuthor() - .substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1); - entry = new EntryLink(item.getLink(), - item.getTitle(), - author, - channel, - item.getPublishedDate(), - item.getCategories()); - description = item.getDescription(); - comments = description.getValue().split("<br/>"); - - int split; - for (final String comment : comments) { - split = comment.indexOf(": "); - - if (split != -1) { - entry.addComment(comment.substring(split + 2).trim(), comment.substring(0, split).trim()); - } - } - - entries.add(entry); - } - } - - return today; - } - - /** - * Saves the entries. - * - * @param bot The bot object. - * @param entries The entries array. - * @param history The history array. - * @param isDayBackup Set the true if the daily backup file should also be created. - */ - @SuppressFBWarnings(value = { "CE_CLASS_ENVY", "CC_CYCLOMATIC_COMPLEXITY" }, - justification = "Yes, it does.") - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public static void saveEntries(final Mobibot bot, - final List<EntryLink> entries, - final List<String> history, - final boolean isDayBackup) { - bot.getLogger().debug("Saving the feeds..."); - - if (StringUtils.isNotBlank(bot.getLogsDir()) && StringUtils.isNotBlank(bot.getWeblogUrl())) { - try { - final SyndFeedOutput output = new SyndFeedOutput(); - SyndFeed rss = new SyndFeedImpl(); - final List<SyndEntry> items = new ArrayList<>(0); - SyndEntry item; - SyndContent description; - try (final Writer fw = new OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.getLogsDir() + CURRENT_XML)), StandardCharsets.UTF_8)) { - rss.setFeedType("rss_2.0"); - rss.setTitle(bot.getChannel() + " IRC Links"); - rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel()); - rss.setLink(bot.getWeblogUrl()); - rss.setPublishedDate(Calendar.getInstance().getTime()); - rss.setLanguage("en"); - - EntryLink entry; - StringBuilder buff; - EntryComment comment; - - for (int i = (entries.size() - 1); i >= 0; --i) { - entry = entries.get(i); - - buff = new StringBuilder() - .append("Posted by <b>") - .append(entry.getNick()) - .append("</b> on <a href=\"irc://") - .append(bot.getIrcServer()).append('/') - .append(entry.getChannel()) - .append("\"><b>") - .append(entry.getChannel()) - .append("</b></a>"); - - if (entry.getCommentsCount() > 0) { - buff.append(" <br/><br/>"); - - final EntryComment[] comments = entry.getComments(); - - for (int j = 0; j < comments.length; j++) { - comment = comments[j]; - - if (j > 0) { - buff.append(" <br/>"); - } - - buff.append(comment.getNick()).append(": ").append(comment.getComment()); - } - } - - item = new SyndEntryImpl(); - item.setLink(entry.getLink()); - description = new SyndContentImpl(); - description.setValue(buff.toString()); - item.setDescription(description); - item.setTitle(entry.getTitle()); - item.setPublishedDate(entry.getDate()); - item.setAuthor( - bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() - + ')'); - item.setCategories(entry.getTags()); - - items.add(item); - } - - rss.setEntries(items); - - bot.getLogger().debug("Writing the entries feed."); - output.output(rss, fw); - } - - try (final Writer fw = new OutputStreamWriter( - Files.newOutputStream(Paths.get( - bot.getLogsDir() + bot.getToday() + XML_EXT)), StandardCharsets.UTF_8)) { - output.output(rss, fw); - } - - if (isDayBackup) { - if (StringUtils.isNotBlank(bot.getBacklogsUrl())) { - if (!history.contains(bot.getToday())) { - history.add(bot.getToday()); - - while (history.size() > MAX_BACKLOGS) { - history.remove(0); - } - } - - try (final Writer fw = new OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.getLogsDir() + NAV_XML)), StandardCharsets.UTF_8)) { - rss = new SyndFeedImpl(); - rss.setFeedType("rss_2.0"); - rss.setTitle(bot.getChannel() + " IRC Links Backlogs"); - rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on " - + bot.getChannel()); - rss.setLink(bot.getBacklogsUrl()); - rss.setPublishedDate(Calendar.getInstance().getTime()); - - String date; - items.clear(); - - for (int i = (history.size() - 1); i >= 0; --i) { - date = history.get(i); - - item = new SyndEntryImpl(); - item.setLink(bot.getBacklogsUrl() + date + ".xml"); - item.setTitle(date); - description = new SyndContentImpl(); - description.setValue("Links for " + date); - item.setDescription(description); - - items.add(item); - } - - rss.setEntries(items); - - bot.getLogger().debug("Writing the backlog feed."); - output.output(rss, fw); - } - } else { - bot.getLogger().warn("Unable to generate the backlogs feed. No property configured."); - } - } - } catch (FeedException | IOException e) { - bot.getLogger().warn("Unable to generate the entries feed.", e); - } - } else { - bot.getLogger().warn("Unable to generate the entries feed. A required property is missing."); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt new file mode 100644 index 0000000..a526542 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -0,0 +1,261 @@ +/* + * EntriesMgr.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import com.rometools.rome.feed.synd.SyndContentImpl +import com.rometools.rome.feed.synd.SyndEntry +import com.rometools.rome.feed.synd.SyndEntryImpl +import com.rometools.rome.feed.synd.SyndFeed +import com.rometools.rome.feed.synd.SyndFeedImpl +import com.rometools.rome.io.FeedException +import com.rometools.rome.io.SyndFeedInput +import com.rometools.rome.io.SyndFeedOutput +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate +import org.apache.commons.lang3.StringUtils +import java.io.IOException +import java.io.InputStreamReader +import java.io.OutputStreamWriter +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* +import kotlin.collections.ArrayList + +/** + * Manages the feed entries. + */ +class EntriesMgr private constructor() { + + + companion object { + /** + * The name of the file containing the current entries. + */ + const val CURRENT_XML = "current.xml" + + /** + * The name of the file containing the backlog entries. + */ + const val NAV_XML = "nav.xml" + + /** + * The .xml extension + */ + const val XML_EXT = ".xml" + + // Maximum number of backlogs to keep + private const val MAX_BACKLOGS = 10 + + /** + * Loads the backlogs. + */ + @Throws(IOException::class, FeedException::class) + fun loadBacklogs(file: String, history: ArrayList<String>) { + history.clear() + val input = SyndFeedInput() + InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader -> + val feed = input.build(reader) + val items = feed.entries + for (i in items.indices.reversed()) { + history.add(items[i].title) + } + } + } + + /** + * Loads the current entries. + */ + @Throws(IOException::class, FeedException::class) + fun loadEntries(file: String, channel: String, entries: ArrayList<EntryLink>): String { + entries.clear() + val input = SyndFeedInput() + var today: String + InputStreamReader( + Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8 + ).use { reader -> + val feed = input.build(reader) + today = isoLocalDate(feed.publishedDate) + val items = feed.entries + var entry: EntryLink + for (i in items.indices.reversed()) { + with(items[i]) { + entry = EntryLink( + link, + title, + author.substring(author.lastIndexOf('(') + 1, author.length - 1), + channel, + publishedDate, + categories + ) + var split: List<String> + for (comment in description.value.split("<br/>")) { + split = comment.split(": ".toRegex(), 2) + if (split.size == 2) { + entry.addComment(comment = split[1].trim(), nick = split[0].trim()) + } + } + } + entries.add(entry) + } + } + return today + } + + /** + * Saves the entries. + */ + fun saveEntries( + bot: Mobibot, + entries: List<EntryLink>, + history: MutableList<String>, + isDayBackup: Boolean + ) { + if (bot.logger.isDebugEnabled) { + bot.logger.debug("Saving the feeds...") + } + if (StringUtils.isNotBlank(bot.logsDir) && StringUtils.isNotBlank(bot.weblogUrl)) { + try { + val output = SyndFeedOutput() + var rss: SyndFeed = SyndFeedImpl() + val items: MutableList<SyndEntry> = ArrayList(0) + var item: SyndEntry + OutputStreamWriter( + Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8 + ).use { fw -> + rss.apply { + feedType = "rss_2.0" + title = bot.channel + " IRC Links" + description = "Links from ${bot.ircServer} on ${bot.channel}" + link = bot.weblogUrl + publishedDate = Calendar.getInstance().time + language = "en" + } + var buff: StringBuilder + var comment: EntryComment + for (i in entries.size - 1 downTo 0) { + with(entries[i]) { + buff = StringBuilder() + .append("Posted by <b>") + .append(nick) + .append("</b> on <a href=\"irc://") + .append(bot.ircServer).append('/') + .append(channel) + .append("\"><b>") + .append(channel) + .append("</b></a>") + if (comments.size > 0) { + buff.append(" <br/><br/>") + val comments = comments + for (j in comments.indices) { + comment = comments[j] + if (j > 0) { + buff.append(" <br/>") + } + buff.append(comment.nick).append(": ").append(comment.comment) + } + } + item = SyndEntryImpl() + item.link = link + item.description = SyndContentImpl().apply { value = buff.toString() } + item.title = title + item.publishedDate = date + item.author = "${bot.channel.substring(1)}@${bot.ircServer} ($nick)" + item.categories = tags + items.add(item) + } + } + rss.entries = items + if (bot.logger.isDebugEnabled) { + bot.logger.debug("Writing the entries feed.") + } + output.output(rss, fw) + } + OutputStreamWriter( + Files.newOutputStream( + Paths.get( + bot.logsDir + bot.today + XML_EXT + ) + ), StandardCharsets.UTF_8 + ).use { fw -> output.output(rss, fw) } + if (isDayBackup) { + if (StringUtils.isNotBlank(bot.backlogsUrl)) { + if (!history.contains(bot.today)) { + history.add(bot.today) + while (history.size > MAX_BACKLOGS) { + history.removeAt(0) + } + } + OutputStreamWriter( + Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8 + ).use { fw -> + rss = SyndFeedImpl() + rss.apply { + feedType = "rss_2.0" + title = "${bot.channel} IRC Links Backlogs" + description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}" + link = bot.backlogsUrl + publishedDate = Calendar.getInstance().time + } + var date: String + items.clear() + for (i in history.size - 1 downTo 0) { + date = history[i] + item = SyndEntryImpl() + item.apply { + link = bot.backlogsUrl + date + ".xml" + title = date + description = SyndContentImpl().apply { value = "Links for $date" } + } + items.add(item) + } + rss.entries = items + if (bot.logger.isDebugEnabled) { + bot.logger.debug("Writing the backlog feed.") + } + output.output(rss, fw) + } + } else { + bot.logger.warn("Unable to generate the backlogs feed. No property configured.") + } + } + } catch (e: FeedException) { + bot.logger.warn("Unable to generate the entries feed.", e) + } catch (e: IOException) { + bot.logger.warn("Unable to generate the entries feed.", e) + } + } else { + bot.logger.warn("Unable to generate the entries feed. A required property is missing.") + } + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java deleted file mode 100644 index e114d66..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * EntriesUtils.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.Constants; -import net.thauvin.erik.mobibot.Utils; - -/** - * The <code>Utils</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-19 - * @since 1.0 - */ -public final class EntriesUtils { - /** - * Disables the default constructor. - */ - private EntriesUtils() { - throw new UnsupportedOperationException("Illegal constructor call."); - } - - /** - * Builds an entry's comment for display on the channel. - * - * @param entryIndex The entry's index. - * @param commentIndex The comment's index. - * @param comment The {@link EntryComment comment} object. - * @return The entry's comment. - */ - public static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) { - return (Constants.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] " - + comment.getComment()); - } - - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link EntryLink entry} object. - * @return The entry's link. - * @see #buildLink(int, EntryLink, boolean) - */ - public static String buildLink(final int index, final EntryLink entry) { - return buildLink(index, entry, false); - } - - /** - * Builds an entry's link for display on the channel. - * - * @param index The entry's index. - * @param entry The {@link EntryLink entry} object. - * @param isView Set to true to display the number of comments. - * @return The entry's link. - */ - @SuppressFBWarnings(value = "CE_CLASS_ENVY", - justification = "Yes, it does.") - public static String buildLink(final int index, final EntryLink entry, final boolean isView) { - final StringBuilder buff = new StringBuilder().append(Constants.LINK_CMD).append(index + 1) - .append(": ").append('[').append(entry.getNick()).append(']'); - - if (isView && entry.hasComments()) { - buff.append("[+").append(entry.getCommentsCount()).append(']'); - } - - buff.append(' '); - - if (Constants.NO_TITLE.equals(entry.getTitle())) { - buff.append(entry.getTitle()); - } else { - buff.append(Utils.bold(entry.getTitle())); - } - - buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )"); - - return buff.toString(); - } - - /** - * Build an entry's tags/categories for display on the channel. - * - * @param entryIndex The entry's index. - * @param entry The {@link EntryLink entry} object. - * @return The entry's tags. - */ - public static String buildTags(final int entryIndex, final EntryLink entry) { - return (Constants.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", ")); - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt new file mode 100644 index 0000000..a96bad0 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -0,0 +1,83 @@ +/* + * EntriesUtils.kt + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils.Companion.bold +import net.thauvin.erik.mobibot.Utils.Companion.green + +/** + * The `Utils` class. + */ +class EntriesUtils private constructor() { + companion object { + /** + * Build link cmd based on its index. e.g: L1 + */ + fun buildLinkCmd(index: Int): String = Constants.LINK_CMD + (index + 1) + + /** + * Builds an entry's comment for display on the channel. + */ + fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = + (buildLinkCmd(entryIndex) + '.' + (commentIndex + 1) + ": [" + comment.nick + "] " + + comment.comment) + + /** + * Builds an entry's link for display on the channel. + */ + @JvmOverloads + fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { + val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ") + .append('[').append(entry.nick).append(']') + if (isView && entry.hasComments()) { + buff.append("[+").append(entry.comments.size).append(']') + } + buff.append(' ') + with(entry) { + if (Constants.NO_TITLE == title) { + buff.append(title) + } else { + buff.append(bold(title)) + } + buff.append(" ( ").append(green(link)).append(" )") + } + return buff.toString() + } + + /** + * Build an entry's tags/categories for display on the channel. + */ + fun buildTags(entryIndex: Int, entry: EntryLink): String = + buildLinkCmd(entryIndex) + "T: " + entry.pinboardTags.replace(",", ", ") + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java deleted file mode 100644 index 5c51f1a..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * EntryComment.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries; - -import java.io.Serializable; -import java.time.LocalDateTime; - -/** - * The class used to store comments associated to a specific entry. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Jan 31, 2004 - * @since 1.0 - */ -@SuppressWarnings({ "PMD.DataClass" }) -public class EntryComment implements Serializable { - // Serial version UID - static final long serialVersionUID = 1L; - - // Creation date - private final LocalDateTime date = LocalDateTime.now(); - - private String comment = ""; - private String nick = ""; - - /** - * Creates a new comment. - * - * @param comment The new comment. - * @param nick The nickname of the comment's author. - */ - public EntryComment(final String comment, final String nick) { - this.comment = comment; - this.nick = nick; - } - - /** - * Creates a new comment. - */ - @SuppressWarnings("UnusedDeclaration") - protected EntryComment() { - // Required for serialization - } - - /** - * Returns the comment. - * - * @return The comment. - */ - public final String getComment() { - return comment; - } - - /** - * Returns the comment's creation date. - * - * @return The date. - */ - @SuppressWarnings("UnusedDeclaration") - public final LocalDateTime getDate() { - return date; - } - - /** - * Returns the nickname of the author of the comment. - * - * @return The nickname. - */ - public final String getNick() { - return nick; - } - - /** - * Sets the comment. - * - * @param comment The actual comment. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setComment(final String comment) { - this.comment = comment; - } - - /** - * Sets the nickname of the author of the comment. - * - * @param nick The new nickname. - */ - public final void setNick(final String nick) { - this.nick = nick; - } - - @Override - public String toString() { - return "EntryComment{" - + "comment='" + comment + '\'' - + ", date=" + date - + ", nick='" + nick + '\'' - + '}'; - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt new file mode 100644 index 0000000..0052a0f --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -0,0 +1,54 @@ +/* + * EntryComment.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Entry comments data class. + */ +data class EntryComment(var comment: String, var nick: String) : Serializable { + /** + * Creation date. + */ + val date: LocalDateTime = LocalDateTime.now() + + override fun toString(): String { + return ("EntryComment{comment='$comment', date=$date, nick='$nick'}") + } + + companion object { + // Serial version UID + const val serialVersionUID = 1L + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java deleted file mode 100644 index ba9a3f5..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * EntryLink.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries; - -import com.rometools.rome.feed.synd.SyndCategory; -import com.rometools.rome.feed.synd.SyndCategoryImpl; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.commands.links.LinksMgr; -import org.apache.commons.lang3.StringUtils; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * The class used to store link entries. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Jan 31, 2004 - * @since 1.0 - */ -public class EntryLink implements Serializable { - // Serial version UID - static final long serialVersionUID = 1L; - - // Link's comments - private final List<EntryComment> comments = new CopyOnWriteArrayList<>(); - - // Tags/categories - private final List<SyndCategory> tags = new CopyOnWriteArrayList<>(); - - // Channel - private String channel; - - // Creation date - private Date date = Calendar.getInstance().getTime(); - - // Link's URL - private String link; - - // Author's login - private String login = ""; - - // Author's nickname - private String nick; - - // Link's title - private String title; - - /** - * Creates a new entry. - * - * @param link The new entry's link. - * @param title The new entry's title. - * @param nick The nickname of the author of the link. - * @param login The login of the author of the link. - * @param channel The channel. - * @param tags The entry's tags/categories. - */ - public EntryLink(final String link, - final String title, - final String nick, - final String login, - final String channel, - final List<String> tags) { - this.link = link; - this.title = title; - this.nick = nick; - this.login = login; - this.channel = channel; - - setTags(tags); - } - - /** - * Creates a new entry. - * - * @param link The new entry's link. - * @param title The new entry's title. - * @param nick The nickname of the author of the link. - * @param channel The channel. - * @param date The entry date. - * @param tags The entry's tags/categories. - */ - public EntryLink(final String link, - final String title, - final String nick, - final String channel, - final Date date, - final List<SyndCategory> tags) { - this.link = link; - this.title = title; - this.nick = nick; - this.channel = channel; - this.date = new Date(date.getTime()); - this.tags.addAll(tags); - } - - /** - * Adds a new comment. - * - * @param comment The actual comment. - * @param nick The nickname of the author of the comment. - * @return The total number of comments for this entry. - */ - public final int addComment(final String comment, final String nick) { - comments.add(new EntryComment(comment, nick)); - - return (comments.size() - 1); - } - - /** - * Deletes a specific comment. - * - * @param index The index of the comment to delete. - */ - public final void deleteComment(final int index) { - if (index < comments.size()) { - comments.remove(index); - } - } - - /** - * Returns the channel the link was posted on. - * - * @return The channel - */ - public final String getChannel() { - return channel; - } - - /** - * Returns a comment. - * - * @param index The comment's index. - * @return The specific comment. - */ - public final EntryComment getComment(final int index) { - return (comments.get(index)); - } - - /** - * Returns all the comments. - * - * @return The comments. - */ - public final EntryComment[] getComments() { - return (comments.toArray(new EntryComment[0])); - } - - /** - * Returns the total number of comments. - * - * @return The count of comments. - */ - public final int getCommentsCount() { - return comments.size(); - } - - /** - * Returns the comment's creation date. - * - * @return The date. - */ - public final Date getDate() { - return new Date(date.getTime()); - } - - /** - * Returns the comment's link. - * - * @return The link. - */ - public final String getLink() { - return link; - } - - /** - * Returns the comment's author login. - * - * @return The login; - */ - public final String getLogin() { - return login; - } - - /** - * Returns the comment's author nickname. - * - * @return The nickname. - */ - public final String getNick() { - return nick; - } - - /** - * Returns the tags formatted for pinboard.in - * - * @return The tags as a comma-delimited string. - */ - public final String getPinboardTags() { - final StringBuilder pinboardTags = new StringBuilder(nick); - - for (final SyndCategory tag : tags) { - pinboardTags.append(','); - pinboardTags.append(tag.getName()); - } - - return pinboardTags.toString(); - } - - /** - * Returns the tags. - * - * @return The tags. - */ - public final List<SyndCategory> getTags() { - return tags; - } - - /** - * Returns the comment's title. - * - * @return The title. - */ - public final String getTitle() { - return title; - } - - /** - * Returns true if the entry has comments. - * - * @return true if there are comments, false otherwise. - */ - public final boolean hasComments() { - return !comments.isEmpty(); - } - - /** - * Returns true if the entry has tags. - * - * @return true if there are tags, false otherwise. - */ - public final boolean hasTags() { - return !tags.isEmpty(); - } - - /** - * Returns true if a string is contained in the link, title, or nick. - * - * @param match The string to match. - * @return {@code true} if matched, {@code false} otherwise. - */ - public Boolean matches(final String match) { - return (StringUtils.containsIgnoreCase(link, match) - || StringUtils.containsIgnoreCase(title, match) - || StringUtils.containsIgnoreCase(nick, match)); - } - - /** - * Sets the channel. - * - * @param channel The channel. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setChannel(final String channel) { - this.channel = channel; - } - - /** - * /** Sets a comment. - * - * @param index The comment's index. - * @param comment The actual comment. - * @param nick The nickname of the author of the comment. - */ - public final void setComment(final int index, final String comment, final String nick) { - if (index < comments.size()) { - comments.set(index, new EntryComment(comment, nick)); - } - } - - /** - * Sets the comment's link. - * - * @param link The new link. - */ - public final void setLink(final String link) { - this.link = link; - } - - /** - * Sets the comment's author login. - * - * @param login The new login. - */ - @SuppressWarnings("UnusedDeclaration") - public final void setLogin(final String login) { - this.login = login; - } - - /** - * Sets the comment's author nickname. - * - * @param nick The new nickname. - */ - public final void setNick(final String nick) { - this.nick = nick; - } - - /** - * Sets the tags. - * - * @param tags The space-delimited tags. - */ - public final void setTags(final String tags) { - setTags(Arrays.asList(tags.split(LinksMgr.TAG_MATCH))); - } - - /** - * Sets the tags. - * - * @param tags The tags list. - */ - @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public final void setTags(final List<String> tags) { - if (!tags.isEmpty()) { - SyndCategoryImpl category; - - for (final String tag : tags) { - if (StringUtils.isNoneBlank(tag)) { - final String t = StringUtils.lowerCase(tag); - final char mod = t.charAt(0); - if (mod == '-') { - // Don't remove the channel tag - if (!channel.substring(1).equals(t.substring(1))) { - category = new SyndCategoryImpl(); - category.setName(t.substring(1)); - this.tags.remove(category); - } - } else { - category = new SyndCategoryImpl(); - if (mod == '+') { - category.setName(t.substring(1)); - } else { - category.setName(t); - } - if (!this.tags.contains(category)) { - this.tags.add(category); - } - } - } - } - } - } - - /** - * Sets the comment's title. - * - * @param title The new title. - */ - public final void setTitle(final String title) { - this.title = title; - } - - /** - * Returns a string representation of the object. - * - * @return A string representation of the object. - */ - @Override - public String toString() { - return "EntryLink{" - + "channel='" + channel + '\'' - + ", comments=" + comments - + ", date=" + date - + ", link='" + link + '\'' - + ", login='" + login + '\'' - + ", nick='" + nick + '\'' - + ", tags=" + tags - + ", title='" + title + '\'' - + '}'; - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt new file mode 100644 index 0000000..9601d17 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -0,0 +1,223 @@ +/* + * EntryLink.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import com.rometools.rome.feed.synd.SyndCategory +import com.rometools.rome.feed.synd.SyndCategoryImpl +import net.thauvin.erik.mobibot.commands.links.LinksMgr +import org.apache.commons.lang3.StringUtils +import java.io.Serializable +import java.util.* +import java.util.concurrent.CopyOnWriteArrayList + +/** + * The class used to store link entries. + */ +class EntryLink : Serializable { + // Link's comments + val comments: MutableList<EntryComment> = CopyOnWriteArrayList() + + // Tags/categories + val tags: MutableList<SyndCategory> = CopyOnWriteArrayList() + + // Channel + var channel: String + + // Creation date + var date = Calendar.getInstance().time + + // Link's URL + var link: String + + // Author's login + var login = "" + + // Author's nickname + var nick: String + + // Link's title + var title: String + + /** + * Creates a new entry. + */ + constructor( + link: String, + title: String, + nick: String, + login: String, + channel: String, + tags: List<String?> + ) { + this.link = link + this.title = title + this.nick = nick + this.login = login + this.channel = channel + setTags(tags) + } + + /** + * Creates a new entry. + */ + constructor( + link: String, + title: String, + nick: String, + channel: String, + date: Date, + tags: List<SyndCategory> + ) { + this.link = link + this.title = title + this.nick = nick + this.channel = channel + this.date = Date(date.time) + this.tags.addAll(tags) + } + + /** + * Adds a new comment. + */ + fun addComment(comment: String?, nick: String?): Int { + comments.add(EntryComment(comment!!, nick!!)) + return comments.size - 1 + } + + /** + * Deletes a specific comment. + */ + fun deleteComment(index: Int) { + if (index < comments.size) { + comments.removeAt(index) + } + } + + /** + * Returns a comment. + */ + fun getComment(index: Int): EntryComment = comments[index] + + /** + * Returns the tags formatted for pinboard.in + */ + val pinboardTags: String + get() { + val pinboardTags = StringBuilder(nick) + for (tag in tags) { + pinboardTags.append(',') + pinboardTags.append(tag.name) + } + return pinboardTags.toString() + } + + /** + * Returns true if the entry has comments. + */ + fun hasComments(): Boolean = comments.isNotEmpty() + + /** + * Returns true if the entry has tags. + */ + fun hasTags(): Boolean = tags.isNotEmpty() + + /** + * Returns true if a string is contained in the link, title, or nick. + */ + fun matches(match: String?): Boolean { + return (StringUtils.containsIgnoreCase(link, match) + || StringUtils.containsIgnoreCase(title, match) + || StringUtils.containsIgnoreCase(nick, match)) + } + + /** + * Sets a comment. + */ + fun setComment(index: Int, comment: String?, nick: String?) { + if (index < comments.size) { + comments[index] = EntryComment(comment!!, nick!!) + } + } + + /** + * Sets the tags. + */ + fun setTags(tags: String) { + setTags(tags.split(LinksMgr.TAG_MATCH).toTypedArray().toList()) + } + + /** + * Sets the tags. + */ + fun setTags(tags: List<String?>) { + if (!tags.isEmpty()) { + var category: SyndCategoryImpl + for (tag in tags) { + if (StringUtils.isNoneBlank(tag)) { + val t = StringUtils.lowerCase(tag) + val mod = t[0] + if (mod == '-') { + // Don't remove the channel tag + if (channel.substring(1) != t.substring(1)) { + category = SyndCategoryImpl() + category.name = t.substring(1) + this.tags.remove(category) + } + } else { + category = SyndCategoryImpl() + if (mod == '+') { + category.name = t.substring(1) + } else { + category.name = t + } + if (!this.tags.contains(category)) { + this.tags.add(category) + } + } + } + } + } + } + + /** + * Returns a string representation of the object. + */ + override fun toString(): String { + return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," + + "nick='$nick', tags=$tags, title='$title'}") + } + + companion object { + // Serial version UID + const val serialVersionUID = 1L + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index 07de9f9..014846f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -37,6 +37,7 @@ import net.thauvin.erik.mobibot.TwitterTimer import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entriesCount import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getEntry +import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage import org.apache.commons.lang3.StringUtils @@ -76,7 +77,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { override val isValidProperties: Boolean get() { for (s in propertyKeys) { - if (AUTOPOST_PROP != s && HANDLE_PROP != s && properties[s]!!.isBlank()) { + if (AUTOPOST_PROP != s && HANDLE_PROP != s && properties[s].isNullOrBlank()) { return false } } @@ -130,7 +131,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { Thread { try { if (logger.isDebugEnabled) { - logger.debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1) + logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkCmd(index)) } post(message = msg, isDm = false) } catch (e: ModuleException) { @@ -146,7 +147,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { if (isAutoPost) { addEntry(index) if (bot.logger.isDebugEnabled) { - bot.logger.debug("Scheduling {}{} for posting on Twitter.", Constants.LINK_CMD, index + 1) + bot.logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkCmd(index)) } bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index d1dcf25..9fc9847 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -138,8 +138,8 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } if (cwd.hasWindData()) { with(cwd.windData) { - if (this != null && hasSpeed()) { - messages.add(NoticeMessage("Wind: ${wind(speed)}")) + if (this != null && hasSpeed() && speed != null) { + messages.add(NoticeMessage("Wind: ${wind(speed!!)}")) } } } @@ -148,7 +148,9 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { val list = cwd.weatherList if (list != null) { for (w in list) { - condition.append(' ').append(w!!.getDescription().capitalize()).append('.') + if (w != null) { + condition.append(' ').append(w.getDescription().capitalize()).append('.') + } } messages.add(NoticeMessage(condition.toString())) } @@ -182,9 +184,9 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { return messages } - private fun wind(w: Double?): String { - val kmh = w!! * 1.60934 - return "${Math.round(w)} mph, ${kmh.roundToInt()} km/h" + private fun wind(w: Double): String { + val kmh = w * 1.60934 + return "${w.roundToInt()} mph, ${kmh.roundToInt()} km/h" } } diff --git a/src/test/java/net/thauvin/erik/mobibot/LocalProperties.java b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt similarity index 57% rename from src/test/java/net/thauvin/erik/mobibot/LocalProperties.java rename to src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt index 1585681..04490ed 100644 --- a/src/test/java/net/thauvin/erik/mobibot/LocalProperties.java +++ b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,5 +1,5 @@ /* - * LocalProperties.java + * LocalProperties.kt * * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -29,54 +29,47 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.thauvin.erik.mobibot -package net.thauvin.erik.mobibot; - -import org.apache.commons.lang3.StringUtils; -import org.testng.annotations.BeforeSuite; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Properties; +import org.testng.annotations.BeforeSuite +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* /** - * The <code>properties</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-08 - * @since 1.0 + * Access to `local.properties`. */ -public class LocalProperties { - private static final Properties localProps = new Properties(); - - public static String getProperty(final String key) { - if (localProps.containsKey(key)) { - return localProps.getProperty(key); - } else { - final String env = System.getenv(keyToEnv(key)); - if (env != null) { - localProps.setProperty(key, env); - } - return env; - } - } - - private static String keyToEnv(final String key) { - return StringUtils.upperCase(key.replace('-', '_')); - } - +open class LocalProperties { @BeforeSuite(alwaysRun = true) - public void loadProperties() { - final Path localPath = Paths.get("local.properties"); + fun loadProperties() { + val localPath = Paths.get("local.properties") if (Files.exists(localPath)) { - try (final InputStream stream = Files.newInputStream(localPath)) { - localProps.load(stream); - } catch (IOException ignore) { + try { + Files.newInputStream(localPath).use { stream -> localProps.load(stream) } + } catch (ignore: IOException) { // Do nothing } } } + + companion object { + private val localProps = Properties() + + fun getProperty(key: String): String { + return if (localProps.containsKey(key)) { + localProps.getProperty(key) + } else { + val env = System.getenv(keyToEnv(key)) + if (env != null) { + localProps.setProperty(key, env) + } + env + } + } + + private fun keyToEnv(key: String): String { + return key.replace('-', '_').toUpperCase() + } + } } diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java deleted file mode 100644 index 68550bc..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * EntryLinkTest.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries; - -import com.rometools.rome.feed.synd.SyndCategory; -import org.testng.annotations.Test; - -import java.security.SecureRandom; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The <code>EntryUtilsTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-04-19 - * @since 1.0 - */ -public class EntryLinkTest { - private final EntryLink entryLink = new EntryLink("https://www.mobitopia.org/", "Mobitopia", "Skynx", - "JimH", "#mobitopia", List.of("tag1", "tag2", "tag3", "TAG4", "Tag5")); - - - @Test - public void testAddDeleteComment() { - int i = 0; - for (; i < 5; i++) { - entryLink.addComment("c" + i, "u" + i); - } - - i = 0; - for (final EntryComment comment : entryLink.getComments()) { - assertThat(comment.getComment()).as("getComment(" + i + ')').isEqualTo("c" + i); - assertThat(comment.getNick()).as("getNick(" + i + ')').isEqualTo("u" + i); - i++; - } - - final SecureRandom r = new SecureRandom(); - while (entryLink.getCommentsCount() > 0) { - entryLink.deleteComment(r.nextInt(entryLink.getCommentsCount())); - } - assertThat(entryLink.hasComments()).as("hasComments()").isFalse(); - - entryLink.addComment("nothing", "nobody"); - entryLink.setComment(0, "something", "somebody"); - assertThat(entryLink.getComment(0).getNick()).as("getNick(somebody)").isEqualTo("somebody"); - assertThat(entryLink.getComment(0).getComment()).as("getComment(something)").isEqualTo("something"); - - } - - @Test - public void testTags() { - final List<SyndCategory> tags = entryLink.getTags(); - - int i = 0; - for (final SyndCategory tag : tags) { - assertThat(tag.getName()).as("tag.getName(" + i + ')').isEqualTo("tag" + (i + 1)); - i++; - } - - entryLink.setTags("-tag5"); - entryLink.setTags("+mobitopia"); - entryLink.setTags("tag4"); - entryLink.setTags("-mobitopia"); - - assertThat(entryLink.getPinboardTags()).as("getPinboardTags()") - .isEqualTo(entryLink.getNick() + ",tag1,tag2,tag3,tag4,mobitopia"); - } -} diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt new file mode 100644 index 0000000..d92b58f --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -0,0 +1,92 @@ +/* + * EntryLinkTest.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import com.rometools.rome.feed.synd.SyndCategory +import org.assertj.core.api.Assertions +import org.testng.annotations.Test +import java.security.SecureRandom + +/** + * The `EntryUtilsTest` class. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @created 2019-04-19 + * @since 1.0 + */ +class EntryLinkTest { + private val entryLink = EntryLink( + "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) + + @Test + fun testAddDeleteComment() { + var i = 0 + while (i < 5) { + entryLink.addComment("c$i", "u$i") + i++ + } + Assertions.assertThat(entryLink.comments.size).`as`("getComments().size() == 5").isEqualTo(i) + i = 0 + for (comment in entryLink.comments) { + Assertions.assertThat(comment.comment).`as`("getComment($i)").isEqualTo("c$i") + Assertions.assertThat(comment.nick).`as`("getNick($i)").isEqualTo("u$i") + i++ + } + val r = SecureRandom() + while (entryLink.comments.size > 0) { + entryLink.deleteComment(r.nextInt(entryLink.comments.size)) + } + Assertions.assertThat(entryLink.hasComments()).`as`("hasComments()").isFalse + entryLink.addComment("nothing", "nobody") + entryLink.setComment(0, "something", "somebody") + Assertions.assertThat(entryLink.getComment(0).nick).`as`("getNick(somebody)").isEqualTo("somebody") + Assertions.assertThat(entryLink.getComment(0).comment).`as`("getComment(something)").isEqualTo("something") + } + + @Test + fun testTags() { + val tags: List<SyndCategory> = entryLink.tags + for ((i, tag) in tags.withIndex()) { + Assertions.assertThat(tag.name).`as`("tag.getName($i)").isEqualTo("tag" + (i + 1)) + } + Assertions.assertThat(entryLink.tags.size).`as`("getTags().size() is 5").isEqualTo(5) + Assertions.assertThat(entryLink.hasTags()).`as`("hasTags() is true").isTrue + entryLink.setTags("-tag5") + entryLink.setTags("+mobitopia") + entryLink.setTags("tag4") + entryLink.setTags("-mobitopia") + Assertions.assertThat(entryLink.pinboardTags).`as`("getPinboardTags()") + .isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia") + } +} From fb05647b7b7221cee65dfd29a468f0261a365d29 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 2 Dec 2020 22:58:42 -0800 Subject: [PATCH 440/842] Added debug arg on run. --- build.gradle | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 55950b9..6b905f9 100644 --- a/build.gradle +++ b/build.gradle @@ -19,8 +19,6 @@ final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' final def semverProcessor = "net.thauvin.erik:semver:1.2.0" -mainClassName = packageName + '.Mobibot' - ext.versions = [ jacoco : '0.8.6', log4j : '2.14.0', @@ -79,6 +77,10 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +application { + mainClassName = packageName + '.Mobibot' +} + tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") @@ -142,7 +144,7 @@ clean { } run { - //args '--v' + args '-d' } incrementBuildMeta { From ca2939629faa453a4534f89a878c1ca53b8d5024 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 2 Dec 2020 22:59:08 -0800 Subject: [PATCH 441/842] Version 0.8.0-beta+287 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index b548cec..cf5342d 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,14 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+234"; + public static final String VERSION = "0.8.0-beta+287"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1606946086512L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1606978487815L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String BUILDMETA = "234"; + public static final String BUILDMETA = "287"; public static final String PRERELEASE = "beta"; public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; diff --git a/version.properties b/version.properties index 418f0b7..f94bcbe 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Dec 02 13:54:44 PST 2020 -version.buildmeta=234 +#Wed Dec 02 22:54:45 PST 2020 +version.buildmeta=287 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+234 +version.semver=0.8.0-beta+287 From 2d24f5fc97a385d5851caf46f87e33bee4ac1ffe Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 01:40:20 -0800 Subject: [PATCH 442/842] Switched to buffered output stream for stdout/stderr. --- .../java/net/thauvin/erik/mobibot/Mobibot.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java index 63ec7cf..420833d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java @@ -87,6 +87,7 @@ import org.apache.logging.log4j.core.config.Configurator; import org.jibble.pircbot.PircBot; import org.jibble.pircbot.User; +import java.io.BufferedOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -299,7 +300,6 @@ public class Mobibot extends PircBot { System.out.println(ReleaseInfo.WEBSITE); } else { final Properties p = new Properties(); - try (final InputStream fis = Files.newInputStream( Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG.charAt(0), "./mobibot.properties")))) { // Load the properties files @@ -321,8 +321,8 @@ public class Mobibot extends PircBot { // Redirect the stdout and stderr if (!commandLine.hasOption(Constants.DEBUG_ARG.charAt(0))) { try { - final PrintStream stdout = new PrintStream( - new FileOutputStream(logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)); + final PrintStream stdout = new PrintStream(new BufferedOutputStream(new FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)), true); System.setOut(stdout); } catch (IOException e) { System.err.println("Unable to open output (stdout) log file."); @@ -331,7 +331,9 @@ public class Mobibot extends PircBot { } try { - final PrintStream stderr = new PrintStream(new FileOutputStream(logsDir + nickname + ".err", true)); + final PrintStream stderr = + new PrintStream(new BufferedOutputStream( + new FileOutputStream(logsDir + nickname + ".err", true)), true); System.setErr(stderr); } catch (IOException e) { System.err.println("Unable to open error (stderr) log file."); @@ -724,7 +726,9 @@ public class Mobibot extends PircBot { @Override protected final void onPrivateMessage(final String sender, final String login, final String hostname, final String message) { - LOGGER.debug(">>> {} : {}", sender, message); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(">>> {} : {}", sender, message); + } final String[] cmds = message.split(" ", 2); final String cmd = lowerCase(cmds[0]); From 0e8db3440f782297ae2e6d0be0f40c0959939b26 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 01:44:16 -0800 Subject: [PATCH 443/842] Optimized Links Manager. --- config/detekt/baseline.xml | 1 + .../thauvin/erik/mobibot/commands/AddLog.kt | 23 ++++++----- .../thauvin/erik/mobibot/commands/Info.java | 2 +- .../erik/mobibot/commands/links/Comment.kt | 4 +- .../erik/mobibot/commands/links/LinksMgr.kt | 41 ++++++++----------- .../erik/mobibot/commands/links/Posting.kt | 17 ++++---- .../erik/mobibot/commands/links/Tags.kt | 4 +- .../erik/mobibot/commands/links/View.kt | 9 ++-- .../thauvin/erik/mobibot/modules/Twitter.kt | 7 ++-- 9 files changed, 52 insertions(+), 56 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 07b0938..32e2375 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -11,6 +11,7 @@ <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> + <ID>MagicNumber:AddLog.kt$AddLog$4</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt index f7639a5..2848369 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -33,8 +33,8 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.addHistory -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getHistory + +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.history import net.thauvin.erik.mobibot.entries.EntriesMgr import java.io.File @@ -52,15 +52,18 @@ class AddLog(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - if (isOp && args.isNotBlank()) { - // e.g: 2014-04-01 - val backlog = File("${bot.logsDir}$args${EntriesMgr.XML_EXT}") - if (backlog.exists()) { - addHistory(0, args) - bot.send(sender, getHistory().toString(), isPrivate) - } else { - bot.send(sender, "The specified log could not be found.", isPrivate) + if (isOp) { + if (args.isNotBlank()) { + // e.g: 2014-04-01 + val backlog = File("${bot.logsDir}$args${EntriesMgr.XML_EXT}") + if (backlog.exists()) { + history.add(0, args) + } else { + bot.send(sender, "The specified log could not be found.", isPrivate) + return + } } + bot.sendList(sender, history, 4, isPrivate, false) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index 1282d78..e9c9975 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -93,7 +93,7 @@ public class Info extends AbstractCommand { info.append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())) .append(" [Entries: ") - .append(LinksMgr.getEntriesCount()); + .append(LinksMgr.entries.size()); if (isOp) { if (getBot().getTell().isEnabled()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 00b718a..65941cf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -68,8 +68,8 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { val cmds = args.substring(1).split("[.:]".toRegex(), 3) val index = cmds[0].toInt() - 1 - if (index < LinksMgr.entriesCount) { - val entry: EntryLink = LinksMgr.getEntry(index) + if (index < LinksMgr.entries.size) { + val entry: EntryLink = LinksMgr.entries[index] val commentIndex = cmds[1].toInt() - 1 if (commentIndex < entry.comments.size) { when (val cmd = cmds[2].trim()) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index ac5257d..cd4104b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -40,7 +40,6 @@ import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.entries.EntriesMgr import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink -import org.apache.logging.log4j.LogManager import org.jsoup.Jsoup import java.io.IOException @@ -65,23 +64,22 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { const val TAG_MATCH = ", *| +" // Entries array - private val entries = ArrayList<EntryLink>(0) + @JvmField + val entries = ArrayList<EntryLink>(0) // History/backlogs array - private val history = ArrayList<String>(0) + @JvmField + val history = ArrayList<String>(0) - @JvmStatic - val entriesCount - get() = entries.size @JvmStatic var startDate: String = Utils.today() private set - @JvmStatic - fun addHistory(index: Int, entry: String) { - history.add(index, entry) - } + // @JvmStatic + // fun addHistory(index: Int, entry: String) { + // history.add(index, entry) + // } /** * Saves the entries. @@ -93,20 +91,15 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { EntriesMgr.saveEntries(bot, entries, history, isDayBackup) } - @JvmStatic - fun removeEntry(index: Int) { - entries.removeAt(index) - } - - @JvmStatic - fun getEntry(index: Int): EntryLink { - return entries[index] - } - - @JvmStatic - fun getHistory(): List<String> { - return history - } + // @JvmStatic + // fun removeEntry(index: Int) { + // entries.removeAt(index) + // } + // + // @JvmStatic + // fun getEntry(index: Int): EntryLink { + // return entries[index] + // } @JvmStatic fun startup(current: String, backlogs: String, channel: String) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index bf275f5..71ec786 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -36,6 +36,7 @@ import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink @@ -67,7 +68,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { val cmds = args.substring(1).split(":", limit = 2) val index = cmds[0].toInt() - 1 - if (index < LinksMgr.entriesCount) { + if (index < entries.size) { when (val cmd = cmds[1].trim()) { "" -> showEntry(index) "-" -> removeEntry(sender, login, isOp, index) // L1:- @@ -88,7 +89,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { } private fun addComment(cmd: String, sender: String, index: Int) { - val entry: EntryLink = LinksMgr.getEntry(index) + val entry: EntryLink = entries[index] val commentIndex = entry.addComment(cmd, sender) val comment = entry.getComment(commentIndex) bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false) @@ -97,7 +98,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { private fun changeTitle(cmd: String, index: Int) { if (cmd.length > 1) { - val entry: EntryLink = LinksMgr.getEntry(index) + val entry: EntryLink = entries[index] entry.title = cmd.substring(1).trim() bot.updatePin(entry.link, entry) bot.send(EntriesUtils.buildLink(index, entry)) @@ -106,7 +107,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { } private fun changeUrl(cmd: String, login: String, isOp: Boolean, index: Int) { - val entry: EntryLink = LinksMgr.getEntry(index) + val entry: EntryLink = entries[index] if (entry.login == login || isOp) { val link = cmd.substring(1) if (link.matches(LinksMgr.LINK_MATCH.toRegex())) { @@ -122,7 +123,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { private fun changeAuthor(cmd: String, sender: String, isOp: Boolean, index: Int) { if (isOp) { if (cmd.length > 1) { - val entry: EntryLink = LinksMgr.getEntry(index) + val entry: EntryLink = entries[index] entry.nick = cmd.substring(1) bot.send(EntriesUtils.buildLink(index, entry)) LinksMgr.saveEntries(bot, false) @@ -133,10 +134,10 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { } private fun removeEntry(sender: String, login: String, isOp: Boolean, index: Int) { - val entry: EntryLink = LinksMgr.getEntry(index) + val entry: EntryLink = LinksMgr.entries[index] if (entry.login == login || isOp) { bot.deletePin(index, entry) - LinksMgr.removeEntry(index) + entries.removeAt(index) bot.send("Entry ${EntriesUtils.buildLinkCmd(index)} removed.") LinksMgr.saveEntries(bot, false) } else { @@ -145,7 +146,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { } private fun showEntry(index: Int) { - val entry: EntryLink = LinksMgr.getEntry(index) + val entry: EntryLink = entries[index] bot.send(EntriesUtils.buildLink(index, entry)) if (entry.hasTags()) { bot.send(EntriesUtils.buildTags(index, entry)) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index 28b57c9..9a9a73e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -63,9 +63,9 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { val cmds = args.substring(1).split("T:", limit = 2) val index = cmds[0].toInt() - 1 - if (index < LinksMgr.entriesCount) { + if (index < LinksMgr.entries.size) { val cmd = cmds[1].trim() - val entry: EntryLink = LinksMgr.getEntry(index) + val entry: EntryLink = LinksMgr.entries[index] if (cmd.isNotEmpty()) { if (entry.login == login || isOp) { entry.setTags(cmd) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index 98dd423..dbafa4a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -35,8 +35,7 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entriesCount -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getEntry +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink @@ -62,7 +61,7 @@ class View(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - if (entriesCount != 0) { + if (entries.size != 0) { showPosts(bot, args, sender) } else { bot.send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate) @@ -70,7 +69,7 @@ class View(bot: Mobibot) : AbstractCommand(bot) { } private fun showPosts(bot: Mobibot, args: String, sender: String) { - val max = entriesCount + val max = entries.size var lcArgs = args.toLowerCase() var i = 0 if (lcArgs.isEmpty() && max > maxEntries) { @@ -96,7 +95,7 @@ class View(bot: Mobibot) : AbstractCommand(bot) { var entry: EntryLink var sent = 0 while (i < max && sent < maxEntries) { - entry = getEntry(i) + entry = entries[i] if (lcArgs.isNotBlank()) { if (entry.matches(lcArgs)) { bot.send(sender, EntriesUtils.buildLink(i, entry, true), false) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index 014846f..392cf85 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -35,8 +35,7 @@ import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.TwitterTimer import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entriesCount -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getEntry +import net.thauvin.erik.mobibot.commands.links.LinksMgr import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage @@ -125,8 +124,8 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { */ fun postEntry(index: Int) { with(bot) { - if (isAutoPost && hasEntry(index) && entriesCount >= index) { - val entry = getEntry(index) + if (isAutoPost && hasEntry(index) && LinksMgr.entries.size >= index) { + val entry = LinksMgr.entries[index] val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel" Thread { try { From 8178744f8555362e97759f74ee9f8f51efdf76bc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 03:05:52 -0800 Subject: [PATCH 444/842] Fixed typo. --- .../java/net/thauvin/erik/mobibot/commands/links/Posting.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 71ec786..fcdc20e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -44,7 +44,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { override val name = "posting" override val help = listOf( "Post a URL, by saying it on a line on its own:", - Utils.helpIndent("<url> [<title>] ${Tags.COMMAND}}: <+tag> [...]]"), + Utils.helpIndent("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1", "To add a title, use its label and a pipe:", Utils.helpIndent("${Constants.LINK_CMD}1:|This is the title"), From 274c711dc27026392163e3892dba06fe994a26e9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 03:06:19 -0800 Subject: [PATCH 445/842] Version 0.8.0-beta+302 --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 2 +- .idea/modules/mobibot.test.iml | 2 +- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 7d99ee9..51a4995 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+147" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index cc8a457..c6fe00f 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+147" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index b0c7198..f864b22 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+147" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index cf5342d..654713c 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,14 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+287"; + public static final String VERSION = "0.8.0-beta+302"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1606978487815L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1607079230723L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String BUILDMETA = "287"; + public static final String BUILDMETA = "302"; public static final String PRERELEASE = "beta"; public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; diff --git a/version.properties b/version.properties index f94bcbe..375622b 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Dec 02 22:54:45 PST 2020 -version.buildmeta=287 +#Fri Dec 04 02:53:48 PST 2020 +version.buildmeta=302 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+287 +version.semver=0.8.0-beta+302 From 92e703b68a763ad50a1a113a0f91b6147255cd98 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 15:49:25 -0800 Subject: [PATCH 446/842] Convert Tell Message Manager to Kotlin. --- config/detekt/baseline.xml | 6 + .../java/net/thauvin/erik/mobibot/Utils.kt | 8 +- .../erik/mobibot/commands/tell/Tell.java | 393 ------------------ .../erik/mobibot/commands/tell/Tell.kt | 362 ++++++++++++++++ .../mobibot/commands/tell/TellMessage.java | 195 --------- .../erik/mobibot/commands/tell/TellMessage.kt | 112 +++++ .../commands/tell/TellMessagesMgr.java | 132 ------ .../mobibot/commands/tell/TellMessagesMgr.kt | 104 +++++ .../commands/tell/TellMessageTest.java | 81 ---- .../mobibot/commands/tell/TellMessageTest.kt | 68 +++ 10 files changed, 658 insertions(+), 803 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt delete mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt delete mode 100644 src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.java create mode 100644 src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 32e2375..59b5db4 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -24,6 +24,8 @@ <ID>MagicNumber:Ignore.kt$Ignore$8</ID> <ID>MagicNumber:Modules.kt$Modules$7</ID> <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> + <ID>MagicNumber:Tell.kt$Tell$50</ID> + <ID>MagicNumber:Tell.kt$Tell$7</ID> <ID>MagicNumber:Twitter.kt$Twitter$1000L</ID> <ID>MagicNumber:Twitter.kt$Twitter$60L</ID> <ID>MagicNumber:Users.kt$Users$8</ID> @@ -55,6 +57,9 @@ <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> + <ID>NestedBlockDepth:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID> + <ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID> + <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr.Companion$ fun save(file: String, messages: List<TellMessage?>?, logger: Logger)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> @@ -66,6 +71,7 @@ <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> + <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> <ID>TooManyFunctions:Utils.kt$Utils$Companion</ID> </CurrentIssues> </SmellBaseline> diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index e14f1eb..09f7062 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -298,8 +298,12 @@ class Utils private constructor() { * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. */ @JvmStatic - fun utcDateTime(date: LocalDateTime): String { - return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + fun utcDateTime(date: LocalDateTime?): String { + return if (date != null) { + date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + } else { + "" + } } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java deleted file mode 100644 index d3fa026..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Tell.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.tell; - -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.commands.AbstractCommand; -import net.thauvin.erik.mobibot.commands.links.View; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * The <code>Tell</code> command. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2016-07-02 - * @since 1.0 - */ -public class Tell extends AbstractCommand { - /** - * Max days property. - */ - public static final String MAX_DAYS_PROP = "tell-max-days"; - /** - * Max size proeprty. - */ - public static final String MAX_SIZE_PROP = "tell-max-size"; - /** - * The tell command. - */ - public static final String TELL_CMD = "tell"; - // Arrow - private static final String ARROW = " --> "; - // Serialized object file extension - private static final String SER_EXT = ".ser"; - // All keyword - private static final String TELL_ALL_KEYWORD = "all"; - //T he delete command. - private static final String TELL_DEL_KEYWORD = "del"; - // Messages queue - private final List<TellMessage> messages = new CopyOnWriteArrayList<>(); - // Serialized object file - private final String serializedObject; - // Maximum number of days to keep messages - private int maxDays = 7; - // Message maximum queue size - private int maxSize = 50; - - /** - * Creates a new instance. - * - * @param bot The bot. - */ - public Tell(final Mobibot bot) { - super(bot); - initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP); - - // Load the message queue - serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT; - messages.addAll(TellMessagesMgr.load(serializedObject, bot.getLogger())); - - if (clean()) { - save(); - } - } - - /** - * Cleans the messages queue. - * - * @return <code>true</code> if the queue was cleaned. - */ - @SuppressWarnings("WeakerAccess") - final boolean clean() { - if (getBot().getLogger().isDebugEnabled()) { - getBot().getLogger().debug("Cleaning the messages."); - } - return TellMessagesMgr.clean(messages, maxDays); - } - - // Delete message. - private void deleteMessage(final String sender, final String args, final boolean isOp, final boolean isPrivate) { - final String[] split = args.split(" "); - - if (split.length == 2) { - final String id = split[1]; - boolean deleted = false; - - if (TELL_ALL_KEYWORD.equalsIgnoreCase(id)) { - for (final TellMessage message : messages) { - if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) { - messages.remove(message); - deleted = true; - } - } - - if (deleted) { - save(); - getBot().send(sender, "Delivered messages have been deleted.", isPrivate); - } else { - getBot().send(sender, "No delivered messages were found.", isPrivate); - } - - } else { - boolean found = false; - - for (final TellMessage message : messages) { - found = message.isMatchId(id); - - if (found && (message.getSender().equalsIgnoreCase(sender) || getBot().isOp(sender))) { - messages.remove(message); - - save(); - getBot().send(sender, "Your message was deleted from the queue.", isPrivate); - deleted = true; - break; - } - } - - if (!deleted) { - if (found) { - getBot().send(sender, "Only messages that you sent can be deleted.", isPrivate); - } else { - getBot().send(sender, "The specified message [ID " + id + "] could not be found.", isPrivate); - } - } - } - } else { - helpResponse(args, sender, isOp, isPrivate); - } - } - - @NotNull - @Override - public String getName() { - return TELL_CMD; - } - - @NotNull - @Override - public List<String> getHelp() { - return List.of("To send a message to someone when they join the channel:", - Utils.helpIndent("%c " + TELL_CMD + " <nick> <message>"), - "To view queued and sent messages:", - Utils.helpIndent("%c " + TELL_CMD + ' ' + View.VIEW_CMD), - "Messages are kept for " + Utils.bold(maxDays) - + Utils.plural(maxDays, " day.", " days.")); - } - - @Override - public boolean isOp() { - return false; - } - - @Override - public boolean isPublic() { - return isEnabled(); - } - - @Override - public boolean isVisible() { - return isEnabled(); - } - - @Override - public void commandResponse(@NotNull final String sender, - @NotNull final String login, - @NotNull final String args, - final boolean isOp, - final boolean isPrivate) { - if (isEnabled()) { - if (StringUtils.isBlank(args)) { - helpResponse(args, sender, isOp, isPrivate); - } else if (args.startsWith(View.VIEW_CMD)) { - if (getBot().isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(args)) { - viewAll(sender, isPrivate); - } else { - viewMessages(sender, isPrivate); - } - } else if (args.startsWith(TELL_DEL_KEYWORD + ' ')) { - deleteMessage(sender, args, isOp, isPrivate); - } else { - newMessage(sender, args, isOp, isPrivate); - } - - if (clean()) { - save(); - } - } - } - - @Override - public boolean isEnabled() { - return maxSize > 0 && maxDays > 0; - } - - @Override - public void setProperty(@NotNull final String key, @NotNull final String value) { - super.setProperty(key, value); - if (MAX_DAYS_PROP.equals(key)) { - this.maxDays = Utils.getIntProperty(value, maxDays); - } else if (MAX_SIZE_PROP.equals(key)) { - this.maxSize = Utils.getIntProperty(value, maxSize); - } - } - - // New message. - private void newMessage(final String sender, final String args, final boolean isOp, final boolean isPrivate) { - final String[] split = args.split(" ", 2); - - if (split.length == 2 && (StringUtils.isNotBlank(split[1]) && split[1].contains(" "))) { - if (messages.size() < maxSize) { - final TellMessage message = new TellMessage(sender, split[0], split[1].trim()); - - messages.add(message); - - save(); - - getBot().send(sender, "Message [ID " + message.getId() + "] was queued for " - + Utils.bold(message.getRecipient()), isPrivate); - } else { - getBot().send(sender, "Sorry, the messages queue is currently full.", isPrivate); - } - } else { - helpResponse(args, sender, isOp, isPrivate); - } - } - - /** - * Saves the messages queue. - */ - @SuppressWarnings("WeakerAccess") - final void save() { - TellMessagesMgr.save(serializedObject, messages, getBot().getLogger()); - } - - /** - * Checks and sends messages. - * - * @param nickname The user's nickname. - * @param isMessage The message flag. - */ - public void send(final String nickname, final boolean isMessage) { - if (isEnabled() && !nickname.equals(getBot().getNick())) { - messages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> { - if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) { - if (message.getSender().equals(nickname)) { - if (!isMessage) { - getBot().send(nickname, Utils.bold("You") + " wanted me to remind you: " - + Utils.reverseColor(message.getMessage()), - true); - - message.setIsReceived(); - message.setIsNotified(); - - save(); - } - } else { - getBot().send(nickname, message.getSender() + " wanted me to tell you: " - + Utils.reverseColor(message.getMessage()), - true); - - message.setIsReceived(); - - save(); - } - } else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() - && !message.isNotified()) { - getBot().send(nickname, - "Your message " - + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " - + Utils.bold(message.getRecipient()) + " on " - + Utils.utcDateTime(message.getReceived()), - true); - - message.setIsNotified(); - - save(); - } - }); - } - } - - /** - * Checks and sends messages. - * - * @param nickname The user's nickname. - */ - public void send(final String nickname) { - send(nickname, false); - } - - /** - * Returns the messages queue size. - * - * @return The size. - */ - public int size() { - return messages.size(); - } - - // View all messages. - private void viewAll(final String sender, final boolean isPrivate) { - if (!messages.isEmpty()) { - for (final TellMessage message : messages) { - getBot().send(sender, Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) - + " [ID: " + message.getId() + ", " - + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']', - isPrivate); - } - } else { - getBot().send(sender, "There are no messages in the queue.", isPrivate); - } - } - - // View messages. - private void viewMessages(final String sender, final boolean isPrivate) { - boolean hasMessage = false; - - for (final TellMessage message : messages) { - if (message.isMatch(sender)) { - if (!hasMessage) { - hasMessage = true; - getBot().send(sender, "Here are your messages: ", isPrivate); - } - - if (message.isReceived()) { - getBot().send(sender, - Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getReceived()) + ", ID: " - + Utils.bold(message.getId()) + ", DELIVERED]", - isPrivate); - - } else { - getBot().send(sender, - Utils.bold(message.getSender()) + ARROW + Utils.bold(message.getRecipient()) - + " [" + Utils.utcDateTime(message.getQueued()) + ", ID: " - + Utils.bold(message.getId()) + ", QUEUED]", - isPrivate); - } - - getBot().send(sender, Utils.helpIndent(message.getMessage()), isPrivate); - } - } - - if (!hasMessage) { - getBot().send(sender, "You have no messages in the queue.", isPrivate); - } else { - getBot().send(sender, "To delete one or all delivered messages:", isPrivate); - getBot().send(sender, - Utils.helpIndent(Utils.helpFormat( - "%c " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|" + TELL_ALL_KEYWORD + '>', - getBot().getNick(), - isPrivate)), - isPrivate); - getBot().send(sender, - "Messages are kept for " + Utils.bold(maxDays) - + Utils.plural(maxDays, " day.", " days."), isPrivate); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt new file mode 100644 index 0000000..0b485eb --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -0,0 +1,362 @@ +/* + * Tell.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.Companion.bold +import net.thauvin.erik.mobibot.Utils.Companion.getIntProperty +import net.thauvin.erik.mobibot.Utils.Companion.helpFormat +import net.thauvin.erik.mobibot.Utils.Companion.helpIndent +import net.thauvin.erik.mobibot.Utils.Companion.plural +import net.thauvin.erik.mobibot.Utils.Companion.reverseColor +import net.thauvin.erik.mobibot.Utils.Companion.utcDateTime +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.View +import org.apache.commons.lang3.StringUtils +import java.util.concurrent.CopyOnWriteArrayList + +/** + * The `Tell` command. + */ +class Tell(bot: Mobibot) : AbstractCommand(bot) { + // Messages queue + private val messages: MutableList<TellMessage> = CopyOnWriteArrayList() + + // Serialized object file + private val serializedObject: String + + // Maximum number of days to keep messages + private var maxDays = 7 + + // Message maximum queue size + private var maxSize = 50 + + /** + * Cleans the messages queue. + */ + private fun clean(): Boolean { + if (bot.logger.isDebugEnabled) { + bot.logger.debug("Cleaning the messages.") + } + return TellMessagesMgr.clean(messages, maxDays) + } + + // Delete message. + private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) { + val split = args.split(" ").toTypedArray() + if (split.size == 2) { + val id = split[1] + var deleted = false + if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { + for (message in messages) { + if (message.sender.equals(sender, ignoreCase = true) && message.isReceived) { + messages.remove(message) + deleted = true + } + } + if (deleted) { + save() + bot.send(sender, "Delivered messages have been deleted.", isPrivate) + } else { + bot.send(sender, "No delivered messages were found.", isPrivate) + } + } else { + var found = false + for (message in messages) { + found = message.isMatchId(id) + if (found && (message.sender.equals(sender, ignoreCase = true) || bot.isOp(sender))) { + messages.remove(message) + save() + bot.send(sender, "Your message was deleted from the queue.", isPrivate) + deleted = true + break + } + } + if (!deleted) { + if (found) { + bot.send(sender, "Only messages that you sent can be deleted.", isPrivate) + } else { + bot.send(sender, "The specified message [ID $id] could not be found.", isPrivate) + } + } + } + } else { + helpResponse(args, sender, isOp, isPrivate) + } + } + + /** + * The tell command. + */ + override val name = "tell" + + override val help: List<String> + get() = listOf( + "To send a message to someone when they join the channel:", + helpIndent("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpIndent("%c $name ${View.VIEW_CMD}"), + "Messages are kept for " + bold(maxDays) + + plural(maxDays.toLong(), " day.", " days.") + ) + override val isOp: Boolean + get() = false + override val isPublic: Boolean + get() = isEnabled() + override val isVisible: Boolean + get() = isEnabled() + + override fun commandResponse( + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isEnabled()) { + if (StringUtils.isBlank(args)) { + helpResponse(args, sender, isOp, isPrivate) + } else if (args.startsWith(View.VIEW_CMD)) { + if (bot.isOp(sender) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { + viewAll(sender, isPrivate) + } else { + viewMessages(sender, isPrivate) + } + } else if (args.startsWith("$TELL_DEL_KEYWORD ")) { + deleteMessage(sender, args, isOp, isPrivate) + } else { + newMessage(sender, args, isOp, isPrivate) + } + if (clean()) { + save() + } + } + } + + override fun isEnabled(): Boolean { + return maxSize > 0 && maxDays > 0 + } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (MAX_DAYS_PROP == key) { + maxDays = getIntProperty(value, maxDays) + } else if (MAX_SIZE_PROP == key) { + maxSize = getIntProperty(value, maxSize) + } + } + + // New message. + private fun newMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) { + val split = args.split(" ".toRegex(), 2).toTypedArray() + if (split.size == 2 && StringUtils.isNotBlank(split[1]) && split[1].contains(" ")) { + if (messages.size < maxSize) { + val message = TellMessage(sender, split[0], split[1].trim()) + messages.add(message) + save() + bot.send(sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate + ) + } else { + bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate) + } + } else { + helpResponse(args, sender, isOp, isPrivate) + } + } + + /** + * Saves the messages queue. + */ + private fun save() { + TellMessagesMgr.save(serializedObject, messages, bot.logger) + } + + /** + * Checks and sends messages. + */ + @JvmOverloads + fun send(nickname: String, isMessage: Boolean = false) { + if (isEnabled() && nickname != bot.nick) { + messages.stream().filter { message: TellMessage -> message.isMatch(nickname) } + .forEach { message: TellMessage -> + if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { + if (message.sender == nickname) { + if (!isMessage) { + bot.send( + nickname, + "${bold("You")} wanted me to remind you: ${reverseColor(message.message)}", + true + ) + message.isReceived = true + message.isNotified = true + save() + } + } else { + bot.send( + nickname, + "${message.sender} wanted me to tell you: ${reverseColor(message.message)}", + true + ) + message.isReceived = true + save() + } + } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived + && !message.isNotified + ) { + bot.send( + nickname, + "Your message ${reverseColor("[ID " + message.id + ']')} was sent to " + + "${bold(message.recipient)} on ${utcDateTime(message.receptionDate)}", + true + ) + message.isNotified = true + save() + } + } + } + } + + /** + * Returns the messages queue size. + * + * @return The size. + */ + fun size(): Int { + return messages.size + } + + // View all messages. + private fun viewAll(sender: String, isPrivate: Boolean) { + if (messages.isNotEmpty()) { + for (message in messages) { + bot.send( + sender, bold(message.sender) + ARROW + bold(message.recipient) + + " [ID: " + message.id + ", " + + (if (message.isReceived) "DELIVERED" else "QUEUED") + ']', + isPrivate + ) + } + } else { + bot.send(sender, "There are no messages in the queue.", isPrivate) + } + } + + // View messages. + private fun viewMessages(sender: String, isPrivate: Boolean) { + var hasMessage = false + for (message in messages) { + if (message.isMatch(sender)) { + if (!hasMessage) { + hasMessage = true + bot.send(sender, "Here are your messages: ", isPrivate) + } + if (message.isReceived) { + bot.send( + sender, + bold(message.sender) + ARROW + bold(message.recipient) + + " [" + utcDateTime(message.receptionDate) + ", ID: " + + bold(message.id) + ", DELIVERED]", + isPrivate + ) + } else { + bot.send( + sender, + bold(message.sender) + ARROW + bold(message.recipient) + + " [" + utcDateTime(message.queued) + ", ID: " + + bold(message.id) + ", QUEUED]", + isPrivate + ) + } + bot.send(sender, helpIndent(message.message), isPrivate) + } + } + if (!hasMessage) { + bot.send(sender, "You have no messages in the queue.", isPrivate) + } else { + bot.send(sender, "To delete one or all delivered messages:", isPrivate) + bot.send( + sender, + helpIndent( + helpFormat( + "%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", + bot.nick, + isPrivate + ) + ), + isPrivate + ) + bot.send( + sender, + "Messages are kept for ${bold(maxDays)}${plural(maxDays.toLong(), " day.", " days.")}", + isPrivate + ) + } + } + + companion object { + /** + * Max days property. + */ + const val MAX_DAYS_PROP = "tell-max-days" + + /** + * Max size proeprty. + */ + const val MAX_SIZE_PROP = "tell-max-size" + + // Arrow + private const val ARROW = " --> " + + // Serialized object file extension + private const val SER_EXT = ".ser" + + // All keyword + private const val TELL_ALL_KEYWORD = "all" + + //T he delete command. + private const val TELL_DEL_KEYWORD = "del" + } + + /** + * Creates a new instance. + */ + init { + initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP) + + // Load the message queue + serializedObject = bot.logsDir + bot.name + SER_EXT + messages.addAll(TellMessagesMgr.load(serializedObject, bot.logger)) + if (clean()) { + save() + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java deleted file mode 100644 index 40d71f7..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * TellMessage.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.tell; - -import java.io.Serializable; -import java.time.Clock; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -/** - * The <code>TellMessage</code> class. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-24 - * @since 1.0 - */ -@SuppressWarnings("PMD.DataClass") -public class TellMessage implements Serializable { - private static final long serialVersionUID = 2L; - private final String id; - private final String message; - private final LocalDateTime queued; - private final String recipient; - private final String sender; - private boolean isNotified; - private boolean isReceived; - private LocalDateTime received; - - /** - * Create a new message. - * - * @param sender The sender's nick. - * @param recipient The recipient's nick. - * @param message The message. - */ - TellMessage(final String sender, final String recipient, final String message) { - this.sender = sender; - this.recipient = recipient; - this.message = message; - - this.queued = LocalDateTime.now(Clock.systemUTC()); - this.id = this.queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); - } - - /** - * Returns the message id. - * - * @return The message id. - */ - public String getId() { - return id; - } - - /** - * Returns the message text. - * - * @return The text of the message. - */ - public String getMessage() { - return message; - } - - /** - * Returns the queued date/time. - * - * @return <code>true</code> if the message is queued. - */ - LocalDateTime getQueued() { - return queued; - } - - /** - * Returns the state of the received flag. - * - * @return <code>true</code> if the message has been received. - */ - public LocalDateTime getReceived() { - return received; - } - - /** - * Returns the message's recipient. - * - * @return The recipient of the message. - */ - String getRecipient() { - return recipient; - } - - /** - * Returns the message's sender. - * - * @return The sender of the message. - */ - public String getSender() { - return sender; - } - - /** - * Matches the message sender or recipient. - * - * @param nick The nickname to match with. - * @return <code>true</code> if the nickname matches. - */ - boolean isMatch(final String nick) { - return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick)); - } - - /** - * Match the message ID. - * - * @param id The ID to match with. - * @return <code>true</code> if the id matches. - */ - boolean isMatchId(final String id) { - return this.id.equals(id); - } - - /** - * Returns the notification flag state. - * - * @return <code>true</code> if the sender has been notified. - */ - boolean isNotified() { - return isNotified; - } - - /** - * Returns the received flag state. - * - * @return <code>true</code> if the message was received. - */ - public boolean isReceived() { - return isReceived; - } - - /** - * Sets the notified flag. - */ - void setIsNotified() { - isNotified = true; - } - - /** - * Sets the received flag. - */ - void setIsReceived() { - received = LocalDateTime.now(Clock.systemUTC()); - isReceived = true; - } - - @Override - public String toString() { - return "TellMessage{" - + "id='" + id + '\'' - + ", isNotified=" + isNotified - + ", isReceived=" + isReceived - + ", message='" + message + '\'' - + ", queued=" + queued - + ", received=" + received - + ", recipient='" + recipient + '\'' - + ", sender='" + sender + '\'' - + '}'; - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt new file mode 100644 index 0000000..4dd3f40 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -0,0 +1,112 @@ +/* + * TellMessage.kt + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import java.io.Serializable +import java.time.Clock +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +/** + * The `TellMessage` class. + */ +class TellMessage internal constructor( + /** + * Returns the message's sender. + */ + val sender: String, + + /** + * Returns the message's recipient. + */ + val recipient: String, + + /** + * Returns the message text. + */ + val message: String +) : Serializable { + /** + * Returns the queued date/time. + */ + var queued: LocalDateTime = LocalDateTime.now(Clock.systemUTC()) + + /** + * Returns the message id. + */ + var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + + /** + * Returns {@code true) if a notification was send. + */ + var isNotified = false + + /** + * Returns {@code true) if the message was received. + */ + var isReceived = false + set(value) { + if (value) { + receptionDate = LocalDateTime.now(Clock.systemUTC()) + } + field = value + } + + /** + * Return the message creating date. + */ + var receptionDate: LocalDateTime = LocalDateTime.MIN + + + /** + * Matches the message sender or recipient. + */ + fun isMatch(nick: String?): Boolean { + return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true) + } + + /** + * Match the message ID. + */ + fun isMatchId(id: String): Boolean { + return this.id == id + } + + override fun toString(): String { + return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " + + "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") + } + + companion object { + private const val serialVersionUID = 2L + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java deleted file mode 100644 index 98eed8a..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * TellMessagesMgr.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.tell; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.apache.logging.log4j.Logger; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectInputStream; -import java.io.ObjectOutput; -import java.io.ObjectOutputStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.Clock; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - - -/** - * The Tell Messages Manager. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-26 - * @since 1.0 - */ -final class TellMessagesMgr { - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private TellMessagesMgr() { - throw new UnsupportedOperationException("Illegal constructor call."); - } - - /** - * Cleans the messages queue. - * - * @param tellMessages The messages list. - * @param tellMaxDays The maximum number of days to keep messages for. - * @return <code>True</code> if the queue was cleaned. - */ - static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) { - final LocalDateTime today = LocalDateTime.now(Clock.systemUTC()); - - return tellMessages.removeIf(o -> o.getQueued().plusDays(tellMaxDays).isBefore(today)); - } - - /** - * Loads the messages. - * - * @param file The serialized objects file. - * @param logger The logger. - * @return The {@link TellMessage} array. - */ - @SuppressWarnings("unchecked") - @SuppressFBWarnings("OBJECT_DESERIALIZATION") - public static List<TellMessage> load(final String file, final Logger logger) { - try { - try (final ObjectInput input = new ObjectInputStream( - new BufferedInputStream(Files.newInputStream(Paths.get(file))))) { - if (logger.isDebugEnabled()) { - logger.debug("Loading the messages."); - } - return ((List<TellMessage>) input.readObject()); - } - } catch (FileNotFoundException ignore) { - // Do nothing - } catch (IOException | ClassNotFoundException e) { - logger.error("An error occurred loading the messages queue.", e); - } - - return new ArrayList<>(); - } - - /** - * Saves the messages. - * - * @param file The serialized objects file. - * @param messages The {@link TellMessage} array. - * @param logger The logger. - */ - public static void save(final String file, final List<TellMessage> messages, final Logger logger) { - try { - try (final BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(file)))) { - try (final ObjectOutput output = new ObjectOutputStream(bos)) { - if (logger.isDebugEnabled()) { - logger.debug("Saving the messages."); - } - output.writeObject(messages); - } - } - } catch (IOException e) { - logger.error("Unable to save messages queue.", e); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt new file mode 100644 index 0000000..44f487d --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -0,0 +1,104 @@ +/* + * TellMessagesMgr.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import org.apache.logging.log4j.Logger +import java.time.LocalDateTime +import java.io.ObjectInputStream +import java.io.BufferedInputStream +import java.io.FileNotFoundException +import java.io.IOException +import java.lang.ClassNotFoundException +import java.io.BufferedOutputStream +import java.io.ObjectOutputStream +import java.nio.file.Files +import java.nio.file.Paths +import java.time.Clock +import java.util.ArrayList + +/** + * The Tell Messages Manager. + */ +internal class TellMessagesMgr private constructor() { + companion object { + /** + * Cleans the messages queue. + */ + fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Int): Boolean { + val today = LocalDateTime.now(Clock.systemUTC()) + return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) } + } + + /** + * Loads the messages. + */ + + fun load(file: String, logger: Logger): List<TellMessage> { + try { + ObjectInputStream( + BufferedInputStream(Files.newInputStream(Paths.get(file))) + ).use { input -> + if (logger.isDebugEnabled) { + logger.debug("Loading the messages.") + } + @Suppress("UNCHECKED_CAST") + return input.readObject() as List<TellMessage> + } + } catch (ignore: FileNotFoundException) { + // Do nothing + } catch (e: IOException) { + logger.error("An IO error occurred loading the messages queue.", e) + } catch (e: ClassNotFoundException) { + logger.error("An error occurred loading the messages queue.", e) + } + return ArrayList() + } + + /** + * Saves the messages. + */ + fun save(file: String, messages: List<TellMessage?>?, logger: Logger) { + try { + BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> + ObjectOutputStream(bos).use { output -> + if (logger.isDebugEnabled) { + logger.debug("Saving the messages.") + } + output.writeObject(messages) + } + } + } catch (e: IOException) { + logger.error("Unable to save messages queue.", e) + } + } + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.java b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.java deleted file mode 100644 index e57c52a..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * TellMessageTest.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.tell; - -import org.testng.annotations.Test; - -import java.time.Duration; -import java.time.LocalDateTime; -import java.time.temporal.Temporal; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The <code>TellMessageTest</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2019-07-29 - * @since 1.0 - */ -public class TellMessageTest { - private boolean isValidDate(final Temporal date) { - return Duration.between(date, LocalDateTime.now()).toMinutes() < 1; - } - - @Test - void testTellMessage() { - final String message = "Test message."; - final String recipient = "recipient"; - final String sender = "sender"; - final TellMessage tellMessage = new TellMessage(sender, recipient, message); - - assertThat(tellMessage.getSender()).as(sender).isEqualTo(sender); - assertThat(tellMessage.getRecipient()).as(recipient).isEqualTo(recipient); - assertThat(tellMessage.getMessage()).as(message).isEqualTo(message); - assertThat(isValidDate(tellMessage.getQueued())).as("queued is valid date/time").isTrue(); - - assertThat(tellMessage.isMatch(sender)).as("match sender").isTrue(); - assertThat(tellMessage.isMatch(recipient)).as("match recipient").isTrue(); - assertThat(tellMessage.isMatch("foo")).as("foo is no match").isFalse(); - - assertThat(tellMessage.isMatchId(tellMessage.getId())).as("is match ID").isTrue(); - - tellMessage.setIsReceived(); - assertThat(tellMessage.isReceived()).as("is received").isTrue(); - assertThat(isValidDate(tellMessage.getReceived())).as("received is valid date/time").isTrue(); - - tellMessage.setIsNotified(); - assertThat(tellMessage.isNotified()).as("is notified").isTrue(); - } -} - diff --git a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt new file mode 100644 index 0000000..5dbf7f3 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -0,0 +1,68 @@ +/* + * TellMessageTest.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import java.time.temporal.Temporal +import java.time.LocalDateTime +import org.assertj.core.api.Assertions +import org.testng.annotations.Test +import java.time.Duration + +/** + * The `TellMessageTest` class. + */ +class TellMessageTest { + private fun isValidDate(date: Temporal): Boolean { + return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 + } + + @Test + fun testTellMessage() { + val message = "Test message." + val recipient = "recipient" + val sender = "sender" + val tellMessage = TellMessage(sender, recipient, message) + Assertions.assertThat(tellMessage.sender).`as`(sender).isEqualTo(sender) + Assertions.assertThat(tellMessage.recipient).`as`(recipient).isEqualTo(recipient) + Assertions.assertThat(tellMessage.message).`as`(message).isEqualTo(message) + Assertions.assertThat(isValidDate(tellMessage.queued)).`as`("queued is valid date/time").isTrue + Assertions.assertThat(tellMessage.isMatch(sender)).`as`("match sender").isTrue + Assertions.assertThat(tellMessage.isMatch(recipient)).`as`("match recipient").isTrue + Assertions.assertThat(tellMessage.isMatch("foo")).`as`("foo is no match").isFalse + Assertions.assertThat(tellMessage.isMatchId(tellMessage.id)).`as`("is match ID").isTrue + tellMessage.isReceived = true + Assertions.assertThat(tellMessage.isReceived).`as`("is received").isTrue + Assertions.assertThat(isValidDate(tellMessage.receptionDate)).`as`("received is valid date/time").isTrue + tellMessage.isNotified = true + Assertions.assertThat(tellMessage.isNotified).`as`("is notified").isTrue + } +} From a44b0c1f49e9bb14c410091fb483b36b95b21053 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 15:50:03 -0800 Subject: [PATCH 447/842] Version 0.8.0-beta+305 --- .../java/net/thauvin/erik/mobibot/ReleaseInfo.java | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java index 654713c..900222b 100644 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java @@ -13,14 +13,14 @@ import java.time.*; */ public final class ReleaseInfo { public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+302"; + public static final String VERSION = "0.8.0-beta+305"; public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1607079230723L), ZoneId.systemDefault()); + LocalDateTime.ofInstant(Instant.ofEpochMilli(1607122730368L), ZoneId.systemDefault()); public static final int MAJOR = 0; public static final int MINOR = 8; public static final int PATCH = 0; - public static final String BUILDMETA = "302"; + public static final String BUILDMETA = "305"; public static final String PRERELEASE = "beta"; public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; diff --git a/version.properties b/version.properties index 375622b..25393d2 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Dec 04 02:53:48 PST 2020 -version.buildmeta=302 +#Fri Dec 04 15:43:01 PST 2020 +version.buildmeta=312 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+302 +version.semver=0.8.0-beta+312 From 69441f7006ebe39e6096ca64ae35e598ceb02731 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 23:11:39 -0800 Subject: [PATCH 448/842] Converted Mobibot class to Kotlin. --- .idea/compiler.xml | 11 - .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 83 +- .idea/modules/mobibot.test.iml | 26 +- ReleaseInfo.mustache | 32 + build.gradle | 13 +- config/detekt/baseline.xml | 16 +- mobibot.mustache | 36 - .../net/thauvin/erik/mobibot/ReleaseInfo.java | 36 - .../net/thauvin/erik/mobibot/Mobibot.java | 956 ------------------ .../java/net/thauvin/erik/mobibot/Mobibot.kt | 776 ++++++++++++++ .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 8 +- .../erik/mobibot/commands/tell/TellMessage.kt | 12 +- .../mobibot/commands/tell/TellMessagesMgr.kt | 9 +- .../erik/mobibot/entries/EntriesMgr.kt | 5 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 10 +- .../erik/mobibot/modules/AbstractModule.kt | 5 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 3 +- .../erik/mobibot/modules/CurrencyConverter.kt | 8 +- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 12 +- .../erik/mobibot/modules/StockQuote.kt | 5 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 3 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 3 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 4 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- version.properties | 6 +- 30 files changed, 956 insertions(+), 1134 deletions(-) create mode 100644 ReleaseInfo.mustache delete mode 100644 mobibot.mustache delete mode 100644 src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java delete mode 100644 src/main/java/net/thauvin/erik/mobibot/Mobibot.java create mode 100644 src/main/java/net/thauvin/erik/mobibot/Mobibot.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 1f720ec..fb7f4a8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,17 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CompilerConfiguration"> - <annotationProcessing> - <profile name="Gradle Imported" enabled="true"> - <outputRelativeToContentRoot value="true" /> - <option name="semver.project.dir" value="$PROJECT_DIR$" /> - <processorPath useClasspath="false"> - <entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" /> - <entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" /> - </processorPath> - <module name="mobibot.main" /> - </profile> - </annotationProcessing> <bytecodeTargetLevel target="11" /> </component> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 51a4995..85e216a 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index c6fe00f..4c00984 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> + <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -20,6 +20,9 @@ </option> <option name="pluginClasspaths"> <array> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.4.20/fc1d26586910b32d676480c75acd3e922e5e81fa/kotlin-compiler-embeddable-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.4.20/a051291fb01bf2397759625626fec670cd57b3f0/kotlin-daemon-embeddable-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.4.20/411fc46e908bfa9c034f52b0d31b2e1f61f06127/kotlin-reflect-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.20/2e2bf29688a76cec111df56bc5e358c5bbc5057/kotlin-scripting-common-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.20/83704fbbe39946cc2ac6d0f07f41947abeb8dc20/kotlin-scripting-jvm-1.4.20.jar" /> @@ -27,6 +30,7 @@ <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option value="$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar" /> </array> </option> <option name="errors"> @@ -39,46 +43,59 @@ <component name="NewModuleRootManager"> <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> <exclude-output /> - <content url="file://$MODULE_DIR$/../../src/generated/java"> - <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" generated="true" /> + <content url="file://$MODULE_DIR$/../../build/generated/source/kapt/main"> + <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kapt/main" isTestSource="false" generated="true" /> + </content> + <content url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/main"> + <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/main" isTestSource="false" generated="true" /> </content> <content url="file://$MODULE_DIR$/../../src/main"> <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> </content> <orderEntry type="inheritedJdk" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library"> + <library name="Gradle: kaptGeneratedClasses"> + <CLASSES> + <root url="file://$MODULE_DIR$/../../build/tmp/kapt3/classes/main" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index f864b22..822bffe 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.18.1/aaa02680dd92a568a4278bb40aa4a6305f632ec0/assertj-core-3.18.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/media/erik/Projects/java/mobibot/build/tmp/kapt3/classes/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.18.1/aaa02680dd92a568a4278bb40aa4a6305f632ec0/assertj-core-3.18.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -17,6 +17,7 @@ <array> <option value="$MODULE_DIR$/../../build/classes/java/main" /> <option value="$MODULE_DIR$/../../build/classes/kotlin/main" /> + <option value="$MODULE_DIR$/../../build/tmp/kapt3/classes/main" /> <option value="$MODULE_DIR$/../../build/libs/mobibot.jar" /> </array> </option> @@ -27,6 +28,9 @@ </option> <option name="pluginClasspaths"> <array> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.4.20/fc1d26586910b32d676480c75acd3e922e5e81fa/kotlin-compiler-embeddable-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.4.20/a051291fb01bf2397759625626fec670cd57b3f0/kotlin-daemon-embeddable-1.4.20.jar" /> + <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.4.20/411fc46e908bfa9c034f52b0d31b2e1f61f06127/kotlin-reflect-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.20/2e2bf29688a76cec111df56bc5e358c5bbc5057/kotlin-scripting-common-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.20/83704fbbe39946cc2ac6d0f07f41947abeb8dc20/kotlin-scripting-jvm-1.4.20.jar" /> @@ -34,6 +38,7 @@ <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> + <option value="$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar" /> </array> </option> <option name="errors"> @@ -46,6 +51,12 @@ <component name="NewModuleRootManager"> <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> <exclude-output /> + <content url="file://$MODULE_DIR$/../../build/generated/source/kapt/test"> + <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kapt/test" isTestSource="true" generated="true" /> + </content> + <content url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/test"> + <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/test" isTestSource="true" generated="true" /> + </content> <content url="file://$MODULE_DIR$/../../src/test"> <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> @@ -53,10 +64,20 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library"> + <library name="Gradle: kaptGeneratedClasses"> + <CLASSES> + <root url="file://$MODULE_DIR$/../../build/tmp/kapt3/classes/test" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> <orderEntry type="module" module-name="mobibot.main" /> <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> @@ -100,6 +121,7 @@ <orderEntry type="library" name="Gradle: com.google.errorprone:error_prone_annotations:2.1.3" level="project" /> <orderEntry type="library" name="Gradle: com.google.j2objc:j2objc-annotations:1.1" level="project" /> <orderEntry type="library" name="Gradle: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> </component> <component name="TestModuleProperties" production-module="mobibot.main" /> </module> \ No newline at end of file diff --git a/ReleaseInfo.mustache b/ReleaseInfo.mustache new file mode 100644 index 0000000..b2672e4 --- /dev/null +++ b/ReleaseInfo.mustache @@ -0,0 +1,32 @@ +/* +* This file is automatically generated. +* Do not modify! -- ALL CHANGES WILL BE ERASED! +*/ +package {{packageName}} + +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.Instant + +/** +* Provides semantic version information. +* +* @author [Semantic Version Annotation Processor](https://github.com/ethauvin/semver) +*/ +object ReleaseInfo{ + const val PROJECT = "{{project}}" + const val VERSION = "{{version}}" + + @JvmField + val BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()) + + const val MAJOR = {{major}} + const val MINOR = {{minor}} + const val PATCH = {{patch}} + const val BUILDMETA = "{{buildMeta}}" + const val PRERELEASE = "{{preRelease}}" + + const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val AUTHOR = "Erik C. Thauvin" + const val AUTHOR_URL = "https://erik.thauvin.net/" +} diff --git a/build.gradle b/build.gradle index 6b905f9..9ff3602 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ plugins { id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.4.20' + id 'org.jetbrains.kotlin.kapt' version '1.4.20' id 'org.sonarqube' version '3.0' id 'pmd' } @@ -33,8 +34,8 @@ repositories { } dependencies { - annotationProcessor semverProcessor - compileOnly semverProcessor + kapt(semverProcessor) + implementation(semverProcessor) implementation 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' @@ -77,14 +78,18 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +kapt { + arguments { + arg("semver.project.dir", projectDir) + } +} + application { mainClassName = packageName + '.Mobibot' } tasks.withType(JavaCompile) { options.encoding = 'UTF-8' - options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java") - options.compilerArgs += ["-Asemver.project.dir=$projectDir"] } compileJava { diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 59b5db4..46ddfa2 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -3,8 +3,10 @@ <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>ComplexMethod:Mobibot.kt$Mobibot$override fun onPrivateMessage( sender: String, login: String, hostname: String, message: String )</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>LongMethod:Mobibot.kt$Mobibot.Companion$ @JvmStatic fun main(args: Array<String>)</ID> <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> @@ -22,6 +24,11 @@ <ID>MagicNumber:Cycle.kt$Cycle$10</ID> <ID>MagicNumber:Dice.kt$Dice$7</ID> <ID>MagicNumber:Ignore.kt$Ignore$8</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$10</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$1000L</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$3</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$5</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$8</ID> <ID>MagicNumber:Modules.kt$Modules$7</ID> <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> <ID>MagicNumber:Tell.kt$Tell$50</ID> @@ -50,11 +57,14 @@ <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: ArrayList<EntryLink>): String</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> - <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ fun setTags(tags: List<String?>)</ID> + <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> <ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:Mobibot.kt$Mobibot$ fun connect()</ID> + <ID>NestedBlockDepth:Mobibot.kt$Mobibot$override fun onMessage( channel: String, sender: String, login: String, hostname: String, message: String )</ID> + <ID>NestedBlockDepth:Mobibot.kt$Mobibot$override fun onPrivateMessage( sender: String, login: String, hostname: String, message: String )</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID> @@ -63,14 +73,18 @@ <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> + <ID>ReturnCount:Mobibot.kt$Mobibot$override fun onMessage( channel: String, sender: String, login: String, hostname: String, message: String )</ID> <ID>ReturnCount:Utils.kt$Utils.Companion$ @JvmStatic fun colorize(s: String?, color: String): String</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> + <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID> + <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID> <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> + <ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID> <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> <ID>TooManyFunctions:Utils.kt$Utils$Companion</ID> </CurrentIssues> diff --git a/mobibot.mustache b/mobibot.mustache deleted file mode 100644 index a090f01..0000000 --- a/mobibot.mustache +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is automatically generated. - * Do not modify! -- ALL CHANGES WILL BE ERASED! - */ -package {{packageName}}; - -import java.time.*; - -/** - * Provides semantic version information. - * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> - */ -public final class {{className}} { - public static final String PROJECT = "{{project}}"; - public static final String VERSION = "{{version}}"; - public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); - - public static final int MAJOR = {{major}}; - public static final int MINOR = {{minor}}; - public static final int PATCH = {{patch}}; - public static final String BUILDMETA = "{{buildMeta}}"; - public static final String PRERELEASE = "{{preRelease}}"; - - public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; - public static final String AUTHOR = "Erik C. Thauvin"; - public static final String AUTHOR_URL = "https://erik.thauvin.net/"; - - /** - * Disables the default constructor. - */ - private {{className}}() { - throw new UnsupportedOperationException("Illegal constructor call."); - } -} diff --git a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java b/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java deleted file mode 100644 index 900222b..0000000 --- a/src/generated/java/net/thauvin/erik/mobibot/ReleaseInfo.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is automatically generated. - * Do not modify! -- ALL CHANGES WILL BE ERASED! - */ -package net.thauvin.erik.mobibot; - -import java.time.*; - -/** - * Provides semantic version information. - * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> - */ -public final class ReleaseInfo { - public static final String PROJECT = "mobibot"; - public static final String VERSION = "0.8.0-beta+305"; - public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli(1607122730368L), ZoneId.systemDefault()); - - public static final int MAJOR = 0; - public static final int MINOR = 8; - public static final int PATCH = 0; - public static final String BUILDMETA = "305"; - public static final String PRERELEASE = "beta"; - - public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; - public static final String AUTHOR = "Erik C. Thauvin"; - public static final String AUTHOR_URL = "https://erik.thauvin.net/"; - - /** - * Disables the default constructor. - */ - private ReleaseInfo() { - throw new UnsupportedOperationException("Illegal constructor call."); - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java deleted file mode 100644 index 420833d..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java +++ /dev/null @@ -1,956 +0,0 @@ -/* - * Mobibot.java - * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.thauvin.erik.mobibot.commands.AbstractCommand; -import net.thauvin.erik.mobibot.commands.AddLog; -import net.thauvin.erik.mobibot.commands.ChannelFeed; -import net.thauvin.erik.mobibot.commands.Cycle; -import net.thauvin.erik.mobibot.commands.Ignore; -import net.thauvin.erik.mobibot.commands.Info; -import net.thauvin.erik.mobibot.commands.Me; -import net.thauvin.erik.mobibot.commands.Modules; -import net.thauvin.erik.mobibot.commands.Msg; -import net.thauvin.erik.mobibot.commands.Nick; -import net.thauvin.erik.mobibot.commands.Recap; -import net.thauvin.erik.mobibot.commands.Say; -import net.thauvin.erik.mobibot.commands.Users; -import net.thauvin.erik.mobibot.commands.Versions; -import net.thauvin.erik.mobibot.commands.links.Comment; -import net.thauvin.erik.mobibot.commands.links.LinksMgr; -import net.thauvin.erik.mobibot.commands.links.Posting; -import net.thauvin.erik.mobibot.commands.links.Tags; -import net.thauvin.erik.mobibot.commands.links.View; -import net.thauvin.erik.mobibot.commands.tell.Tell; -import net.thauvin.erik.mobibot.entries.EntriesMgr; -import net.thauvin.erik.mobibot.entries.EntryLink; -import net.thauvin.erik.mobibot.modules.AbstractModule; -import net.thauvin.erik.mobibot.modules.Calc; -import net.thauvin.erik.mobibot.modules.CurrencyConverter; -import net.thauvin.erik.mobibot.modules.Dice; -import net.thauvin.erik.mobibot.modules.GoogleSearch; -import net.thauvin.erik.mobibot.modules.Joke; -import net.thauvin.erik.mobibot.modules.Lookup; -import net.thauvin.erik.mobibot.modules.Ping; -import net.thauvin.erik.mobibot.modules.RockPaperScissors; -import net.thauvin.erik.mobibot.modules.StockQuote; -import net.thauvin.erik.mobibot.modules.Twitter; -import net.thauvin.erik.mobibot.modules.War; -import net.thauvin.erik.mobibot.modules.Weather2; -import net.thauvin.erik.mobibot.modules.WorldTime; -import net.thauvin.erik.mobibot.msg.Message; -import net.thauvin.erik.pinboard.PinboardPoster; -import net.thauvin.erik.semver.Version; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.config.Configurator; -import org.jibble.pircbot.PircBot; -import org.jibble.pircbot.User; - -import java.io.BufferedOutputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; -import java.util.Properties; -import java.util.Timer; -import java.util.logging.ConsoleHandler; -import java.util.regex.Pattern; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.apache.commons.lang3.StringUtils.lowerCase; - -/** - * Implements the #mobitopia bot. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created Jan 31, 2004 - * @since 1.0 - */ -@SuppressWarnings("WeakerAccess") -@Version(properties = "version.properties", - className = "ReleaseInfo") -public class Mobibot extends PircBot { - // Logger - private static final Logger LOGGER = LogManager.getLogger(Mobibot.class); - // Maximum number of times the bot will try to reconnect, if disconnected - private static final int MAX_RECONNECT = 10; - // Timer - private static final Timer TIMER = new Timer(true); - // Commands and Modules - private final Addons addons = new Addons(); - // Main channel - private final String ircChannel; - // IRC port - private final int ircPort; - // IRC server - private final String ircServer; - // Logger default level - private final Level loggerLevel; - // Log directory - private final String logsDir; - // Tell command - private final Tell tell; - // Today's date - private final String today = Utils.today(); - // Twitter module - private final Twitter twitter; - // Backlogs URL - private String backLogsUrl = ""; - // Ident message - private String identMsg = ""; - // Ident nick - private String identNick = ""; - // NickServ ident password - private String identPwd = ""; - // Pinboard posts handler - private PinboardPoster pinboard; - // Weblog URL - private String weblogUrl = ""; - - /** - * Creates a new {@link Mobibot} instance. - * - * @param channel The irc channel. - * @param nickname The bot's nickname. - * @param logsDirPath The path to the logs directory. - * @param p The bot's properties. - */ - public Mobibot(final String nickname, final String channel, final String logsDirPath, final Properties p) { - super(); - System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", - String.valueOf(Constants.CONNECT_TIMEOUT)); - System.getProperties().setProperty("sun.net.client.defaultReadTimeout", - String.valueOf(Constants.CONNECT_TIMEOUT)); - - setName(nickname); - - ircServer = p.getProperty("server", Constants.DEFAULT_SERVER); - ircPort = Utils.getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT); - ircChannel = channel; - logsDir = logsDirPath; - - // Set the logger level - loggerLevel = LOGGER.getLevel(); - - // Load the current entries and backlogs, if any - try { - LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, ircChannel); - LOGGER.debug("Last feed: {}", LinksMgr.getStartDate()); - } catch (Exception e) { - LOGGER.error("An error occurred while loading the logs.", e); - } - - // Initialize the bot - setVerbose(true); - setAutoNickChange(true); - setLogin(p.getProperty("login", getName())); - setVersion(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION); - // setMessageDelay(1000); - setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", "")); - - // Set the URLs - setWeblogUrl(p.getProperty("weblog", "")); - setBacklogsUrl(Utils.ensureDir(p.getProperty("backlogs", weblogUrl), true)); - - // Set the pinboard authentication - setPinboardAuth(p.getProperty("pinboard-api-token")); - - // Load the commands - addons.add(new AddLog(this), p); - addons.add(new ChannelFeed(this, getChannelName()), p); - addons.add(new Cycle(this), p); - addons.add(new Ignore(this), p); - addons.add(new Info(this), p); - addons.add(new Me(this), p); - addons.add(new Modules(this), p); - addons.add(new Msg(this), p); - addons.add(new Nick(this), p); - addons.add(new Recap(this), p); - addons.add(new Say(this), p); - addons.add(new Users(this), p); - addons.add(new Versions(this), p); - - // Tell command - tell = new Tell(this); - addons.add(tell, p); - - // Load the links commands - addons.add(new Comment(this), p); - addons.add(new Posting(this), p); - addons.add(new Tags(this), p); - addons.add(new LinksMgr(this), p); - addons.add(new View(this), p); - - // Load the modules - addons.add(new Calc(this), p); - addons.add(new CurrencyConverter(this), p); - addons.add(new Dice(this), p); - addons.add(new GoogleSearch(this), p); - addons.add(new Joke(this), p); - addons.add(new Lookup(this), p); - addons.add(new Ping(this), p); - addons.add(new RockPaperScissors(this), p); - addons.add(new StockQuote(this), p); - addons.add(new War(this), p); - addons.add(new Weather2(this), p); - addons.add(new WorldTime(this), p); - - // Twitter module - twitter = new Twitter(this); - addons.add(twitter, p); - - // Sort the addons - addons.sort(); - - // Save the entries - LinksMgr.saveEntries(this, true); - } - - /** - * The Truth Is Out There... - * - * @param args The command line arguments. - */ - @SuppressFBWarnings( - { "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", - "DM_DEFAULT_ENCODING", - "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" }) - @SuppressWarnings({ "PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource" }) - public static void main(final String[] args) { - // Setup the command line options - final Options options = new Options() - .addOption(Constants.HELP_ARG.substring(0, 1), - Constants.HELP_ARG, - false, - "print this help message") - .addOption(Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG, false, - "print debug & logging data directly to the console") - .addOption(Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg() - .argName("file") - .desc("use " + "alternate properties file") - .longOpt(Constants.PROPS_ARG).build()) - .addOption(Constants.VERSION_ARG.substring(0, 1), - Constants.VERSION_ARG, - false, - "print version info"); - - // Parse the command line - final CommandLineParser parser = new DefaultParser(); - CommandLine commandLine = null; - - try { - commandLine = parser.parse(options, args); - } catch (ParseException e) { - System.err.println("CLI Parsing failed. Reason: " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } - - if (commandLine.hasOption(Constants.HELP_ARG.charAt(0))) { - // Output the usage - new HelpFormatter().printHelp(Mobibot.class.getName(), options); - } else if (commandLine.hasOption(Constants.VERSION_ARG.charAt(0))) { - System.out.println(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION - + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')'); - System.out.println(ReleaseInfo.WEBSITE); - } else { - final Properties p = new Properties(); - try (final InputStream fis = Files.newInputStream( - Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG.charAt(0), "./mobibot.properties")))) { - // Load the properties files - p.load(fis); - } catch (FileNotFoundException e) { - System.err.println("Unable to find properties file."); - e.printStackTrace(System.err); - System.exit(1); - } catch (IOException e) { - System.err.println("Unable to open properties file."); - e.printStackTrace(System.err); - System.exit(1); - } - - final String nickname = p.getProperty("nick", lowerCase(Mobibot.class.getName())); - final String channel = p.getProperty("channel"); - final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false); - - // Redirect the stdout and stderr - if (!commandLine.hasOption(Constants.DEBUG_ARG.charAt(0))) { - try { - final PrintStream stdout = new PrintStream(new BufferedOutputStream(new FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)), true); - System.setOut(stdout); - } catch (IOException e) { - System.err.println("Unable to open output (stdout) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - - try { - final PrintStream stderr = - new PrintStream(new BufferedOutputStream( - new FileOutputStream(logsDir + nickname + ".err", true)), true); - System.setErr(stderr); - } catch (IOException e) { - System.err.println("Unable to open error (stderr) log file."); - e.printStackTrace(System.err); - System.exit(1); - } - } - - // Create the bot - final Mobibot bot = new Mobibot(nickname, channel, logsDir, p); - - // Connect - bot.connect(); - } - } - - /** - * Sends an action to the current channel. - * - * @param action The action. - */ - public final void action(final String action) { - action(ircChannel, action); - } - - /** - * Sends an action to the channel. - * - * @param channel The channel. - * @param action The action. - */ - private void action(final String channel, final String action) { - if (isNotBlank(channel) && isNotBlank(action)) { - sendAction(channel, action); - } - } - - /** - * Adds pin on pinboard. - * - * @param entry The entry to add. - */ - public final void addPin(final EntryLink entry) { - if (pinboard != null) { - PinboardUtils.addPin(pinboard, ircServer, entry); - } - } - - /** - * Connects to the server and joins the channel. - */ - @SuppressFBWarnings({ "DM_EXIT", "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE" }) - public final void connect() { - try { - connect(ircServer, ircPort); - } catch (Exception e) { - int retries = 0; - - while ((retries++ < MAX_RECONNECT) && !isConnected()) { - sleep(10); - - try { - connect(ircServer, ircPort); - } catch (Exception ex) { - if (retries == MAX_RECONNECT) { - LOGGER.debug("Unable to reconnect to {} after {} retries.", ircServer, MAX_RECONNECT, ex); - e.printStackTrace(System.err); - System.exit(1); - } - } - } - } - identify(); - joinChannel(); - } - - /** - * Deletes pin on pinboard. - * - * @param entry The entry to delete. - */ - public final void deletePin(final int index, final EntryLink entry) { - if (pinboard != null) { - PinboardUtils.deletePin(pinboard, entry); - } - if (twitter.isAutoPost()) { - twitter.removeEntry(index); - } - } - - /** - * Returns the backlogs URL. - * - * @return The backlogs URL. - */ - public final String getBacklogsUrl() { - return backLogsUrl; - } - - /** - * Returns the current channel. - * - * @return The current channel. - */ - public final String getChannel() { - return ircChannel; - } - - /** - * Returns the current channel name. - * - * @return The current channel name. - */ - @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD") - public final String getChannelName() { - return ircChannel.substring(1); - } - - /** - * Returns the irc server. - * - * @return The irc server. - */ - public final String getIrcServer() { - return ircServer; - } - - /** - * Returns the bot's logger. - * - * @return The bot's logger. - */ - public final Logger getLogger() { - return LOGGER; - } - - /** - * Returns the log directory. - * - * @return the log directory. - */ - public final String getLogsDir() { - return logsDir; - } - - /** - * Returns the enabled modules names. - * - * @return The modules names. - */ - public final List<String> getModulesNames() { - return addons.getModulesNames(); - } - - /** - * Returns the Tell command. - * - * @return The tell command. - */ - public final Tell getTell() { - return tell; - } - - /** - * Returns the bot's timer. - * - * @return The timer. - */ - public final Timer getTimer() { - return TIMER; - } - - /** - * Get today's date for the feed. - * - * @return Today's date. - */ - public String getToday() { - return today; - } - - /** - * Returns the Twitter command. - * - * @return The Twitter command. - */ - public final Twitter getTwitter() { - return twitter; - } - - /** - * Returns the weblog URL. - * - * @return The weblog URL. - */ - public final String getWeblogUrl() { - return weblogUrl; - } - - /** - * Responds with the commands help, if any. - * - * @param sender The nick of the person requesting Constants. - * @param topic The help topic. - * @param isPrivate The private flag. - * @return {@code true} if the topic was found, {@code false} otherwise. - */ - private boolean helpCommands(final String sender, final String topic, final boolean isPrivate) { - for (final AbstractCommand command : addons.getCommands()) { - if (command.isVisible() && command.getName().startsWith(topic)) { - return command.helpResponse(topic, sender, isOp(sender), isPrivate); - } - } - return false; - } - - /** - * Responds with the default Constants. - * - * @param sender The nick of the person requesting Constants. - * @param isOp The channel operator flag. - * @param isPrivate The private flag. - */ - public void helpDefault(final String sender, final boolean isOp, final boolean isPrivate) { - send(sender, "Type a URL on " + ircChannel + " to post it.", isPrivate); - send(sender, "For more information on a specific command, type:", isPrivate); - send(sender, - Utils.helpIndent(Utils.helpFormat("%c " + Constants.HELP_CMD + " <command>", getNick(), isPrivate)), - isPrivate); - send(sender, "The commands are:", isPrivate); - sendList(sender, addons.getNames(), 8, isPrivate, true); - if (isOp) { - send(sender, "The op commands are:", isPrivate); - sendList(sender, addons.getOps(), 8, isPrivate, true); - } - } - - /** - * Responds with the modules help, if any. - * - * @param sender The nick of the person requesting Constants. - * @param topic The help topic. - * @param isPrivate The private flag. - * @return {@code true} if the topic was found, {@code false} otherwise. - */ - private boolean helpModules(final String sender, final String topic, final boolean isPrivate) { - for (final AbstractModule module : addons.getModules()) { - for (final String cmd : module.commands) { - if (topic.equals(cmd)) { - module.helpResponse(sender, isPrivate); - return true; - } - } - } - return false; - } - - /** - * Responds with the bot's Constants. - * - * @param sender The nick of the person who sent the private message. - * @param topic The help topic, if any. - * @param isPrivate The private flag. - */ - private void helpResponse(final String sender, final String topic, final boolean isPrivate) { - final boolean isOp = isOp(sender); - if (StringUtils.isBlank(topic)) { - helpDefault(sender, isOp, isPrivate); - } else { - // Command, Modules or Default - if (!helpCommands(sender, topic, isPrivate) && !helpModules(sender, lowerCase(topic).trim(), isPrivate)) { - helpDefault(sender, isOp, isPrivate); - } - } - } - - /** - * Identifies the bot. - */ - private void identify() { - // Identify with NickServ - if (isNotBlank(identPwd)) { - identify(identPwd); - } - - // Identify with a specified nick - if (isNotBlank(identNick) && isNotBlank(identMsg)) { - sendMessage(identNick, identMsg); - } - } - - /** - * Returns <code>true</code> if the specified sender is an Op on the {@link #ircChannel channel}. - * - * @param sender The sender. - * @return true, if the sender is an Op. - */ - public boolean isOp(final String sender) { - final User[] users = getUsers(ircChannel); - - for (final User user : users) { - if (user.getNick().equals(sender)) { - return user.isOp(); - } - } - - return false; - } - - /** - * Joins the bot's channel. - */ - public final void joinChannel() { - joinChannel(ircChannel); - twitter.notification(getName() + " " + ReleaseInfo.VERSION + " has joined " + getChannel()); - } - - /** - * {@inheritDoc} - */ - @Override - protected final void onDisconnect() { - if (isNotBlank(weblogUrl)) { - setVersion(weblogUrl); - } - sleep(5); - connect(); - } - - /** - * {@inheritDoc} - */ - @SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", - justification = "Working on it.") - @Override - protected final void onMessage(final String channel, final String sender, final String login, final String hostname, - final String message) { - LOGGER.debug(">>> {} : {}", sender, message); - - tell.send(sender, true); - - if (message.matches("(?i)" + Pattern.quote(getNick()) + ":.*")) { // mobibot: <command> - final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2); - final String cmd = lowerCase(cmds[0]); - - String args = ""; - - if (cmds.length > 1) { - args = cmds[1].trim(); - } - - if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help - helpResponse(sender, args, false); - } else { - // Commands - for (final AbstractCommand command : addons.getCommands()) { - if (command.isPublic() && command.getName().startsWith(cmd)) { - command.commandResponse(sender, login, args, isOp(sender), false); - return; - } - } - // Modules - for (final AbstractModule module : addons.getModules()) { // modules - for (final String c : module.commands) { - if (cmd.startsWith(c)) { - module.commandResponse(sender, cmd, args, false); - return; - } - } - } - } - } else { - // Commands, e.g.: https://www.example.com/ - for (final AbstractCommand command : addons.getCommands()) { - if (command.matches(message)) { - command.commandResponse(sender, login, message, isOp(sender), false); - return; - } - } - } - - Recap.storeRecap(sender, message, false); - } - - /** - * {@inheritDoc} - */ - @SuppressFBWarnings(value = { "DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY" }, - justification = "Yes, we want to bail out.") - @Override - protected final void onPrivateMessage(final String sender, final String login, final String hostname, - final String message) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(">>> {} : {}", sender, message); - } - - final String[] cmds = message.split(" ", 2); - final String cmd = lowerCase(cmds[0]); - String args = ""; - - if (cmds.length > 1) { - args = cmds[1].trim(); - } - - final boolean isOp = isOp(sender); - - if (cmd.startsWith(Constants.HELP_CMD)) { // help - helpResponse(sender, args, true); - } else if (isOp && "kill".equals(cmd)) { // kill - twitter.notification(getName() + " killed by " + sender + " on " + getChannel()); - sendRawLine("QUIT : Poof!"); - System.exit(0); - } else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug - if (LOGGER.isDebugEnabled()) { - Configurator.setLevel(LOGGER.getName(), loggerLevel); - } else { - Configurator.setLevel(LOGGER.getName(), Level.DEBUG); - } - send(sender, "Debug logging is " + (LOGGER.isDebugEnabled() ? "enabled." : "disabled."), true); - } else if (isOp && Constants.DIE_CMD.equals(cmd)) { // die - send(sender + " has just signed my death sentence."); - TIMER.cancel(); - twitter.shutdown(); - twitter.notification(getName() + " stopped by " + sender + " on " + getChannel()); - sleep(3); - quitServer("The Bot Is Out There!"); - System.exit(0); - } else { - for (final AbstractCommand command : addons.getCommands()) { - if (command.getName().startsWith(cmd)) { - command.commandResponse(sender, login, args, isOp, true); - return; - } - } - for (final AbstractModule module : addons.getModules()) { - if (module.isPrivateMsgEnabled()) { - for (final String c : module.commands) { - if (cmd.equals(c)) { - module.commandResponse(sender, cmd, args, true); - return; - } - } - } - } - helpDefault(sender, isOp, true); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected final void onAction(final String sender, final String login, final String hostname, final String target, - final String action) { - if (ircChannel.equals(target)) { - Recap.storeRecap(sender, action, true); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onJoin(final String channel, final String sender, final String login, final String hostname) { - tell.send(sender); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) { - tell.send(newNick); - } - - /** - * Sends a private message or notice. - * - * @param sender The channel or nick of the person who sent the message. - * @param message The actual message. - * @param isPrivate Set to <code>true</code> if the response should be a private message, otherwise a notice is - * sent. - */ - public final void send(final String sender, final String message, final boolean isPrivate) { - if (isNotBlank(message) && isNotBlank(sender)) { - if (isPrivate) { - LOGGER.debug("Sending message to {} : {}", sender, message); - sendMessage(sender, message); - } else { - LOGGER.debug("Sending notice to {} : {}", sender, message); - sendNotice(sender, message); - } - } - } - - /** - * Sends a notice to the channel. - * - * @param notice The notice message. - */ - public final void send(final String notice) { - send(getChannel(), notice, false); - - } - - /** - * Sends a message. - * - * @param who The channel or nick of the person who sent the command. - * @param message The message. - */ - public final void send(final String who, final Message message) { - send(message.isNotice() ? who : getChannel(), message.getMsg(), message.getColor(), message.isPrivate()); - } - - /** - * Sends a message. - * - * @param who The channel or nick of the person who sent the command. - * @param message The actual message. - * @param color The message's color. - * @param isPrivate The private flag. - */ - public final void send(final String who, final String message, final String color, final boolean isPrivate) { - send(who, Utils.colorize(message, color), isPrivate); - } - - /** - * Send a formatted commands/modules, etc. list. - * - * @param nick The nick to send the list to. - * @param list The list to format. - * @param size The number of items per line. - * @param isPrivate The private flag. - * @param isBold The bold flag - */ - public final void sendList(final String nick, - final List<String> list, - final int size, - final boolean isPrivate, - final boolean isBold) { - for (int i = 0; i < list.size(); i += size) { - send(nick, Utils.helpIndent( - String.join(" ", list.subList(i, Math.min(list.size(), i + size))), isBold), isPrivate); - } - } - - /** - * Sets the backlogs URL. - * - * @param url The backlogs URL. - */ - final void setBacklogsUrl(final String url) { - backLogsUrl = url; - } - - /** - * Sets the bot's identification. - * - * @param pwd The password for NickServ, if any. - * @param nick The ident nick name. - * @param msg The ident message. - */ - final void setIdentity(final String pwd, final String nick, final String msg) { - identPwd = pwd; - identNick = nick; - identMsg = msg; - } - - /** - * Sets the pinboard authentication. - * - * @param apiToken The API token - */ - final void setPinboardAuth(final String apiToken) { - if (isNotBlank(apiToken)) { - pinboard = new PinboardPoster(apiToken); - if (LOGGER.isDebugEnabled()) { - final ConsoleHandler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(java.util.logging.Level.FINE); - pinboard.getLogger().addHandler(consoleHandler); - pinboard.getLogger().setLevel(java.util.logging.Level.FINE); - } - } - } - - /** - * Sets the weblog URL. - * - * @param url The weblog URL. - */ - final void setWeblogUrl(final String url) { - weblogUrl = url; - } - - /** - * Sleeps for the specified number of seconds. - * - * @param secs The number of seconds to sleep for. - */ - public final void sleep(final int secs) { - try { - Thread.sleep(secs * 1000L); - } catch (InterruptedException ignore) { - // Do nothing - } - } - - /** - * Updates pin on pinboard. - * - * @param oldUrl The old pin url. - * @param entry The entry to update. - */ - public final void updatePin(final String oldUrl, final EntryLink entry) { - if (pinboard != null) { - PinboardUtils.updatePin(pinboard, ircServer, oldUrl, entry); - } - } -} diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt new file mode 100644 index 0000000..a93f2ae --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -0,0 +1,776 @@ +/* + * Mobibot.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.PinboardUtils.addPin +import net.thauvin.erik.mobibot.PinboardUtils.deletePin +import net.thauvin.erik.mobibot.PinboardUtils.updatePin +import net.thauvin.erik.mobibot.Utils.Companion.colorize +import net.thauvin.erik.mobibot.Utils.Companion.ensureDir +import net.thauvin.erik.mobibot.Utils.Companion.getIntProperty +import net.thauvin.erik.mobibot.Utils.Companion.helpFormat +import net.thauvin.erik.mobibot.Utils.Companion.helpIndent +import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate +import net.thauvin.erik.mobibot.Utils.Companion.today +import net.thauvin.erik.mobibot.commands.AddLog +import net.thauvin.erik.mobibot.commands.ChannelFeed +import net.thauvin.erik.mobibot.commands.Cycle +import net.thauvin.erik.mobibot.commands.Ignore +import net.thauvin.erik.mobibot.commands.Info +import net.thauvin.erik.mobibot.commands.Me +import net.thauvin.erik.mobibot.commands.Modules +import net.thauvin.erik.mobibot.commands.Msg +import net.thauvin.erik.mobibot.commands.Nick +import net.thauvin.erik.mobibot.commands.Recap +import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap +import net.thauvin.erik.mobibot.commands.Say +import net.thauvin.erik.mobibot.commands.Users +import net.thauvin.erik.mobibot.commands.Versions +import net.thauvin.erik.mobibot.commands.links.Comment +import net.thauvin.erik.mobibot.commands.links.LinksMgr +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.saveEntries +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startDate +import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startup +import net.thauvin.erik.mobibot.commands.links.Posting +import net.thauvin.erik.mobibot.commands.links.Tags +import net.thauvin.erik.mobibot.commands.links.View +import net.thauvin.erik.mobibot.commands.tell.Tell +import net.thauvin.erik.mobibot.entries.EntriesMgr +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.modules.Calc +import net.thauvin.erik.mobibot.modules.CurrencyConverter +import net.thauvin.erik.mobibot.modules.Dice +import net.thauvin.erik.mobibot.modules.GoogleSearch +import net.thauvin.erik.mobibot.modules.Joke +import net.thauvin.erik.mobibot.modules.Lookup +import net.thauvin.erik.mobibot.modules.Ping +import net.thauvin.erik.mobibot.modules.RockPaperScissors +import net.thauvin.erik.mobibot.modules.StockQuote +import net.thauvin.erik.mobibot.modules.Twitter +import net.thauvin.erik.mobibot.modules.War +import net.thauvin.erik.mobibot.modules.Weather2 +import net.thauvin.erik.mobibot.modules.WorldTime +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.pinboard.PinboardPoster +import net.thauvin.erik.semver.Version +import org.apache.commons.cli.CommandLine +import org.apache.commons.cli.CommandLineParser +import org.apache.commons.cli.DefaultParser +import org.apache.commons.cli.HelpFormatter +import org.apache.commons.cli.Option +import org.apache.commons.cli.Options +import org.apache.commons.cli.ParseException +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Logger +import org.apache.logging.log4j.core.config.Configurator +import org.jibble.pircbot.PircBot +import java.io.BufferedOutputStream +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.io.PrintStream +import java.lang.String.join +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* +import java.util.logging.ConsoleHandler +import java.util.regex.Pattern +import kotlin.system.exitProcess + +/** + * Implements the #mobitopia bot. + */ +@Version(properties = "version.properties", className = "ReleaseInfo", template = "ReleaseInfo.mustache", type = "kt") +class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : PircBot() { + // Commands and Modules + private val addons = Addons() + + /** Main channel. */ + val channel: String + + // IRC port + private val ircPort: Int + + /** IRC server. */ + val ircServer: String + + /** Logger. */ + val logger: Logger = LogManager.getLogger(Mobibot::class.java) + + // Logger default level + private val loggerLevel: Level + + /** Log directory. */ + val logsDir: String + + // Pinboard posts handler + private val pinboard: PinboardPoster = PinboardPoster() + + /** Tell command. */ + val tell: Tell + + /** Today's date. */ + val today = today() + + /** Twitter module. */ + val twitter: Twitter + + /** The backlogs URL. */ + var backlogsUrl = "" + + // Ident message + private var identMsg = "" + + // Ident nick + private var identNick = "" + + // NickServ ident password + private var identPwd = "" + + // Is pinboard enabled? + private var isPinboardEnabled = false + + /** Timer. */ + val timer = Timer(true) + + /** Weblog URL */ + var weblogUrl = "" + + /** The current channel name. */ + private val channelName: String + get() = channel.substring(1) + + /** The enabled modules names. */ + val modulesNames: List<String> + get() = addons.modulesNames + + /** + * Sends an action to the current channel. + */ + fun action(action: String) { + action(channel, action) + } + + /** + * Sends an action to the channel. + */ + private fun action(channel: String, action: String) { + if (channel.isNotBlank() && action.isNotBlank()) { + sendAction(channel, action) + } + } + + /** + * Adds pin on pinboard. + */ + fun addPin(entry: EntryLink) { + if (isPinboardEnabled) { + addPin(pinboard, ircServer, entry) + } + } + + /** + * Connects to the server and joins the channel. + */ + fun connect() { + try { + connect(ircServer, ircPort) + } catch (e: Exception) { + var retries = 0 + while (retries++ < MAX_RECONNECT && !isConnected) { + sleep(10) + try { + connect(ircServer, ircPort) + } catch (ex: Exception) { + if (retries == MAX_RECONNECT) { + logger.debug("Unable to reconnect to $ircServer, after $MAX_RECONNECT retries.", ex) + e.printStackTrace(System.err) + exitProcess(1) + } + } + } + } + identify() + joinChannel() + } + + /** + * Deletes pin on pinboard. + */ + fun deletePin(index: Int, entry: EntryLink) { + if (isPinboardEnabled) { + deletePin(pinboard, entry) + } + if (twitter.isAutoPost) { + twitter.removeEntry(index) + } + } + + /** + * Responds with the commands help, if any. + */ + private fun helpCommands(sender: String, topic: String, isPrivate: Boolean): Boolean { + for (command in addons.commands) { + if (command.isVisible && command.name.startsWith(topic)) { + return command.helpResponse(topic, sender, isOp(sender), isPrivate) + } + } + return false + } + + /** + * Responds with the default help. + */ + fun helpDefault(sender: String, isOp: Boolean, isPrivate: Boolean) { + send(sender, "Type a URL on $channel to post it.", isPrivate) + send(sender, "For more information on a specific command, type:", isPrivate) + send( + sender, + helpIndent(helpFormat("""%c ${Constants.HELP_CMD} <command>""", nick, isPrivate)), + isPrivate + ) + send(sender, "The commands are:", isPrivate) + sendList(sender, addons.names, 8, isPrivate, true) + if (isOp) { + send(sender, "The op commands are:", isPrivate) + sendList(sender, addons.ops, 8, isPrivate, true) + } + } + + /** + * Responds with the modules help, if any. + */ + private fun helpModules(sender: String, topic: String, isPrivate: Boolean): Boolean { + for (module in addons.modules) { + for (cmd in module.commands) { + if (topic == cmd) { + module.helpResponse(sender, isPrivate) + return true + } + } + } + return false + } + + /** + * Responds with the default, commands or modules help. + */ + private fun helpResponse(sender: String, topic: String, isPrivate: Boolean) { + val isOp = isOp(sender) + if (topic.isBlank()) { + helpDefault(sender, isOp, isPrivate) + } else { + // Command, Modules or Default + if (!helpCommands(sender, topic, isPrivate) && !helpModules( + sender, + topic.toLowerCase().trim(), + isPrivate + ) + ) { + helpDefault(sender, isOp, isPrivate) + } + } + } + + /** + * Identifies the bot. + */ + private fun identify() { + // Identify with NickServ + if (identPwd.isNotBlank()) { + identify(identPwd) + } + // Identify with a specified nick + if (identNick.isNotBlank() && identMsg.isNotBlank()) { + sendMessage(identNick, identMsg) + } + } + + /** + * Returns {@code true} if the specified sender is an Op on the [channel][.ircChannel]. + */ + fun isOp(sender: String): Boolean { + for (user in getUsers(channel)) { + if (user.nick == sender) { + return user.isOp + } + } + return false + } + + /** + * Joins the bot's channel. + */ + private fun joinChannel() { + joinChannel(channel) + twitter.notification("$name ${ReleaseInfo.VERSION} has joined $channel") + } + + override fun onDisconnect() { + if (weblogUrl.isNotBlank()) { + version = weblogUrl + } + sleep(5) + connect() + } + + override fun onMessage( + channel: String, + sender: String, + login: String, + hostname: String, + message: String + ) { + logger.debug(">>> $sender: $message") + tell.send(sender, true) + if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command> + val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2).toTypedArray() + val cmd = cmds[0].toLowerCase() + val args = if (cmds.size > 1) { + cmds[1].trim() + } else "" + if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help + helpResponse(sender, args, false) + } else { + // Commands + for (command in addons.commands) { + if (command.isPublic && command.name.startsWith(cmd)) { + command.commandResponse(sender, login, args, isOp(sender), false) + return + } + } + // Modules + for (module in addons.modules) { // modules + for (c in module.commands) { + if (cmd.startsWith(c)) { + module.commandResponse(sender, cmd, args, false) + return + } + } + } + } + } else { + // Commands, e.g.: https://www.example.com/ + for (command in addons.commands) { + if (command.matches(message)) { + command.commandResponse(sender, login, message, isOp(sender), false) + return + } + } + } + storeRecap(sender, message, false) + } + + override fun onPrivateMessage( + sender: String, + login: String, + hostname: String, + message: String + ) { + if (logger.isDebugEnabled) { + logger.debug(">>> $sender : $message") + } + val cmds = message.split(" ".toRegex(), 2).toTypedArray() + val cmd = cmds[0].toLowerCase() + val args = if (cmds.size > 1) { + cmds[1].trim() + } else "" + val isOp = isOp(sender) + if (cmd.startsWith(Constants.HELP_CMD)) { // help + helpResponse(sender, args, true) + } else if (isOp && "kill" == cmd) { // kill + twitter.notification("$name killed by $sender on $channel") + sendRawLine("QUIT : Poof!") + exitProcess(0) + } else if (isOp && Constants.DEBUG_CMD == cmd) { // debug + if (logger.isDebugEnabled) { + Configurator.setLevel(logger.name, loggerLevel) + } else { + Configurator.setLevel(logger.name, Level.DEBUG) + } + send(sender, "Debug logging is " + if (logger.isDebugEnabled) "enabled." else "disabled.", true) + } else if (isOp && Constants.DIE_CMD == cmd) { // die + send("$sender has just signed my death sentence.") + timer.cancel() + twitter.shutdown() + twitter.notification("$name stopped by $sender on $channel") + sleep(3) + quitServer("The Bot Is Out There!") + exitProcess(0) + } else { + for (command in addons.commands) { + if (command.name.startsWith(cmd)) { + command.commandResponse(sender, login, args, isOp, true) + return + } + } + for (module in addons.modules) { + if (module.isPrivateMsgEnabled) { + for (c in module.commands) { + if (cmd == c) { + module.commandResponse(sender, cmd, args, true) + return + } + } + } + } + helpDefault(sender, isOp, true) + } + } + + override fun onAction(sender: String, login: String, hostname: String, target: String, action: String) { + if (channel == target) { + storeRecap(sender, action, true) + } + } + + override fun onJoin(channel: String, sender: String, login: String, hostname: String) { + tell.send(sender) + } + + override fun onNickChange(oldNick: String, login: String, hostname: String, newNick: String) { + tell.send(newNick) + } + + /** + * Sends a private message or notice. + */ + fun send(sender: String, message: String?, isPrivate: Boolean) { + if (message != null && sender.isNotBlank()) { + if (isPrivate) { + logger.debug("Sending message to $sender : $message") + sendMessage(sender, message) + } else { + logger.debug("Sending notice to $sender: $message") + sendNotice(sender, message) + } + } + } + + /** + * Sends a notice to the channel. + */ + fun send(notice: String?) { + if (notice != null) send(channel, notice, false) + } + + /** + * Sends a message. + */ + fun send(who: String, message: Message) { + send(if (message.isNotice) who else channel, message.msg, message.color, message.isPrivate) + } + + /** + * Sends a message. + */ + fun send(who: String, message: String, color: String, isPrivate: Boolean) { + send(who, colorize(message, color), isPrivate) + } + + /** + * Send a formatted commands/modules, etc. list. + */ + fun sendList( + nick: String, + list: List<String>, + size: Int, + isPrivate: Boolean, + isBold: Boolean + ) { + var i = 0 + while (i < list.size) { + send( + nick, + helpIndent(join(" ", list.subList(i, list.size.coerceAtMost(i + size))), isBold), + isPrivate + ) + i += size + } + } + + /** + * Sets the bot's identification. + */ + private fun setIdentity(pwd: String, nick: String, msg: String) { + identPwd = pwd + identNick = nick + identMsg = msg + } + + /** + * Sets the pinboard authentication. + */ + private fun setPinboardAuth(apiToken: String) { + if (apiToken.isNotBlank()) { + pinboard.apiToken = apiToken + isPinboardEnabled = true + if (logger.isDebugEnabled) { + val consoleHandler = ConsoleHandler() + consoleHandler.level = java.util.logging.Level.FINE + pinboard.logger.addHandler(consoleHandler) + pinboard.logger.level = java.util.logging.Level.FINE + } + } + } + + /** + * Sleeps for the specified number of seconds. + */ + fun sleep(secs: Int) { + try { + Thread.sleep(secs * 1000L) + } catch (ignore: InterruptedException) { + // Do nothing + } + } + + /** + * Updates pin on pinboard. + */ + fun updatePin(oldUrl: String, entry: EntryLink) { + if (isPinboardEnabled) { + updatePin(pinboard, ircServer, oldUrl, entry) + } + } + + companion object { + // Maximum number of times the bot will try to reconnect, if disconnected + private const val MAX_RECONNECT = 10 + + /** + * The Truth is Out There! + */ + @JvmStatic + fun main(args: Array<String>) { + // Setup the command line options + val options = Options() + .addOption( + Constants.HELP_ARG.substring(0, 1), + Constants.HELP_ARG, + false, + "print this help message" + ) + .addOption( + Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG, false, + "print debug & logging data directly to the console" + ) + .addOption( + Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg() + .argName("file") + .desc("use " + "alternate properties file") + .longOpt(Constants.PROPS_ARG).build() + ) + .addOption( + Constants.VERSION_ARG.substring(0, 1), + Constants.VERSION_ARG, + false, + "print version info" + ) + + // Parse the command line + val parser: CommandLineParser = DefaultParser() + val commandLine: CommandLine + try { + commandLine = parser.parse(options, args) + } catch (e: ParseException) { + System.err.println("CLI Parsing failed. Reason: ${e.message}") + e.printStackTrace(System.err) + exitProcess(1) + } + when { + commandLine.hasOption(Constants.HELP_ARG[0]) -> { + // Output the usage + HelpFormatter().printHelp(Mobibot::class.java.name, options) + } + commandLine.hasOption(Constants.VERSION_ARG[0]) -> { + println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${isoLocalDate(ReleaseInfo.BUILDDATE)})") + println(ReleaseInfo.WEBSITE) + } + else -> { + val p = Properties() + try { + Files.newInputStream( + Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties")) + ).use { fis -> + // Load the properties files + p.load(fis) + } + } catch (e: FileNotFoundException) { + System.err.println("Unable to find properties file.") + e.printStackTrace(System.err) + exitProcess(1) + } catch (e: IOException) { + System.err.println("Unable to open properties file.") + e.printStackTrace(System.err) + exitProcess(1) + } + val nickname = p.getProperty("nick", Mobibot::class.java.name.toLowerCase()) + val channel = p.getProperty("channel") + val logsDir = ensureDir(p.getProperty("logs", "."), false) + + // Redirect the stdout and stderr + if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { + try { + val stdout = PrintStream( + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + today() + ".log", true + ) + ), true + ) + System.setOut(stdout) + } catch (e: IOException) { + System.err.println("Unable to open output (stdout) log file.") + e.printStackTrace(System.err) + exitProcess(1) + } + try { + val stderr = PrintStream( + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true + ) + System.setErr(stderr) + } catch (e: IOException) { + System.err.println("Unable to open error (stderr) log file.") + e.printStackTrace(System.err) + exitProcess(1) + } + } + + // Create the bot + val bot = Mobibot(nickname, channel, logsDir, p) + + // Connect + bot.connect() + } + } + } + } + + /** + * Initialize the bot. + */ + init { + System.getProperties().setProperty( + "sun.net.client.defaultConnectTimeout", + java.lang.String.valueOf(Constants.CONNECT_TIMEOUT) + ) + System.getProperties().setProperty( + "sun.net.client.defaultReadTimeout", + java.lang.String.valueOf(Constants.CONNECT_TIMEOUT) + ) + name = nickname + ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) + ircPort = getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT) + this.channel = channel + logsDir = logsDirPath + + // Set the logger level + loggerLevel = logger.level + + // Load the current entries and backlogs, if any + try { + startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) + logger.debug("Last feed: $startDate") + } catch (e: Exception) { + logger.error("An error occurred while loading the logs.", e) + } + + // Initialize the bot + setVerbose(true) + setAutoNickChange(true) + login = p.getProperty("login", name) + version = ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION + // setMessageDelay(1000); + setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", "")) + + // Set the URLs + weblogUrl = p.getProperty("weblog", "") + backlogsUrl = ensureDir(p.getProperty("backlogs", weblogUrl), true) + + // Set the pinboard authentication + setPinboardAuth(p.getProperty("pinboard-api-token")) + + // Load the commands + addons.add(AddLog(this), p) + addons.add(ChannelFeed(this, channelName), p) + addons.add(Cycle(this), p) + addons.add(Ignore(this), p) + addons.add(Info(this), p) + addons.add(Me(this), p) + addons.add(Modules(this), p) + addons.add(Msg(this), p) + addons.add(Nick(this), p) + addons.add(Recap(this), p) + addons.add(Say(this), p) + addons.add(Users(this), p) + addons.add(Versions(this), p) + + // Tell command + tell = Tell(this) + addons.add(tell, p) + + // Load the links commands + addons.add(Comment(this), p) + addons.add(Posting(this), p) + addons.add(Tags(this), p) + addons.add(LinksMgr(this), p) + addons.add(View(this), p) + + // Load the modules + addons.add(Calc(this), p) + addons.add(CurrencyConverter(this), p) + addons.add(Dice(this), p) + addons.add(GoogleSearch(this), p) + addons.add(Joke(this), p) + addons.add(Lookup(this), p) + addons.add(Ping(this), p) + addons.add(RockPaperScissors(this), p) + addons.add(StockQuote(this), p) + addons.add(War(this), p) + addons.add(Weather2(this), p) + addons.add(WorldTime(this), p) + + // Twitter module + twitter = Twitter(this) + addons.add(twitter, p) + + // Sort the addons + addons.sort() + + // Save the entries + saveEntries(this, true) + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index fcdc20e..474108f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -134,7 +134,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { } private fun removeEntry(sender: String, login: String, isOp: Boolean, index: Int) { - val entry: EntryLink = LinksMgr.entries[index] + val entry: EntryLink = entries[index] if (entry.login == login || isOp) { bot.deletePin(index, entry) entries.removeAt(index) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 0b485eb..280d9aa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -41,7 +41,6 @@ import net.thauvin.erik.mobibot.Utils.Companion.reverseColor import net.thauvin.erik.mobibot.Utils.Companion.utcDateTime import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.View -import org.apache.commons.lang3.StringUtils import java.util.concurrent.CopyOnWriteArrayList /** @@ -143,7 +142,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { isPrivate: Boolean ) { if (isEnabled()) { - if (StringUtils.isBlank(args)) { + if (args.isBlank()) { helpResponse(args, sender, isOp, isPrivate) } else if (args.startsWith(View.VIEW_CMD)) { if (bot.isOp(sender) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { @@ -178,12 +177,13 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { // New message. private fun newMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) { val split = args.split(" ".toRegex(), 2).toTypedArray() - if (split.size == 2 && StringUtils.isNotBlank(split[1]) && split[1].contains(" ")) { + if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) { if (messages.size < maxSize) { val message = TellMessage(sender, split[0], split[1].trim()) messages.add(message) save() - bot.send(sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate + bot.send( + sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate ) } else { bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 4dd3f40..09dc5da 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -74,12 +74,12 @@ class TellMessage internal constructor( * Returns {@code true) if the message was received. */ var isReceived = false - set(value) { - if (value) { - receptionDate = LocalDateTime.now(Clock.systemUTC()) - } - field = value - } + set(value) { + if (value) { + receptionDate = LocalDateTime.now(Clock.systemUTC()) + } + field = value + } /** * Return the message creating date. diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index 44f487d..3f8710a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -32,18 +32,17 @@ package net.thauvin.erik.mobibot.commands.tell import org.apache.logging.log4j.Logger -import java.time.LocalDateTime -import java.io.ObjectInputStream import java.io.BufferedInputStream +import java.io.BufferedOutputStream import java.io.FileNotFoundException import java.io.IOException -import java.lang.ClassNotFoundException -import java.io.BufferedOutputStream +import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.nio.file.Files import java.nio.file.Paths import java.time.Clock -import java.util.ArrayList +import java.time.LocalDateTime +import java.util.* /** * The Tell Messages Manager. diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index a526542..f8e9a43 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -41,7 +41,6 @@ import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.SyndFeedOutput import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate -import org.apache.commons.lang3.StringUtils import java.io.IOException import java.io.InputStreamReader import java.io.OutputStreamWriter @@ -143,7 +142,7 @@ class EntriesMgr private constructor() { if (bot.logger.isDebugEnabled) { bot.logger.debug("Saving the feeds...") } - if (StringUtils.isNotBlank(bot.logsDir) && StringUtils.isNotBlank(bot.weblogUrl)) { + if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { try { val output = SyndFeedOutput() var rss: SyndFeed = SyndFeedImpl() @@ -208,7 +207,7 @@ class EntriesMgr private constructor() { ), StandardCharsets.UTF_8 ).use { fw -> output.output(rss, fw) } if (isDayBackup) { - if (StringUtils.isNotBlank(bot.backlogsUrl)) { + if (bot.backlogsUrl.isNotBlank()) { if (!history.contains(bot.today)) { history.add(bot.today) while (history.size > MAX_BACKLOGS) { diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt index 9601d17..c041249 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -53,7 +53,7 @@ class EntryLink : Serializable { var channel: String // Creation date - var date = Calendar.getInstance().time + var date: Date = Calendar.getInstance().time // Link's URL var link: String @@ -178,12 +178,12 @@ class EntryLink : Serializable { /** * Sets the tags. */ - fun setTags(tags: List<String?>) { - if (!tags.isEmpty()) { + private fun setTags(tags: List<String?>) { + if (tags.isNotEmpty()) { var category: SyndCategoryImpl for (tag in tags) { - if (StringUtils.isNoneBlank(tag)) { - val t = StringUtils.lowerCase(tag) + if (!tag.isNullOrBlank()) { + val t = tag.toLowerCase() val mod = t[0] if (mod == '-') { // Don't remove the channel tag diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt index dd40a8c..f4f36a7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import org.apache.commons.lang3.StringUtils import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -113,7 +112,7 @@ abstract class AbstractModule(val bot: Mobibot) { open val isValidProperties: Boolean get() { for (s in propertyKeys) { - if (StringUtils.isBlank(properties[s])) { + if (properties[s].isNullOrBlank()) { return false } } @@ -124,7 +123,7 @@ abstract class AbstractModule(val bot: Mobibot) { * Sets a property key and value. */ fun setProperty(key: String, value: String) { - if (StringUtils.isNotBlank(key)) { + if (key.isNotBlank()) { properties[key] = value } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt index 22a6c7e..b1ea6a2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt @@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.modules import net.objecthunter.exp4j.ExpressionBuilder import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import org.apache.commons.lang3.StringUtils import java.text.DecimalFormat /** @@ -47,7 +46,7 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { args: String, isPrivate: Boolean ) { - if (StringUtils.isNotBlank(args)) { + if (args.isNotBlank()) { bot.send(calc(args)) } else { helpResponse(sender, isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index c72e73d..487981c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -157,8 +157,8 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { if (cmds[3] == cmds[1] || "0" == cmds[0]) { PublicMessage("You're kidding, right?") } else { - val to = StringUtils.upperCase(cmds[1]) - val from = StringUtils.upperCase(cmds[3]) + val to = cmds[1].toUpperCase() + val from = cmds[3].toUpperCase() if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) { try { val amt = cmds[0].replace(",", "").toDouble() @@ -166,10 +166,10 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { val doubleTo = EXCHANGE_RATES[from]!!.toDouble() PublicMessage( NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1) - + " ${StringUtils.upperCase(cmds[1])} = " + + " ${cmds[1].toUpperCase()} = " + NumberFormat.getCurrencyInstance(Constants.LOCALE) .format(amt * doubleTo / doubleFrom).substring(1) - + " ${StringUtils.upperCase(cmds[3])}" + + " ${cmds[3].toUpperCase()}" ) } catch (e: NumberFormatException) { ErrorMessage("Let's try with some real numbers next time, okay?") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 891a6f2..b9c78d0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -52,7 +52,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { with(bot) { - if (StringUtils.isNotBlank(args)) { + if (args.isNotBlank()) { try { val results = searchGoogle( args, properties[GOOGLE_API_KEY_PROP], diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt index 722ab75..26383e2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt @@ -56,11 +56,13 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { /** * Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/). */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) = try { - bot.send(Utils.cyan(randomJoke().msg)) - } catch (e: ModuleException) { - bot.logger.warn(e.debugMessage, e) - bot.send(sender, e.message, isPrivate) + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + try { + bot.send(Utils.cyan(randomJoke().msg)) + } catch (e: ModuleException) { + bot.logger.warn(e.debugMessage, e) + bot.send(sender, e.message, isPrivate) + } } companion object { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index 434ca54..702077a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -37,7 +37,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage import net.thauvin.erik.mobibot.msg.PublicMessage -import org.apache.commons.lang3.StringUtils import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -53,7 +52,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { with(bot) { - if (StringUtils.isNotBlank(args)) { + if (args.isNotBlank()) { try { val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) for (msg in messages) { @@ -88,7 +87,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { @Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject { - return if (StringUtils.isNotBlank(response)) { + return if (response.isNotBlank()) { val json = JSONObject(response) try { val info = json.getString("Information") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index 392cf85..ef648c8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -39,7 +39,6 @@ import net.thauvin.erik.mobibot.commands.links.LinksMgr import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage -import org.apache.commons.lang3.StringUtils import twitter4j.TwitterException import twitter4j.TwitterFactory import twitter4j.conf.ConfigurationBuilder @@ -88,7 +87,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { */ fun notification(msg: String) { with(bot) { - if (isEnabled && StringUtils.isNotBlank(handle)) { + if (isEnabled && !handle.isNullOrBlank()) { Thread { try { post(message = msg, isDm = true) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index 9fc9847..023a4d9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -41,7 +41,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage import net.thauvin.erik.mobibot.msg.PublicMessage -import org.apache.commons.lang3.StringUtils import org.jibble.pircbot.Colors import java.util.* import kotlin.math.roundToInt @@ -54,7 +53,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { * Fetches the weather data from a specific city. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - if (StringUtils.isNotBlank(args)) { + if (args.isNotBlank()) { try { val messages = getWeather(args, properties[OWM_API_KEY_PROP]) if (messages[0].isError) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index cafe903..835fcca 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -190,7 +190,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { with(bot) { if (args.isEmpty()) { send(sender, "The supported countries/zones are: ", isPrivate) - sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, false, false) + sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false, isBold = false) } else { val msg = worldTime(args) if (isPrivate) { diff --git a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 5dbf7f3..75a1139 100644 --- a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -31,11 +31,11 @@ */ package net.thauvin.erik.mobibot.commands.tell -import java.time.temporal.Temporal -import java.time.LocalDateTime import org.assertj.core.api.Assertions import org.testng.annotations.Test import java.time.Duration +import java.time.LocalDateTime +import java.time.temporal.Temporal /** * The `TellMessageTest` class. diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 019538f..8592447 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -62,7 +62,7 @@ class GoogleSearchTest : LocalProperties() { .`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI") && !apiKey.isNullOrBlank() && !cseKey.isNullOrBlank()) { + if ("true" == System.getenv("CI") && !apiKey.isBlank() && !cseKey.isBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey)) } else { throw e diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index ea78c34..c47509b 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -63,7 +63,7 @@ class StockQuoteTest : LocalProperties() { .isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI") && !apiKey.isNullOrBlank()) { + if ("true" == System.getenv("CI") && !apiKey.isBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey)) } else { throw e diff --git a/version.properties b/version.properties index 25393d2..0f713d3 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Dec 04 15:43:01 PST 2020 -version.buildmeta=312 +#Fri Dec 04 22:59:43 PST 2020 +version.buildmeta=337 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+312 +version.semver=0.8.0-beta+337 From 5a669601da3257af752b784c0ed91477eaad28e3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 4 Dec 2020 23:39:58 -0800 Subject: [PATCH 449/842] Replace java string functions with kotlin's. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.kt | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index a93f2ae..0b633fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -99,7 +99,6 @@ import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.IOException import java.io.PrintStream -import java.lang.String.join import java.nio.file.Files import java.nio.file.Paths import java.util.* @@ -512,7 +511,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert while (i < list.size) { send( nick, - helpIndent(join(" ", list.subList(i, list.size.coerceAtMost(i + size))), isBold), + helpIndent(list.subList(i, list.size.coerceAtMost(i + size)).joinToString(" ", truncated = ""), isBold), isPrivate ) i += size @@ -588,7 +587,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert .addOption( Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg() .argName("file") - .desc("use " + "alternate properties file") + .desc("use alternate properties file") .longOpt(Constants.PROPS_ARG).build() ) .addOption( @@ -683,14 +682,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert * Initialize the bot. */ init { - System.getProperties().setProperty( - "sun.net.client.defaultConnectTimeout", - java.lang.String.valueOf(Constants.CONNECT_TIMEOUT) - ) - System.getProperties().setProperty( - "sun.net.client.defaultReadTimeout", - java.lang.String.valueOf(Constants.CONNECT_TIMEOUT) - ) + System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", Constants.CONNECT_TIMEOUT.toString()) + System.getProperties().setProperty("sun.net.client.defaultReadTimeout", Constants.CONNECT_TIMEOUT.toString()) name = nickname ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) ircPort = getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT) From ece6ae98a0a082d83ebf7a021219a6648ef5f6c1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 5 Dec 2020 14:45:43 -0800 Subject: [PATCH 450/842] Converted ArrayList to List (tokenizer, etc.) whenever possible. --- .../java/net/thauvin/erik/mobibot/Mobibot.kt | 32 +++++++++---------- .../erik/mobibot/commands/tell/Tell.kt | 4 +-- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 14 +++----- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 2 +- 7 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index 0b633fa..6d37136 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -255,7 +255,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert send(sender, "For more information on a specific command, type:", isPrivate) send( sender, - helpIndent(helpFormat("""%c ${Constants.HELP_CMD} <command>""", nick, isPrivate)), + helpIndent(helpFormat("%c ${Constants.HELP_CMD} <command>", nick, isPrivate)), isPrivate ) send(sender, "The commands are:", isPrivate) @@ -353,7 +353,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert logger.debug(">>> $sender: $message") tell.send(sender, true) if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command> - val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2).toTypedArray() + val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) val cmd = cmds[0].toLowerCase() val args = if (cmds.size > 1) { cmds[1].trim() @@ -399,7 +399,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert if (logger.isDebugEnabled) { logger.debug(">>> $sender : $message") } - val cmds = message.split(" ".toRegex(), 2).toTypedArray() + val cmds = message.split(" ".toRegex(), 2) val cmd = cmds[0].toLowerCase() val args = if (cmds.size > 1) { cmds[1].trim() @@ -503,7 +503,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert fun sendList( nick: String, list: List<String>, - size: Int, + maxPerLine: Int, isPrivate: Boolean, isBold: Boolean ) { @@ -511,22 +511,16 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert while (i < list.size) { send( nick, - helpIndent(list.subList(i, list.size.coerceAtMost(i + size)).joinToString(" ", truncated = ""), isBold), + helpIndent( + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(" ", truncated = ""), + isBold + ), isPrivate ) - i += size + i += maxPerLine } } - /** - * Sets the bot's identification. - */ - private fun setIdentity(pwd: String, nick: String, msg: String) { - identPwd = pwd - identNick = nick - identMsg = msg - } - /** * Sets the pinboard authentication. */ @@ -690,7 +684,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert this.channel = channel logsDir = logsDirPath - // Set the logger level + // Set the default logger level loggerLevel = logger.level // Load the current entries and backlogs, if any @@ -707,7 +701,11 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert login = p.getProperty("login", name) version = ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION // setMessageDelay(1000); - setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", "")) + + // Set NICKSERV identification + identPwd = p.getProperty("ident", "") + identNick = p.getProperty("ident-nick", "") + identMsg = p.getProperty("ident-msg", "") // Set the URLs weblogUrl = p.getProperty("weblog", "") diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 280d9aa..c0e97d0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -71,7 +71,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { // Delete message. private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) { - val split = args.split(" ").toTypedArray() + val split = args.split(" ") if (split.size == 2) { val id = split[1] var deleted = false @@ -176,7 +176,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { // New message. private fun newMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) { - val split = args.split(" ".toRegex(), 2).toTypedArray() + val split = args.split(" ".toRegex(), 2) if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) { if (messages.size < maxSize) { val message = TellMessage(sender, split[0], split[1].trim()) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt index c041249..5d6eea2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -172,7 +172,7 @@ class EntryLink : Serializable { * Sets the tags. */ fun setTags(tags: String) { - setTags(tags.split(LinksMgr.TAG_MATCH).toTypedArray().toList()) + setTags(tags.split(LinksMgr.TAG_MATCH)) } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 487981c..f15c184 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -152,7 +152,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { */ @JvmStatic fun convertCurrency(query: String): Message { - val cmds = query.split(" ").toTypedArray() + val cmds = query.split(" ") return if (cmds.size == 4) { if (cmds[3] == cmds[1] || "0" == cmds[0]) { PublicMessage("You're kidding, right?") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt index c46baf0..91954b6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -126,13 +126,9 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { /** * Performs a whois IP query. - * - * @param query The IP address. - * @return The IP whois data, if any. - * @throws java.io.IOException If a connection error occurs. */ @Throws(IOException::class) - private fun whois(query: String): Array<String> { + private fun whois(query: String): List<String> { return whois(query, WHOIS_HOST) } @@ -141,9 +137,9 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { */ @JvmStatic @Throws(IOException::class) - fun whois(query: String, host: String): Array<String> { + fun whois(query: String, host: String): List<String> { val whoisClient = WhoisClient() - val lines: Array<String> + val lines: List<String> with(whoisClient) { try { defaultTimeout = Constants.CONNECT_TIMEOUT @@ -151,9 +147,9 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { soTimeout = Constants.CONNECT_TIMEOUT setSoLinger(false, 0) lines = if (WHOIS_HOST == host) { - query("n - $query").split("\n").toTypedArray() + query("n - $query").split("\n") } else { - query(query).split("\n").toTypedArray() + query(query).split("\n") } } finally { disconnect() diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index 023a4d9..5595519 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -107,7 +107,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { val messages = ArrayList<Message>() owm.unit = OWM.Unit.IMPERIAL if (query.isNotBlank()) { - val argv = query.split(",").toTypedArray() + val argv = query.split(",") if (argv.size in 1..2) { val city = argv[0].trim() val country: String = if (argv.size > 1 && argv[1].isNotBlank()) { diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt index 303a377..aad40e5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -52,7 +52,7 @@ class LookupTest { @Throws(Exception::class) fun testWhois() { val result = whois("17.178.96.59", Lookup.WHOIS_HOST) - Assertions.assertThat(Arrays.stream(result).anyMatch { m: String -> m.contains("Apple Inc.") }) + Assertions.assertThat(result.stream().anyMatch { m: String -> m.contains("Apple Inc.") }) .`as`("whois(17.178.96.59/Apple Inc.").isTrue } } From 5a831f2c7c6418eb83d0b8fed3a66dff29ed2b5a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 6 Dec 2020 01:49:02 -0800 Subject: [PATCH 451/842] Switched to MutableList vs ArrayList whenever possible. Made sure logging level is enabled before logging. --- config/detekt/baseline.xml | 4 +- .../java/net/thauvin/erik/mobibot/Addons.kt | 11 +++--- .../net/thauvin/erik/mobibot/FeedReader.kt | 4 +- .../java/net/thauvin/erik/mobibot/Mobibot.kt | 38 +++++++++---------- .../thauvin/erik/mobibot/commands/Recap.kt | 3 +- .../thauvin/erik/mobibot/commands/Users.kt | 3 +- .../erik/mobibot/commands/links/LinksMgr.kt | 10 ++--- .../erik/mobibot/commands/tell/Tell.kt | 7 +--- .../mobibot/commands/tell/TellMessagesMgr.kt | 10 ++--- .../erik/mobibot/entries/EntriesMgr.kt | 27 ++++++------- .../thauvin/erik/mobibot/entries/EntryLink.kt | 5 +-- .../erik/mobibot/modules/AbstractModule.kt | 8 ++-- .../erik/mobibot/modules/CurrencyConverter.kt | 9 ++--- .../erik/mobibot/modules/GoogleSearch.kt | 5 +-- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 4 +- .../erik/mobibot/modules/StockQuote.kt | 5 +-- .../thauvin/erik/mobibot/modules/Twitter.kt | 10 ++--- .../thauvin/erik/mobibot/modules/Weather2.kt | 5 +-- .../thauvin/erik/mobibot/modules/WorldTime.kt | 3 +- 20 files changed, 75 insertions(+), 98 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 46ddfa2..7d60806 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -18,7 +18,6 @@ <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$33</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID> <ID>MagicNumber:Cycle.kt$Cycle$10</ID> @@ -54,8 +53,9 @@ <ID>MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): Message</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> - <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: ArrayList<EntryLink>): String</ID> + <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> <ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 64b7487..1974aa4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -34,17 +34,16 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.modules.AbstractModule import java.util.* -import kotlin.collections.ArrayList /** * Modules and Commands addons. */ class Addons { - val commands: MutableList<AbstractCommand> = ArrayList() - val modules: MutableList<AbstractModule> = ArrayList() - val modulesNames: MutableList<String> = ArrayList() - val names: MutableList<String> = ArrayList() - val ops: MutableList<String> = ArrayList() + val commands: MutableList<AbstractCommand> = mutableListOf() + val modules: MutableList<AbstractModule> = mutableListOf() + val modulesNames: MutableList<String> = mutableListOf() + val names: MutableList<String> = mutableListOf() + val ops: MutableList<String> = mutableListOf() /** * Add a module with properties. diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt index 0dcdbac..a3f6247 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt @@ -69,10 +69,10 @@ class FeedReader( } } } catch (e: MalformedURLException) { - logger.debug("Invalid feed URL.", e) + if (logger.isDebugEnabled) logger.debug("Invalid feed URL.", e) send(sender, "The feed URL is invalid.", false) } catch (e: Exception) { - logger.debug("Unable to fetch the feed.", e) + if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed.", e) send(sender, "An error has occurred while fetching the feed: ${e.message}", false) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index 6d37136..dcdd5a2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -136,25 +136,25 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert private val pinboard: PinboardPoster = PinboardPoster() /** Tell command. */ - val tell: Tell + val tell: Tell = Tell(this) /** Today's date. */ val today = today() /** Twitter module. */ - val twitter: Twitter + val twitter: Twitter = Twitter(this) /** The backlogs URL. */ - var backlogsUrl = "" + val backlogsUrl: String // Ident message - private var identMsg = "" + private val identMsg: String // Ident nick - private var identNick = "" + private val identNick: String // NickServ ident password - private var identPwd = "" + private val identPwd: String // Is pinboard enabled? private var isPinboardEnabled = false @@ -163,7 +163,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert val timer = Timer(true) /** Weblog URL */ - var weblogUrl = "" + val weblogUrl: String /** The current channel name. */ private val channelName: String @@ -212,7 +212,9 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert connect(ircServer, ircPort) } catch (ex: Exception) { if (retries == MAX_RECONNECT) { - logger.debug("Unable to reconnect to $ircServer, after $MAX_RECONNECT retries.", ex) + if (logger.isDebugEnabled) { + logger.debug("Unable to reconnect to $ircServer, after $MAX_RECONNECT retries.", ex) + } e.printStackTrace(System.err) exitProcess(1) } @@ -350,7 +352,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert hostname: String, message: String ) { - logger.debug(">>> $sender: $message") + if (logger.isDebugEnabled) logger.debug(">>> $sender: $message") tell.send(sender, true) if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command> val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) @@ -396,9 +398,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert hostname: String, message: String ) { - if (logger.isDebugEnabled) { - logger.debug(">>> $sender : $message") - } + if (logger.isDebugEnabled) logger.debug(">>> $sender : $message") val cmds = message.split(" ".toRegex(), 2) val cmd = cmds[0].toLowerCase() val args = if (cmds.size > 1) { @@ -467,10 +467,10 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert fun send(sender: String, message: String?, isPrivate: Boolean) { if (message != null && sender.isNotBlank()) { if (isPrivate) { - logger.debug("Sending message to $sender : $message") + if (logger.isDebugEnabled) logger.debug("Sending message to $sender : $message") sendMessage(sender, message) } else { - logger.debug("Sending notice to $sender: $message") + if (logger.isDebugEnabled) logger.debug("Sending notice to $sender: $message") sendNotice(sender, message) } } @@ -597,7 +597,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert try { commandLine = parser.parse(options, args) } catch (e: ParseException) { - System.err.println("CLI Parsing failed. Reason: ${e.message}") + System.err.println("CLI Parsing failed. Reason: ${e.message}") e.printStackTrace(System.err) exitProcess(1) } @@ -684,13 +684,13 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert this.channel = channel logsDir = logsDirPath - // Set the default logger level + // Store the default logger level loggerLevel = logger.level // Load the current entries and backlogs, if any try { startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) - logger.debug("Last feed: $startDate") + if (logger.isDebugEnabled) logger.debug("Last feed: $startDate") } catch (e: Exception) { logger.error("An error occurred while loading the logs.", e) } @@ -712,7 +712,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert backlogsUrl = ensureDir(p.getProperty("backlogs", weblogUrl), true) // Set the pinboard authentication - setPinboardAuth(p.getProperty("pinboard-api-token")) + setPinboardAuth(p.getProperty("pinboard-api-token", "")) // Load the commands addons.add(AddLog(this), p) @@ -730,7 +730,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(Versions(this), p) // Tell command - tell = Tell(this) addons.add(tell, p) // Load the links commands @@ -755,7 +754,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(WorldTime(this), p) // Twitter module - twitter = Twitter(this) addons.add(twitter, p) // Sort the addons diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt index 168a0cf..e6341f8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -36,7 +36,6 @@ import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import java.time.Clock import java.time.LocalDateTime -import java.util.* class Recap(bot: Mobibot) : AbstractCommand(bot) { override val name = "recap" @@ -49,7 +48,7 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { override val isVisible = true companion object { - private val recaps = ArrayList<String>(0) + private val recaps = mutableListOf<String>() @JvmStatic fun recapCount(): Int { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index 49d1130..0315560 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -35,7 +35,6 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import org.jibble.pircbot.User -import java.util.* class Users(bot: Mobibot) : AbstractCommand(bot) { override val name = "users" @@ -56,7 +55,7 @@ class Users(bot: Mobibot) : AbstractCommand(bot) { isPrivate: Boolean ) { val users: Array<User> = bot.getUsers(bot.channel) - val nicks = ArrayList<String>() + val nicks = mutableListOf<String>() users.forEach { user -> if (bot.isOp(user.nick)) { nicks.add("@${user.nick}") diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index cd4104b..f246760 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -44,8 +44,8 @@ import org.jsoup.Jsoup import java.io.IOException class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { - private val keywords: MutableList<String> = ArrayList() - private val defaultTags: MutableList<String> = ArrayList() + private val keywords: MutableList<String> = mutableListOf() + private val defaultTags: MutableList<String> = mutableListOf() override val name = Constants.LINK_CMD override val help = emptyList<String>() @@ -65,11 +65,11 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { // Entries array @JvmField - val entries = ArrayList<EntryLink>(0) + val entries = mutableListOf<EntryLink>() // History/backlogs array @JvmField - val history = ArrayList<String>(0) + val history = mutableListOf<String>() @JvmStatic @@ -207,7 +207,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { return false } - private fun matchTagKeywords(title: String, tags: ArrayList<String>) { + private fun matchTagKeywords(title: String, tags: MutableList<String>) { for (match in keywords) { val m = Regex.escape(match) if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index c0e97d0..f79bb4c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -41,14 +41,13 @@ import net.thauvin.erik.mobibot.Utils.Companion.reverseColor import net.thauvin.erik.mobibot.Utils.Companion.utcDateTime import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.View -import java.util.concurrent.CopyOnWriteArrayList /** * The `Tell` command. */ class Tell(bot: Mobibot) : AbstractCommand(bot) { // Messages queue - private val messages: MutableList<TellMessage> = CopyOnWriteArrayList() + private val messages: MutableList<TellMessage> = mutableListOf() // Serialized object file private val serializedObject: String @@ -63,9 +62,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { * Cleans the messages queue. */ private fun clean(): Boolean { - if (bot.logger.isDebugEnabled) { - bot.logger.debug("Cleaning the messages.") - } + if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.") return TellMessagesMgr.clean(messages, maxDays) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index 3f8710a..f3907ec 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -66,9 +66,7 @@ internal class TellMessagesMgr private constructor() { ObjectInputStream( BufferedInputStream(Files.newInputStream(Paths.get(file))) ).use { input -> - if (logger.isDebugEnabled) { - logger.debug("Loading the messages.") - } + if (logger.isDebugEnabled) logger.debug("Loading the messages.") @Suppress("UNCHECKED_CAST") return input.readObject() as List<TellMessage> } @@ -79,7 +77,7 @@ internal class TellMessagesMgr private constructor() { } catch (e: ClassNotFoundException) { logger.error("An error occurred loading the messages queue.", e) } - return ArrayList() + return listOf() } /** @@ -89,9 +87,7 @@ internal class TellMessagesMgr private constructor() { try { BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> ObjectOutputStream(bos).use { output -> - if (logger.isDebugEnabled) { - logger.debug("Saving the messages.") - } + if (logger.isDebugEnabled) logger.debug("Saving the messages.") output.writeObject(messages) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index f8e9a43..54ba3e6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -48,7 +48,6 @@ import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths import java.util.* -import kotlin.collections.ArrayList /** * Manages the feed entries. @@ -79,7 +78,7 @@ class EntriesMgr private constructor() { * Loads the backlogs. */ @Throws(IOException::class, FeedException::class) - fun loadBacklogs(file: String, history: ArrayList<String>) { + fun loadBacklogs(file: String, history: MutableList<String>) { history.clear() val input = SyndFeedInput() InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader -> @@ -95,7 +94,7 @@ class EntriesMgr private constructor() { * Loads the current entries. */ @Throws(IOException::class, FeedException::class) - fun loadEntries(file: String, channel: String, entries: ArrayList<EntryLink>): String { + fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String { entries.clear() val input = SyndFeedInput() var today: String @@ -139,14 +138,12 @@ class EntriesMgr private constructor() { history: MutableList<String>, isDayBackup: Boolean ) { - if (bot.logger.isDebugEnabled) { - bot.logger.debug("Saving the feeds...") - } + if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...") if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { try { val output = SyndFeedOutput() var rss: SyndFeed = SyndFeedImpl() - val items: MutableList<SyndEntry> = ArrayList(0) + val items: MutableList<SyndEntry> = mutableListOf() var item: SyndEntry OutputStreamWriter( Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8 @@ -194,9 +191,7 @@ class EntriesMgr private constructor() { } } rss.entries = items - if (bot.logger.isDebugEnabled) { - bot.logger.debug("Writing the entries feed.") - } + if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the entries feed.") output.output(rss, fw) } OutputStreamWriter( @@ -238,9 +233,7 @@ class EntriesMgr private constructor() { items.add(item) } rss.entries = items - if (bot.logger.isDebugEnabled) { - bot.logger.debug("Writing the backlog feed.") - } + if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.") output.output(rss, fw) } } else { @@ -248,12 +241,14 @@ class EntriesMgr private constructor() { } } } catch (e: FeedException) { - bot.logger.warn("Unable to generate the entries feed.", e) + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) } catch (e: IOException) { - bot.logger.warn("Unable to generate the entries feed.", e) + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) } } else { - bot.logger.warn("Unable to generate the entries feed. A required property is missing.") + if (bot.logger.isWarnEnabled) { + bot.logger.warn("Unable to generate the entries feed. A required property is missing.") + } } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt index 5d6eea2..a33a180 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -37,17 +37,16 @@ import net.thauvin.erik.mobibot.commands.links.LinksMgr import org.apache.commons.lang3.StringUtils import java.io.Serializable import java.util.* -import java.util.concurrent.CopyOnWriteArrayList /** * The class used to store link entries. */ class EntryLink : Serializable { // Link's comments - val comments: MutableList<EntryComment> = CopyOnWriteArrayList() + val comments: MutableList<EntryComment> = mutableListOf() // Tags/categories - val tags: MutableList<SyndCategory> = CopyOnWriteArrayList() + val tags: MutableList<SyndCategory> = mutableListOf() // Channel var channel: String diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt index f4f36a7..eb97e4f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -33,8 +33,6 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import java.util.* -import java.util.concurrent.ConcurrentHashMap /** * The `Module` abstract class. @@ -44,11 +42,11 @@ abstract class AbstractModule(val bot: Mobibot) { * The module's commands, if any. */ @JvmField - val commands: MutableList<String> = ArrayList() + val commands: MutableList<String> = mutableListOf() @JvmField - val help: MutableList<String> = ArrayList() - val properties: MutableMap<String, String> = ConcurrentHashMap() + val help: MutableList<String> = mutableListOf() + val properties: MutableMap<String, String> = mutableMapOf() /** * Responds to a command. diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index f15c184..9038a7a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -43,7 +43,6 @@ import org.jdom2.input.SAXBuilder import java.io.IOException import java.net.URL import java.text.NumberFormat -import java.util.* import javax.xml.XMLConstants /** @@ -73,7 +72,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { try { loadRates() } catch (e: ModuleException) { - logger.warn(e.debugMessage, e) + if (bot.logger.isWarnEnabled)logger.warn(e.debugMessage, e) } } @@ -100,7 +99,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { try { loadRates() } catch (e: ModuleException) { - logger.debug(e.debugMessage, e) + if (logger.isDebugEnabled) logger.debug(e.debugMessage, e) } } if (EXCHANGE_RATES.isEmpty()) { @@ -139,7 +138,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { private const val EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty." // Exchange rates - private val EXCHANGE_RATES: MutableMap<String, String> = TreeMap() + private val EXCHANGE_RATES: MutableMap<String, String> = mutableMapOf() // Exchange rates table URL private const val EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml" @@ -183,7 +182,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { @JvmStatic fun currencyRates(): List<String> { - val rates = ArrayList<String>(33) + val rates = mutableListOf<String>() for ((key, value) in EXCHANGE_RATES) { rates.add(" $key: ${StringUtils.leftPad(value, 8)}") } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index b9c78d0..687ce17 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -41,7 +41,6 @@ import org.json.JSONException import org.json.JSONObject import java.io.IOException import java.net.URL -import java.util.* /** * The GoogleSearch module. @@ -62,7 +61,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { send(sender, msg) } } catch (e: ModuleException) { - logger.warn(e.debugMessage, e) + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) send(sender, e.message, isPrivate) } } else { @@ -91,7 +90,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { throw ModuleException("${StringUtils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.") } return if (query.isNotBlank()) { - val results = ArrayList<Message>() + val results = mutableListOf<Message>() try { val url = URL( "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt index 26383e2..bc760b2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt @@ -60,7 +60,7 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { try { bot.send(Utils.cyan(randomJoke().msg)) } catch (e: ModuleException) { - bot.logger.warn(e.debugMessage, e) + if (bot.logger.isWarnEnabled) bot.logger.warn(e.debugMessage, e) bot.send(sender, e.message, isPrivate) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt index 91954b6..32557bd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -76,7 +76,9 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { send("Unknown host.") } } catch (ioe: IOException) { - logger.debug("Unable to perform whois IP lookup: {}", args, ioe) + if (logger.isDebugEnabled) { + logger.debug("Unable to perform whois IP lookup: $args", ioe) + } send("Unable to perform whois IP lookup: ${ioe.message}") } } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index 702077a..5e4ae34 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -41,7 +41,6 @@ import org.json.JSONException import org.json.JSONObject import java.io.IOException import java.net.URL -import java.util.* /** * The StockQuote module. @@ -59,7 +58,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { send(sender, msg) } } catch (e: ModuleException) { - logger.warn(e.debugMessage, e) + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) send(e.message) } } else { @@ -126,7 +125,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { } return if (symbol.isNotBlank()) { val debugMessage = "getQuote($symbol)" - val messages = ArrayList<Message>() + val messages = mutableListOf<Message>() var response: String try { with(messages) { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index ef648c8..9a7ac28 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -91,11 +91,9 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { Thread { try { post(message = msg, isDm = true) - if (logger.isDebugEnabled) { - logger.debug("Notified @{}: {}", handle, msg) - } + if (logger.isDebugEnabled) logger.debug("Notified @$handle: $msg") } catch (e: ModuleException) { - logger.warn("Failed to notify @{}: {}", handle, msg, e) + if (logger.isWarnEnabled) logger.warn("Failed to notify @$handle: $msg", e) } }.start() } @@ -133,7 +131,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { } post(message = msg, isDm = false) } catch (e: ModuleException) { - logger.warn("Failed to post entry on Twitter.", e) + if (bot.logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e) } }.start() removeEntry(index) @@ -167,7 +165,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { isPrivate ) } catch (e: ModuleException) { - logger.warn(e.debugMessage, e) + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) send(sender, e.message, isPrivate) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index 5595519..ab2e385 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -42,7 +42,6 @@ import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage import net.thauvin.erik.mobibot.msg.PublicMessage import org.jibble.pircbot.Colors -import java.util.* import kotlin.math.roundToInt /** @@ -64,7 +63,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } } } catch (e: ModuleException) { - bot.logger.debug(e.debugMessage, e) + if (bot.logger.isDebugEnabled) bot.logger.debug(e.debugMessage, e) bot.send(e.message) } } else { @@ -104,7 +103,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { throw ModuleException("${WEATHER_CMD.capitalize()} is disabled. The API key is missing.") } val owm = OWM(apiKey) - val messages = ArrayList<Message>() + val messages = mutableListOf<Message>() owm.unit = OWM.Unit.IMPERIAL if (query.isNotBlank()) { val argv = query.split(",") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index 835fcca..3338439 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -41,6 +41,7 @@ import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField import java.util.* +import kotlin.collections.ArrayList /** * The WorldTime module. @@ -94,7 +95,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { init { // Initialize the countries map - val countries = TreeMap<String, String>() + val countries = mutableMapOf<String, String>() countries["AE"] = "Asia/Dubai" countries["AF"] = "Asia/Kabul" countries["AQ"] = "Antarctica/South_Pole" From b109ce2de5b0ae4414b3672f99cfa8279b4bc925 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 6 Dec 2020 01:49:37 -0800 Subject: [PATCH 452/842] Version 0.8.0-beta+346 --- version.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.properties b/version.properties index 0f713d3..df4e27a 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Dec 04 22:59:43 PST 2020 -version.buildmeta=337 +#Sun Dec 06 01:19:28 PST 2020 +version.buildmeta=346 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+337 +version.semver=0.8.0-beta+346 From 625ef1368cc233905476ae099640d4dd8ab4e5f4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 6 Dec 2020 02:00:36 -0800 Subject: [PATCH 453/842] Updated baseline. --- config/detekt/baseline.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 7d60806..5d4c70d 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -53,6 +53,7 @@ <ID>MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): Message</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> From 250c52b7d50ca3b50e279f555d9dc156dcf28ddc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 6 Dec 2020 02:39:24 -0800 Subject: [PATCH 454/842] Reverted Tell and Twitter initializations to the init stage. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index dcdd5a2..bb03e89 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -136,13 +136,13 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert private val pinboard: PinboardPoster = PinboardPoster() /** Tell command. */ - val tell: Tell = Tell(this) + val tell: Tell /** Today's date. */ val today = today() /** Twitter module. */ - val twitter: Twitter = Twitter(this) + val twitter: Twitter /** The backlogs URL. */ val backlogsUrl: String @@ -730,6 +730,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(Versions(this), p) // Tell command + tell = Tell(this) addons.add(tell, p) // Load the links commands @@ -754,6 +755,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(WorldTime(this), p) // Twitter module + twitter = Twitter(this) addons.add(twitter, p) // Sort the addons From f6656f116662a6d9191f2ced5901e66eef35a6ff Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 6 Dec 2020 02:39:36 -0800 Subject: [PATCH 455/842] Version 0.8.0-beta+350 --- version.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.properties b/version.properties index df4e27a..254cc73 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Dec 06 01:19:28 PST 2020 -version.buildmeta=346 +#Sun Dec 06 02:35:51 PST 2020 +version.buildmeta=350 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+346 +version.semver=0.8.0-beta+350 From 1c2234a9a24184e490c4f1c76e228fa0353b3e90 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 6 Dec 2020 16:02:05 -0800 Subject: [PATCH 456/842] Added Debug command. Converted Utils classes to objects. --- .idea/mobibot.iml | 2 +- .idea/modules/mobibot.main.iml | 63 ++- .idea/modules/mobibot.test.iml | 92 ++-- build.gradle | 2 +- config/detekt/baseline.xml | 11 +- .../net/thauvin/erik/mobibot/Constants.kt | 7 +- .../java/net/thauvin/erik/mobibot/Mobibot.kt | 30 +- .../net/thauvin/erik/mobibot/PinboardUtils.kt | 2 +- .../java/net/thauvin/erik/mobibot/Utils.kt | 461 +++++++++--------- .../thauvin/erik/mobibot/commands/Debug.kt | 58 +++ .../erik/mobibot/commands/tell/Tell.kt | 14 +- .../mobibot/commands/tell/TellMessagesMgr.kt | 1 - .../erik/mobibot/entries/EntriesMgr.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 79 ++- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 12 +- .../net/thauvin/erik/mobibot/UtilsTest.java | 160 ------ .../net/thauvin/erik/mobibot/UtilsTest.kt | 172 +++++++ .../erik/mobibot/modules/LookupTest.kt | 1 - 19 files changed, 605 insertions(+), 566 deletions(-) create mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt delete mode 100644 src/test/java/net/thauvin/erik/mobibot/UtilsTest.java create mode 100644 src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml index 85e216a..255f0c1 100644 --- a/.idea/mobibot.iml +++ b/.idea/mobibot.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+359" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml index 4c00984..c12d92e 100644 --- a/.idea/modules/mobibot.main.iml +++ b/.idea/modules/mobibot.main.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+359" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> + <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -54,34 +54,6 @@ <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> </content> <orderEntry type="inheritedJdk" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module-library"> <library name="Gradle: kaptGeneratedClasses"> @@ -95,7 +67,34 @@ <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> </component> </module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml index 822bffe..9e64bd2 100644 --- a/.idea/modules/mobibot.test.iml +++ b/.idea/modules/mobibot.test.iml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4"> +<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+359" type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="kotlin-language" name="Kotlin"> <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> @@ -8,7 +8,7 @@ </compilerSettings> <compilerArguments> <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/media/erik/Projects/java/mobibot/build/tmp/kapt3/classes/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.18.1/aaa02680dd92a568a4278bb40aa4a6305f632ec0/assertj-core-3.18.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> + <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/media/erik/Projects/java/mobibot/build/tmp/kapt3/classes/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.18.1/aaa02680dd92a568a4278bb40aa4a6305f632ec0/assertj-core-3.18.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> <option name="noStdlib" value="true" /> <option name="noReflect" value="true" /> <option name="moduleName" value="mobibot" /> @@ -63,6 +63,49 @@ <sourceFolder url="file://$MODULE_DIR$/../../src/test/kotlin" isTestSource="true" /> </content> <orderEntry type="inheritedJdk" /> + <orderEntry type="library" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" /> + <orderEntry type="library" name="Gradle: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> + <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> + <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> + <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> + <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> + <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> + <orderEntry type="library" name="Gradle: com.google.j2objc:j2objc-annotations:1.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> + <orderEntry type="library" name="Gradle: org.testng:testng:7.3.0" level="project" /> + <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.18.1" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> + <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> + <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> + <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.2.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: com.beust:jcommander:1.78" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: com.google.errorprone:error_prone_annotations:2.1.3" level="project" /> + <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> + <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> + <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.ant:ant-launcher:1.10.3" level="project" /> + <orderEntry type="library" name="Gradle: org.checkerframework:checker-compat-qual:2.0.0" level="project" /> + <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> + <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> + <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module-library"> <library name="Gradle: kaptGeneratedClasses"> @@ -77,51 +120,6 @@ <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> - <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.18.1" level="project" /> - <orderEntry type="library" name="Gradle: org.testng:testng:7.3.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.2.2" level="project" /> - <orderEntry type="library" name="Gradle: com.beust:jcommander:1.78" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> - <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> - <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> - <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> - <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.ant:ant-launcher:1.10.3" level="project" /> - <orderEntry type="library" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" /> - <orderEntry type="library" name="Gradle: org.checkerframework:checker-compat-qual:2.0.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.errorprone:error_prone_annotations:2.1.3" level="project" /> - <orderEntry type="library" name="Gradle: com.google.j2objc:j2objc-annotations:1.1" level="project" /> - <orderEntry type="library" name="Gradle: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" /> - <orderEntry type="library" scope="RUNTIME" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> </component> <component name="TestModuleProperties" production-module="mobibot.main" /> </module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9ff3602..8c0d5f3 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ repositories { dependencies { kapt(semverProcessor) - implementation(semverProcessor) + compileOnly(semverProcessor) implementation 'pircbot:pircbot:1.5.0' compileOnly 'pircbot:pircbot:1.5.0:sources' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 5d4c70d..dc51202 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -3,8 +3,8 @@ <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> - <ID>ComplexMethod:Mobibot.kt$Mobibot$override fun onPrivateMessage( sender: String, login: String, hostname: String, message: String )</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>EmptyDefaultConstructor:Message.kt$Message$()</ID> <ID>LongMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>LongMethod:Mobibot.kt$Mobibot.Companion$ @JvmStatic fun main(args: Array<String>)</ID> <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> @@ -35,9 +35,9 @@ <ID>MagicNumber:Twitter.kt$Twitter$1000L</ID> <ID>MagicNumber:Twitter.kt$Twitter$60L</ID> <ID>MagicNumber:Users.kt$Users$8</ID> - <ID>MagicNumber:Utils.kt$Utils.Companion$30</ID> - <ID>MagicNumber:Utils.kt$Utils.Companion$365</ID> - <ID>MagicNumber:Utils.kt$Utils.Companion$7</ID> + <ID>MagicNumber:Utils.kt$Utils$30</ID> + <ID>MagicNumber:Utils.kt$Utils$365</ID> + <ID>MagicNumber:Utils.kt$Utils$7</ID> <ID>MagicNumber:View.kt$View$8</ID> <ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID> <ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID> @@ -75,7 +75,6 @@ <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>ReturnCount:Mobibot.kt$Mobibot$override fun onMessage( channel: String, sender: String, login: String, hostname: String, message: String )</ID> - <ID>ReturnCount:Utils.kt$Utils.Companion$ @JvmStatic fun colorize(s: String?, color: String): String</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> @@ -87,6 +86,6 @@ <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> <ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID> <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> - <ID>TooManyFunctions:Utils.kt$Utils$Companion</ID> + <ID>TooManyFunctions:Utils.kt$Utils$Utils</ID> </CurrentIssues> </SmellBaseline> diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.kt b/src/main/java/net/thauvin/erik/mobibot/Constants.kt index 4f3d1ae..575e29a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot import java.util.* /** - * The `Constants` class. + * The `Constants`. */ object Constants { /** @@ -77,6 +77,11 @@ object Constants { */ const val HELP_CMD = "help" + /** + * The kill command. + */ + const val KILL_CMD = "kill" + /** * The link command. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index bb03e89..e6c1f8b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -34,16 +34,17 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.PinboardUtils.addPin import net.thauvin.erik.mobibot.PinboardUtils.deletePin import net.thauvin.erik.mobibot.PinboardUtils.updatePin -import net.thauvin.erik.mobibot.Utils.Companion.colorize -import net.thauvin.erik.mobibot.Utils.Companion.ensureDir -import net.thauvin.erik.mobibot.Utils.Companion.getIntProperty -import net.thauvin.erik.mobibot.Utils.Companion.helpFormat -import net.thauvin.erik.mobibot.Utils.Companion.helpIndent -import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate -import net.thauvin.erik.mobibot.Utils.Companion.today +import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.ensureDir +import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.helpIndent +import net.thauvin.erik.mobibot.Utils.isoLocalDate +import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AddLog import net.thauvin.erik.mobibot.commands.ChannelFeed import net.thauvin.erik.mobibot.commands.Cycle +import net.thauvin.erik.mobibot.commands.Debug import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.Info import net.thauvin.erik.mobibot.commands.Me @@ -92,7 +93,6 @@ import org.apache.commons.cli.ParseException import org.apache.logging.log4j.Level import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger -import org.apache.logging.log4j.core.config.Configurator import org.jibble.pircbot.PircBot import java.io.BufferedOutputStream import java.io.FileNotFoundException @@ -126,8 +126,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert /** Logger. */ val logger: Logger = LogManager.getLogger(Mobibot::class.java) - // Logger default level - private val loggerLevel: Level + /** Logger default level. */ + val loggerLevel: Level /** Log directory. */ val logsDir: String @@ -407,17 +407,10 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert val isOp = isOp(sender) if (cmd.startsWith(Constants.HELP_CMD)) { // help helpResponse(sender, args, true) - } else if (isOp && "kill" == cmd) { // kill + } else if (isOp && Constants.KILL_CMD == cmd) { // kill twitter.notification("$name killed by $sender on $channel") sendRawLine("QUIT : Poof!") exitProcess(0) - } else if (isOp && Constants.DEBUG_CMD == cmd) { // debug - if (logger.isDebugEnabled) { - Configurator.setLevel(logger.name, loggerLevel) - } else { - Configurator.setLevel(logger.name, Level.DEBUG) - } - send(sender, "Debug logging is " + if (logger.isDebugEnabled) "enabled." else "disabled.", true) } else if (isOp && Constants.DIE_CMD == cmd) { // die send("$sender has just signed my death sentence.") timer.cancel() @@ -718,6 +711,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(AddLog(this), p) addons.add(ChannelFeed(this, channelName), p) addons.add(Cycle(this), p) + addons.add(Debug(this), p) addons.add(Ignore(this), p) addons.add(Info(this), p) addons.add(Me(this), p) diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt index 1dad880..addf76b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -43,7 +43,7 @@ import java.time.format.DateTimeFormatter import java.util.* /** - * The class to handle posts to pinboard.in. + * Handles posts to pinboard.in. */ object PinboardUtils { /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index 09f7062..1aa1829 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -49,261 +49,240 @@ import java.util.concurrent.TimeUnit import java.util.stream.Collectors /** - * Miscellaneous utilities class. + * Miscellaneous utilities. */ -class Utils private constructor() { - companion object { - private val searchFlags = arrayOf("%c", "%n") +object Utils { + private val searchFlags = arrayOf("%c", "%n") - /** - * Makes the given int bold. - */ - @JvmStatic - fun bold(i: Int): String { - return bold(i.toString()) + /** + * Makes the given int bold. + */ + @JvmStatic + fun bold(i: Int): String = bold(i.toString()) + + /** + * Makes the given string bold. + */ + @JvmStatic + fun bold(s: String?): String = colorize(s, Colors.BOLD) + + /** + * Colorize a string. + */ + @JvmStatic + fun colorize(s: String?, color: String): String { + return if (s.isNullOrBlank()) { + Colors.NORMAL + } else if (Colors.BOLD == color || Colors.REVERSE == color) { + color + s + color + } else { + color + s + Colors.NORMAL } + } - /** - * Makes the given string bold. - */ - @JvmStatic - fun bold(s: String?): String { - return colorize(s, Colors.BOLD) - } + /** + * Makes the given string cyan. + */ + @JvmStatic + fun cyan(s: String?): String = colorize(s, Colors.CYAN) - /** - * Colorize a string. - */ - @JvmStatic - fun colorize(s: String?, color: String): String { - if (s.isNullOrBlank()) { - return Colors.NORMAL - } else if (Colors.BOLD == color || Colors.REVERSE == color) { - return color + s + color - } - return color + s + Colors.NORMAL - } + /** + * URL encodes the given string. + */ + fun encodeUrl(s: String): String = URLEncoder.encode(s, StandardCharsets.UTF_8) - /** - * Makes the given string cyan. - */ - @JvmStatic - fun cyan(s: String?): String { - return colorize(s, Colors.CYAN) - } - - /** - * URL encodes the given string. - */ - fun encodeUrl(s: String): String { - return URLEncoder.encode(s, StandardCharsets.UTF_8) - } - - /** - * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. - */ - @JvmStatic - fun ensureDir(location: String, isUrl: Boolean): String { - return if (location.isNotEmpty()) { - if (isUrl) { - if (location[location.length - 1] == '/') { - location - } else { - "$location/" - } + /** + * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. + */ + @JvmStatic + fun ensureDir(location: String, isUrl: Boolean): String { + return if (location.isNotEmpty()) { + if (isUrl) { + if (location[location.length - 1] == '/') { + location } else { - if (location[location.length - 1] == File.separatorChar) { - location - } else { - location + File.separatorChar - } + "$location/" } } else { - location + if (location[location.length - 1] == File.separatorChar) { + location + } else { + location + File.separatorChar + } } + } else { + location } + } - /** - * Returns a property as an int. - */ - @JvmStatic - fun getIntProperty(property: String?, def: Int): Int { - return if (property == null) { + /** + * Returns a property as an int. + */ + @JvmStatic + fun getIntProperty(property: String?, def: Int): Int { + return if (property == null) { + def + } else { + try { + property.toInt() + } catch (ignore: NumberFormatException) { def - } else { - try { - property.toInt() - } catch (ignore: NumberFormatException) { - def - } - } - } - - /** - * Makes the given string green. - */ - @JvmStatic - fun green(s: String?): String { - return colorize(s, Colors.DARK_GREEN) - } - - /** - * Formats a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's - * nick. - */ - @JvmStatic - fun helpFormat(text: String, botNick: String, isPrivate: Boolean): String { - val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) - return StringUtils.replaceEach(text, searchFlags, replace) - } - - /** - * Returns indented help string. - */ - @JvmStatic - @JvmOverloads - fun helpIndent(help: String, isBold: Boolean = true): String { - return " " + if (isBold) bold(help) else help - } - - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun isoLocalDate(date: Date): String { - return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) - } - - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun isoLocalDate(date: LocalDateTime): String { - return date.format(DateTimeFormatter.ISO_LOCAL_DATE) - } - - /** - * Obfuscates the given string. - */ - @JvmStatic - fun obfuscate(s: String): String { - return if (s.isNotBlank()) { - StringUtils.repeat('x', s.length) - } else s - } - - /** - * Returns the plural form of a word, if count > 1. - */ - @JvmStatic - fun plural(count: Long, word: String, plural: String): String { - return if (count > 1) { - plural - } else { - word - } - } - - /** - * Makes the given string red. - */ - @JvmStatic - fun red(s: String?): String { - return colorize(s, Colors.RED) - } - - /** - * Makes the given string reverse color. - */ - @JvmStatic - fun reverseColor(s: String?): String { - return colorize(s, Colors.REVERSE) - } - - /** - * Returns today's date. - */ - @JvmStatic - fun today(): String { - return isoLocalDate(LocalDateTime.now()) - } - - /** - * Converts XML/XHTML entities to plain text. - */ - @JvmStatic - fun unescapeXml(str: String): String { - return Jsoup.parse(str).text() - } - - /** - * Converts milliseconds to year month week day hour and minutes. - */ - @JvmStatic - fun uptime(uptime: Long): String { - val info = StringBuilder() - var days = TimeUnit.MILLISECONDS.toDays(uptime) - val years = days / 365 - days %= 365 - val months = days / 30 - days %= 30 - val weeks = days / 7 - days %= 7 - val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours( - TimeUnit.MILLISECONDS.toDays(uptime) - ) - val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( - TimeUnit.MILLISECONDS.toHours(uptime) - ) - with(info) { - if (years > 0) { - append(years).append(plural(years, " year ", " years ")) - } - if (months > 0) { - append(weeks).append(plural(months, " month ", " months ")) - } - if (weeks > 0) { - append(weeks).append(plural(weeks, " week ", " weeks ")) - } - if (days > 0) { - append(days).append(plural(days, " day ", " days ")) - } - if (hours > 0) { - append(hours).append(plural(hours, " hour ", " hours ")) - } - append(minutes).append(plural(minutes, " minute", " minutes")) - return toString() - } - } - - /** - * Reads contents of a URL. - */ - @JvmStatic - @Throws(IOException::class) - fun urlReader(url: URL): String { - BufferedReader(InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) - .use { reader -> return reader.lines().collect(Collectors.joining(System.lineSeparator())) } - } - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun utcDateTime(date: Date): String { - return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) - } - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun utcDateTime(date: LocalDateTime?): String { - return if (date != null) { - date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) - } else { - "" } } } + + /** + * Makes the given string green. + */ + @JvmStatic + fun green(s: String?): String = colorize(s, Colors.DARK_GREEN) + + /** + * Formats a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's + * nick. + */ + @JvmStatic + fun helpFormat(text: String, botNick: String, isPrivate: Boolean): String { + val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) + return StringUtils.replaceEach(text, searchFlags, replace) + } + + /** + * Returns indented help string. + */ + @JvmStatic + @JvmOverloads + fun helpIndent(help: String, isBold: Boolean = true): String { + return " " + if (isBold) bold(help) else help + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun isoLocalDate(date: Date): String { + return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun isoLocalDate(date: LocalDateTime): String = date.format(DateTimeFormatter.ISO_LOCAL_DATE) + + /** + * Obfuscates the given string. + */ + @JvmStatic + fun obfuscate(s: String): String { + return if (s.isNotBlank()) { + StringUtils.repeat('x', s.length) + } else s + } + + /** + * Returns the plural form of a word, if count > 1. + */ + @JvmStatic + fun plural(count: Long, word: String, plural: String): String { + return if (count > 1) { + plural + } else { + word + } + } + + /** + * Makes the given string red. + */ + @JvmStatic + fun red(s: String?): String = colorize(s, Colors.RED) + + /** + * Makes the given string reverse color. + */ + @JvmStatic + fun reverseColor(s: String?): String = colorize(s, Colors.REVERSE) + + /** + * Returns today's date. + */ + @JvmStatic + fun today(): String = isoLocalDate(LocalDateTime.now()) + + /** + * Converts XML/XHTML entities to plain text. + */ + @JvmStatic + fun unescapeXml(str: String): String = Jsoup.parse(str).text() + + /** + * Converts milliseconds to year month week day hour and minutes. + */ + @JvmStatic + fun uptime(uptime: Long): String { + val info = StringBuilder() + var days = TimeUnit.MILLISECONDS.toDays(uptime) + val years = days / 365 + days %= 365 + val months = days / 30 + days %= 30 + val weeks = days / 7 + days %= 7 + val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours( + TimeUnit.MILLISECONDS.toDays(uptime) + ) + val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( + TimeUnit.MILLISECONDS.toHours(uptime) + ) + with(info) { + if (years > 0) { + append(years).append(plural(years, " year ", " years ")) + } + if (months > 0) { + append(weeks).append(plural(months, " month ", " months ")) + } + if (weeks > 0) { + append(weeks).append(plural(weeks, " week ", " weeks ")) + } + if (days > 0) { + append(days).append(plural(days, " day ", " days ")) + } + if (hours > 0) { + append(hours).append(plural(hours, " hour ", " hours ")) + } + append(minutes).append(plural(minutes, " minute", " minutes")) + return toString() + } + } + + /** + * Reads contents of a URL. + */ + @JvmStatic + @Throws(IOException::class) + fun urlReader(url: URL): String { + BufferedReader(InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) + .use { reader -> return reader.lines().collect(Collectors.joining(System.lineSeparator())) } + } + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun utcDateTime(date: Date): String { + return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) + } + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun utcDateTime(date: LocalDateTime?): String { + return if (date != null) { + date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + } else { + "" + } + } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt new file mode 100644 index 0000000..9bf3630 --- /dev/null +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt @@ -0,0 +1,58 @@ +/* + * Debug.kt + * + * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.core.config.Configurator + +class Debug(bot: Mobibot) : AbstractCommand(bot) { + override val name = "debug" + override val help = emptyList<String>() + override val isOp = true + override val isPublic = false + override val isVisible = false + + override fun commandResponse(sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean) { + if (isOp) { + with(bot) { + if (logger.isDebugEnabled) { + Configurator.setLevel(logger.name, loggerLevel) + } else { + Configurator.setLevel(logger.name, Level.DEBUG) + } + send(sender, "Debug logging is " + if (logger.isDebugEnabled) "enabled." else "disabled.", true) + } + } + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index f79bb4c..be54085 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -32,13 +32,13 @@ package net.thauvin.erik.mobibot.commands.tell import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.Companion.bold -import net.thauvin.erik.mobibot.Utils.Companion.getIntProperty -import net.thauvin.erik.mobibot.Utils.Companion.helpFormat -import net.thauvin.erik.mobibot.Utils.Companion.helpIndent -import net.thauvin.erik.mobibot.Utils.Companion.plural -import net.thauvin.erik.mobibot.Utils.Companion.reverseColor -import net.thauvin.erik.mobibot.Utils.Companion.utcDateTime +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.helpIndent +import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.utcDateTime import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.View diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index f3907ec..7a7e927 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -42,7 +42,6 @@ import java.nio.file.Files import java.nio.file.Paths import java.time.Clock import java.time.LocalDateTime -import java.util.* /** * The Tell Messages Manager. diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 54ba3e6..57702c9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -40,7 +40,7 @@ import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.SyndFeedOutput import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate +import net.thauvin.erik.mobibot.Utils.isoLocalDate import java.io.IOException import java.io.InputStreamReader import java.io.OutputStreamWriter diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index a96bad0..d9ca531 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -32,52 +32,49 @@ package net.thauvin.erik.mobibot.entries import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils.Companion.bold -import net.thauvin.erik.mobibot.Utils.Companion.green +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.green /** - * The `Utils` class. + * Entries utilities. */ -class EntriesUtils private constructor() { - companion object { - /** - * Build link cmd based on its index. e.g: L1 - */ - fun buildLinkCmd(index: Int): String = Constants.LINK_CMD + (index + 1) +object EntriesUtils { + /** + * Build link cmd based on its index. e.g: L1 + */ + fun buildLinkCmd(index: Int): String = Constants.LINK_CMD + (index + 1) - /** - * Builds an entry's comment for display on the channel. - */ - fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - (buildLinkCmd(entryIndex) + '.' + (commentIndex + 1) + ": [" + comment.nick + "] " - + comment.comment) + /** + * Builds an entry's comment for display on the channel. + */ + fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = + ("${buildLinkCmd(entryIndex)}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") - /** - * Builds an entry's link for display on the channel. - */ - @JvmOverloads - fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { - val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ") - .append('[').append(entry.nick).append(']') - if (isView && entry.hasComments()) { - buff.append("[+").append(entry.comments.size).append(']') - } - buff.append(' ') - with(entry) { - if (Constants.NO_TITLE == title) { - buff.append(title) - } else { - buff.append(bold(title)) - } - buff.append(" ( ").append(green(link)).append(" )") - } - return buff.toString() + /** + * Builds an entry's link for display on the channel. + */ + @JvmOverloads + fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { + val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ") + .append('[').append(entry.nick).append(']') + if (isView && entry.hasComments()) { + buff.append("[+").append(entry.comments.size).append(']') } - - /** - * Build an entry's tags/categories for display on the channel. - */ - fun buildTags(entryIndex: Int, entry: EntryLink): String = - buildLinkCmd(entryIndex) + "T: " + entry.pinboardTags.replace(",", ", ") + buff.append(' ') + with(entry) { + if (Constants.NO_TITLE == title) { + buff.append(title) + } else { + buff.append(bold(title)) + } + buff.append(" ( ").append(green(link)).append(" )") + } + return buff.toString() } + + /** + * Build an entry's tags/categories for display on the channel. + */ + fun buildTags(entryIndex: Int, entry: EntryLink): String = + buildLinkCmd(entryIndex) + "T: " + entry.pinboardTags.replace(",", ", ") } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 9038a7a..d02e5d6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -72,7 +72,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { try { loadRates() } catch (e: ModuleException) { - if (bot.logger.isWarnEnabled)logger.warn(e.debugMessage, e) + if (bot.logger.isWarnEnabled) logger.warn(e.debugMessage, e) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt index a6f66f7..9987bd8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,5 +1,5 @@ /* - * Message.java + * Message.kt * * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -41,19 +41,19 @@ open class Message { var DEFAULT_COLOR = Colors.NORMAL } - /** Color */ + /** Message color. */ var color = DEFAULT_COLOR - /** Error */ + /** Error flag. */ var isError = false - /** Notice */ + /** Notice flag. */ var isNotice = false - /** Private */ + /** Private flag. */ var isPrivate = false - /** Message text*/ + /** Message text. */ var msg = "" /** Creates a new message. */ diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java deleted file mode 100644 index bde9d77..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * UtilsTest.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot; - -import org.apache.commons.lang3.StringUtils; -import org.jibble.pircbot.Colors; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.time.LocalDateTime; -import java.util.Calendar; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The <code>Utils Test</code> class. - * - * @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a> - * @created 2017-05-30 - * @since 1.0 - */ -public class UtilsTest { - private static final String ASCII = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; - - private final Calendar cal = Calendar.getInstance(); - private final LocalDateTime localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0); - - @BeforeClass - public void setUp() { - cal.set(1952, Calendar.FEBRUARY, 17, 12, 30, 0); - } - - @Test - public void testBold() { - assertThat(Utils.bold(Integer.toString(1))).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD); - assertThat(Utils.bold(ASCII)).as("bold(ascii").isEqualTo(Colors.BOLD + ASCII + Colors.BOLD); - } - - @Test - public void testColorize() { - assertThat(Utils.colorize(ASCII, Colors.REVERSE)).as("colorize(reverse)").isEqualTo( - Colors.REVERSE + ASCII + Colors.REVERSE); - assertThat(Utils.colorize(ASCII, Colors.RED)).as("colorize(red)").isEqualTo(Colors.RED + ASCII + Colors.NORMAL); - assertThat(Utils.colorize(null, Colors.RED)).as("colorize(null)").isEqualTo(Colors.NORMAL); - } - - @Test - public void testCyan() { - assertThat(Utils.cyan(ASCII)).isEqualTo(Colors.CYAN + ASCII + Colors.NORMAL); - } - - @Test - public void testEnsureDir() { - assertThat(Utils.ensureDir("dir", false)).as("ensureDir(dir, false)").isEqualTo("dir" + File.separatorChar); - assertThat(Utils.ensureDir("https://erik.thauvin.net", true)).as("ensureDir(erik.thauvin.net, true)").isEqualTo( - "https://erik.thauvin.net/"); - } - - @Test - public void testGetIntProperty() { - assertThat(Utils.getIntProperty("10", 1)).as("getIntProperty(10, 1)").isEqualTo(10); - assertThat(Utils.getIntProperty("a", 1)).as("getIntProperty(a, 1)").isEqualTo(1); - } - - @Test - public void testGreen() { - assertThat(Utils.green(ASCII)).isEqualTo(Colors.DARK_GREEN + ASCII + Colors.NORMAL); - } - - @Test - public void testIsoLocalDate() { - assertThat(Utils.isoLocalDate(cal.getTime())).as("isoLocalDate(date)").isEqualTo("1952-02-17"); - assertThat(Utils.isoLocalDate(localDateTime)).as("isoLocalDate(localDate)").isEqualTo("1952-02-17"); - } - - @Test - public void testObfuscate() { - assertThat(Utils.obfuscate(ASCII).length()).as("obfuscate is right length").isEqualTo(ASCII.length()); - assertThat(Utils.obfuscate(ASCII)).as("obfuscate()").isEqualTo(StringUtils.repeat("x", ASCII.length())); - assertThat(Utils.obfuscate(" ")).as("obfuscate(blank)").isEqualTo(" "); - } - - @Test - public void testPlural() { - final String week = "week"; - final String weeks = "weeks"; - - assertThat(Utils.plural(-1, week, weeks)).as("plural(-1)").isEqualTo(week); - assertThat(Utils.plural(0, week, weeks)).as("plural(0)").isEqualTo(week); - assertThat(Utils.plural(1, week, weeks)).as("plural(1)").isEqualTo(week); - assertThat(Utils.plural(2, week, weeks)).as("plural(2)").isEqualTo(weeks); - } - - @Test - public void testReverseColor() { - assertThat(Utils.reverseColor(ASCII)).isEqualTo(Colors.REVERSE + ASCII + Colors.REVERSE); - } - - @Test - public void testToday() { - assertThat(Utils.today()).isEqualTo(Utils.isoLocalDate(LocalDateTime.now())); - } - - @Test - public void testUnescapeXml() { - assertThat(Utils.unescapeXml("<a name="test & ''">")).isEqualTo( - "<a name=\"test & ''\">"); - } - - @Test - public void testUptime() { - assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(Utils.uptime(547800300076L)); - } - - @Test - public void testUrlReader() throws IOException { - assertThat(Utils.urlReader(new URL("https://postman-echo.com/status/200"))).as("urlReader()").isEqualTo( - "{\"status\":200}"); - } - - @Test - public void testUtcDateTime() { - assertThat(Utils.utcDateTime(cal.getTime())).as("utcDateTime(date)").isEqualTo("1952-02-17 12:30"); - assertThat(Utils.utcDateTime(localDateTime)).as("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30"); - } -} diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt new file mode 100644 index 0000000..6e2c269 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt @@ -0,0 +1,172 @@ +/* + * UtilsTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.cyan +import net.thauvin.erik.mobibot.Utils.ensureDir +import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.isoLocalDate +import net.thauvin.erik.mobibot.Utils.obfuscate +import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.today +import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.Utils.uptime +import net.thauvin.erik.mobibot.Utils.urlReader +import net.thauvin.erik.mobibot.Utils.utcDateTime +import org.apache.commons.lang3.StringUtils +import org.assertj.core.api.Assertions +import org.jibble.pircbot.Colors +import org.testng.annotations.BeforeClass +import org.testng.annotations.Test +import java.io.File +import java.io.IOException +import java.net.URL +import java.time.LocalDateTime +import java.util.* + +/** + * The `Utils Test` class. + */ +class UtilsTest { + private val ascii = + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + private val cal = Calendar.getInstance() + private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) + + @BeforeClass + fun setUp() { + cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 + } + + @Test + fun testBold() { + Assertions.assertThat(bold(1.toString())).`as`("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + Assertions.assertThat(bold(ascii)).`as`("bold(ascii").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + } + + @Test + fun testColorize() { + Assertions.assertThat(colorize(ascii, Colors.REVERSE)).`as`("colorize(reverse)").isEqualTo( + Colors.REVERSE + ascii + Colors.REVERSE + ) + Assertions.assertThat(colorize(ascii, Colors.RED)).`as`("colorize(red)") + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + Assertions.assertThat(colorize(null, Colors.RED)).`as`("colorize(null)").isEqualTo(Colors.NORMAL) + } + + @Test + fun testCyan() { + Assertions.assertThat(cyan(ascii)).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) + } + + @Test + fun testEnsureDir() { + Assertions.assertThat(ensureDir("dir", false)).`as`("ensureDir(dir, false)") + .isEqualTo("dir" + File.separatorChar) + Assertions.assertThat(ensureDir("https://erik.thauvin.net", true)).`as`("ensureDir(erik.thauvin.net, true)") + .isEqualTo("https://erik.thauvin.net/") + } + + @Test + fun testGetIntProperty() { + Assertions.assertThat(getIntProperty("10", 1)).`as`("getIntProperty(10, 1)").isEqualTo(10) + Assertions.assertThat(getIntProperty("a", 1)).`as`("getIntProperty(a, 1)").isEqualTo(1) + } + + @Test + fun testGreen() { + Assertions.assertThat(green(ascii)).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) + } + + @Test + fun testIsoLocalDate() { + Assertions.assertThat(isoLocalDate(cal.time)).`as`("isoLocalDate(date)").isEqualTo("1952-02-17") + Assertions.assertThat(isoLocalDate(localDateTime)).`as`("isoLocalDate(localDate)").isEqualTo("1952-02-17") + } + + @Test + fun testObfuscate() { + Assertions.assertThat(obfuscate(ascii).length).`as`("obfuscate is right length").isEqualTo(ascii.length) + Assertions.assertThat(obfuscate(ascii)).`as`("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) + Assertions.assertThat(obfuscate(" ")).`as`("obfuscate(blank)").isEqualTo(" ") + } + + @Test + fun testPlural() { + val week = "week" + val weeks = "weeks" + Assertions.assertThat(plural(-1, week, weeks)).`as`("plural(-1)").isEqualTo(week) + Assertions.assertThat(plural(0, week, weeks)).`as`("plural(0)").isEqualTo(week) + Assertions.assertThat(plural(1, week, weeks)).`as`("plural(1)").isEqualTo(week) + Assertions.assertThat(plural(2, week, weeks)).`as`("plural(2)").isEqualTo(weeks) + } + + @Test + fun testReverseColor() { + Assertions.assertThat(reverseColor(ascii)).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) + } + + @Test + fun testToday() { + Assertions.assertThat(today()).isEqualTo(isoLocalDate(LocalDateTime.now())) + } + + @Test + fun testUnescapeXml() { + Assertions.assertThat(unescapeXml("<a name="test & ''">")).isEqualTo( + "<a name=\"test & ''\">" + ) + } + + @Test + fun testUptime() { + Assertions.assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(uptime(547800300076L)) + } + + @Test + @Throws(IOException::class) + fun testUrlReader() { + Assertions.assertThat(urlReader(URL("https://postman-echo.com/status/200"))).`as`("urlReader()").isEqualTo( + "{\"status\":200}" + ) + } + + @Test + fun testUtcDateTime() { + Assertions.assertThat(utcDateTime(cal.time)).`as`("utcDateTime(date)").isEqualTo("1952-02-17 12:30") + Assertions.assertThat(utcDateTime(localDateTime)).`as`("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") + } +} diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt index aad40e5..ed57c8e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -35,7 +35,6 @@ import net.thauvin.erik.mobibot.modules.Lookup.Companion.lookup import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois import org.assertj.core.api.Assertions import org.testng.annotations.Test -import java.util.* /** * The `Lookup Test` class. From b24c0ebe54d881cba45bbd545b37931e4befeecd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 6 Dec 2020 16:02:47 -0800 Subject: [PATCH 457/842] Version 0.8.0-beta+361 --- version.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.properties b/version.properties index 254cc73..0a176c6 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Dec 06 02:35:51 PST 2020 -version.buildmeta=350 +#Sun Dec 06 15:33:26 PST 2020 +version.buildmeta=361 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+350 +version.semver=0.8.0-beta+361 From f9edff48139b198ec7d21ac07736f798e1e071f9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 7 Dec 2020 01:33:59 -0800 Subject: [PATCH 458/842] Added addons.exec() and addons.match() functions. Converted TellMessagesMgr and EntriesMgr to objects. --- .../java/net/thauvin/erik/mobibot/Addons.kt | 33 ++ .../java/net/thauvin/erik/mobibot/Mobibot.kt | 72 +--- .../mobibot/commands/tell/TellMessagesMgr.kt | 78 ++-- .../erik/mobibot/entries/EntriesMgr.kt | 348 +++++++++--------- 4 files changed, 261 insertions(+), 270 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 1974aa4..04dfcd8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -87,6 +87,39 @@ class Addons { } } + /** + * Execute a command or module. + */ + fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean { + for (command in commands) { + if (command.name.startsWith(cmd)) { + command.commandResponse(sender, login, args, isOp, isPrivate) + return true + } + } + for (module in modules) { + if ((isPrivate && module.isPrivateMsgEnabled) || !isPrivate) { + if (module.commands.contains(cmd)) { + module.commandResponse(sender, cmd, args, isPrivate) + return true + } + } + } + return false + } + + /** + * Match a command. + */ + fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean) { + for (command in commands) { + if (command.matches(message)) { + command.commandResponse(sender, login, message, isOp, isPrivate) + break + } + } + } + /** * Sort commands and modules names. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index e6c1f8b..16c28c0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -58,9 +58,6 @@ import net.thauvin.erik.mobibot.commands.Users import net.thauvin.erik.mobibot.commands.Versions import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.LinksMgr -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.saveEntries -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startDate -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startup import net.thauvin.erik.mobibot.commands.links.Posting import net.thauvin.erik.mobibot.commands.links.Tags import net.thauvin.erik.mobibot.commands.links.View @@ -363,31 +360,12 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help helpResponse(sender, args, false) } else { - // Commands - for (command in addons.commands) { - if (command.isPublic && command.name.startsWith(cmd)) { - command.commandResponse(sender, login, args, isOp(sender), false) - return - } - } - // Modules - for (module in addons.modules) { // modules - for (c in module.commands) { - if (cmd.startsWith(c)) { - module.commandResponse(sender, cmd, args, false) - return - } - } - } + // Execute module or command + addons.exec(sender, login, cmd, args, isOp(sender), false) } } else { - // Commands, e.g.: https://www.example.com/ - for (command in addons.commands) { - if (command.matches(message)) { - command.commandResponse(sender, login, message, isOp(sender), false) - return - } - } + // Links, e.g.: https://www.example.com/ or L1: , etc. + addons.match(sender, login, message, isOp(sender), false) } storeRecap(sender, message, false) } @@ -420,23 +398,9 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert quitServer("The Bot Is Out There!") exitProcess(0) } else { - for (command in addons.commands) { - if (command.name.startsWith(cmd)) { - command.commandResponse(sender, login, args, isOp, true) - return - } + if (!addons.exec(sender, login, cmd, args, isOp, true)) { + helpDefault(sender, isOp, true) } - for (module in addons.modules) { - if (module.isPrivateMsgEnabled) { - for (c in module.commands) { - if (cmd == c) { - module.commandResponse(sender, cmd, args, true) - return - } - } - } - } - helpDefault(sender, isOp, true) } } @@ -600,16 +564,17 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert HelpFormatter().printHelp(Mobibot::class.java.name, options) } commandLine.hasOption(Constants.VERSION_ARG[0]) -> { + // Output the version println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${isoLocalDate(ReleaseInfo.BUILDDATE)})") println(ReleaseInfo.WEBSITE) } else -> { + // Load the properties val p = Properties() try { Files.newInputStream( Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties")) ).use { fis -> - // Load the properties files p.load(fis) } } catch (e: FileNotFoundException) { @@ -625,7 +590,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert val channel = p.getProperty("channel") val logsDir = ensureDir(p.getProperty("logs", "."), false) - // Redirect the stdout and stderr + // Redirect stdout and stderr if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { try { val stdout = PrintStream( @@ -680,15 +645,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert // Store the default logger level loggerLevel = logger.level - // Load the current entries and backlogs, if any - try { - startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) - if (logger.isDebugEnabled) logger.debug("Last feed: $startDate") - } catch (e: Exception) { - logger.error("An error occurred while loading the logs.", e) - } - - // Initialize the bot setVerbose(true) setAutoNickChange(true) login = p.getProperty("login", name) @@ -704,6 +660,14 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert weblogUrl = p.getProperty("weblog", "") backlogsUrl = ensureDir(p.getProperty("backlogs", weblogUrl), true) + // Load the current entries and backlogs, if any + try { + LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) + if (logger.isDebugEnabled) logger.debug("Last feed: ${LinksMgr.startDate}") + } catch (e: Exception) { + logger.error("An error occurred while loading the logs.", e) + } + // Set the pinboard authentication setPinboardAuth(p.getProperty("pinboard-api-token", "")) @@ -756,6 +720,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.sort() // Save the entries - saveEntries(this, true) + LinksMgr.saveEntries(this, true) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index 7a7e927..8bc0b18 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -46,53 +46,51 @@ import java.time.LocalDateTime /** * The Tell Messages Manager. */ -internal class TellMessagesMgr private constructor() { - companion object { - /** - * Cleans the messages queue. - */ - fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Int): Boolean { - val today = LocalDateTime.now(Clock.systemUTC()) - return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) } - } +internal object TellMessagesMgr { + /** + * Cleans the messages queue. + */ + fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Int): Boolean { + val today = LocalDateTime.now(Clock.systemUTC()) + return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) } + } - /** - * Loads the messages. - */ + /** + * Loads the messages. + */ - fun load(file: String, logger: Logger): List<TellMessage> { - try { - ObjectInputStream( - BufferedInputStream(Files.newInputStream(Paths.get(file))) - ).use { input -> - if (logger.isDebugEnabled) logger.debug("Loading the messages.") - @Suppress("UNCHECKED_CAST") - return input.readObject() as List<TellMessage> - } - } catch (ignore: FileNotFoundException) { - // Do nothing - } catch (e: IOException) { - logger.error("An IO error occurred loading the messages queue.", e) - } catch (e: ClassNotFoundException) { - logger.error("An error occurred loading the messages queue.", e) + fun load(file: String, logger: Logger): List<TellMessage> { + try { + ObjectInputStream( + BufferedInputStream(Files.newInputStream(Paths.get(file))) + ).use { input -> + if (logger.isDebugEnabled) logger.debug("Loading the messages.") + @Suppress("UNCHECKED_CAST") + return input.readObject() as List<TellMessage> } - return listOf() + } catch (ignore: FileNotFoundException) { + // Do nothing + } catch (e: IOException) { + logger.error("An IO error occurred loading the messages queue.", e) + } catch (e: ClassNotFoundException) { + logger.error("An error occurred loading the messages queue.", e) } + return listOf() + } - /** - * Saves the messages. - */ - fun save(file: String, messages: List<TellMessage?>?, logger: Logger) { - try { - BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> - ObjectOutputStream(bos).use { output -> - if (logger.isDebugEnabled) logger.debug("Saving the messages.") - output.writeObject(messages) - } + /** + * Saves the messages. + */ + fun save(file: String, messages: List<TellMessage?>?, logger: Logger) { + try { + BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> + ObjectOutputStream(bos).use { output -> + if (logger.isDebugEnabled) logger.debug("Saving the messages.") + output.writeObject(messages) } - } catch (e: IOException) { - logger.error("Unable to save messages queue.", e) } + } catch (e: IOException) { + logger.error("Unable to save messages queue.", e) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 57702c9..823d2ab 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -52,203 +52,199 @@ import java.util.* /** * Manages the feed entries. */ -class EntriesMgr private constructor() { +object EntriesMgr { + /** + * The name of the file containing the current entries. + */ + const val CURRENT_XML = "current.xml" + /** + * The name of the file containing the backlog entries. + */ + const val NAV_XML = "nav.xml" - companion object { - /** - * The name of the file containing the current entries. - */ - const val CURRENT_XML = "current.xml" + /** + * The .xml extension + */ + const val XML_EXT = ".xml" - /** - * The name of the file containing the backlog entries. - */ - const val NAV_XML = "nav.xml" + // Maximum number of backlogs to keep + private const val maxBacklogs = 10 - /** - * The .xml extension - */ - const val XML_EXT = ".xml" - - // Maximum number of backlogs to keep - private const val MAX_BACKLOGS = 10 - - /** - * Loads the backlogs. - */ - @Throws(IOException::class, FeedException::class) - fun loadBacklogs(file: String, history: MutableList<String>) { - history.clear() - val input = SyndFeedInput() - InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader -> - val feed = input.build(reader) - val items = feed.entries - for (i in items.indices.reversed()) { - history.add(items[i].title) - } + /** + * Loads the backlogs. + */ + @Throws(IOException::class, FeedException::class) + fun loadBacklogs(file: String, history: MutableList<String>) { + history.clear() + val input = SyndFeedInput() + InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader -> + val feed = input.build(reader) + val items = feed.entries + for (i in items.indices.reversed()) { + history.add(items[i].title) } } + } - /** - * Loads the current entries. - */ - @Throws(IOException::class, FeedException::class) - fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String { - entries.clear() - val input = SyndFeedInput() - var today: String - InputStreamReader( - Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8 - ).use { reader -> - val feed = input.build(reader) - today = isoLocalDate(feed.publishedDate) - val items = feed.entries - var entry: EntryLink - for (i in items.indices.reversed()) { - with(items[i]) { - entry = EntryLink( - link, - title, - author.substring(author.lastIndexOf('(') + 1, author.length - 1), - channel, - publishedDate, - categories - ) - var split: List<String> - for (comment in description.value.split("<br/>")) { - split = comment.split(": ".toRegex(), 2) - if (split.size == 2) { - entry.addComment(comment = split[1].trim(), nick = split[0].trim()) - } + /** + * Loads the current entries. + */ + @Throws(IOException::class, FeedException::class) + fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String { + entries.clear() + val input = SyndFeedInput() + var today: String + InputStreamReader( + Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8 + ).use { reader -> + val feed = input.build(reader) + today = isoLocalDate(feed.publishedDate) + val items = feed.entries + var entry: EntryLink + for (i in items.indices.reversed()) { + with(items[i]) { + entry = EntryLink( + link, + title, + author.substring(author.lastIndexOf('(') + 1, author.length - 1), + channel, + publishedDate, + categories + ) + var split: List<String> + for (comment in description.value.split("<br/>")) { + split = comment.split(": ".toRegex(), 2) + if (split.size == 2) { + entry.addComment(comment = split[1].trim(), nick = split[0].trim()) } } - entries.add(entry) } + entries.add(entry) } - return today } + return today + } - /** - * Saves the entries. - */ - fun saveEntries( - bot: Mobibot, - entries: List<EntryLink>, - history: MutableList<String>, - isDayBackup: Boolean - ) { - if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...") - if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { - try { - val output = SyndFeedOutput() - var rss: SyndFeed = SyndFeedImpl() - val items: MutableList<SyndEntry> = mutableListOf() - var item: SyndEntry - OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8 - ).use { fw -> - rss.apply { - feedType = "rss_2.0" - title = bot.channel + " IRC Links" - description = "Links from ${bot.ircServer} on ${bot.channel}" - link = bot.weblogUrl - publishedDate = Calendar.getInstance().time - language = "en" - } - var buff: StringBuilder - var comment: EntryComment - for (i in entries.size - 1 downTo 0) { - with(entries[i]) { - buff = StringBuilder() - .append("Posted by <b>") - .append(nick) - .append("</b> on <a href=\"irc://") - .append(bot.ircServer).append('/') - .append(channel) - .append("\"><b>") - .append(channel) - .append("</b></a>") - if (comments.size > 0) { - buff.append(" <br/><br/>") - val comments = comments - for (j in comments.indices) { - comment = comments[j] - if (j > 0) { - buff.append(" <br/>") - } - buff.append(comment.nick).append(": ").append(comment.comment) + /** + * Saves the entries. + */ + fun saveEntries( + bot: Mobibot, + entries: List<EntryLink>, + history: MutableList<String>, + isDayBackup: Boolean + ) { + if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...") + if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { + try { + val output = SyndFeedOutput() + var rss: SyndFeed = SyndFeedImpl() + val items: MutableList<SyndEntry> = mutableListOf() + var item: SyndEntry + OutputStreamWriter( + Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8 + ).use { fw -> + rss.apply { + feedType = "rss_2.0" + title = bot.channel + " IRC Links" + description = "Links from ${bot.ircServer} on ${bot.channel}" + link = bot.weblogUrl + publishedDate = Calendar.getInstance().time + language = "en" + } + var buff: StringBuilder + var comment: EntryComment + for (i in entries.size - 1 downTo 0) { + with(entries[i]) { + buff = StringBuilder() + .append("Posted by <b>") + .append(nick) + .append("</b> on <a href=\"irc://") + .append(bot.ircServer).append('/') + .append(channel) + .append("\"><b>") + .append(channel) + .append("</b></a>") + if (comments.size > 0) { + buff.append(" <br/><br/>") + val comments = comments + for (j in comments.indices) { + comment = comments[j] + if (j > 0) { + buff.append(" <br/>") } + buff.append(comment.nick).append(": ").append(comment.comment) } + } + item = SyndEntryImpl() + item.link = link + item.description = SyndContentImpl().apply { value = buff.toString() } + item.title = title + item.publishedDate = date + item.author = "${bot.channel.substring(1)}@${bot.ircServer} ($nick)" + item.categories = tags + items.add(item) + } + } + rss.entries = items + if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the entries feed.") + output.output(rss, fw) + } + OutputStreamWriter( + Files.newOutputStream( + Paths.get( + bot.logsDir + bot.today + XML_EXT + ) + ), StandardCharsets.UTF_8 + ).use { fw -> output.output(rss, fw) } + if (isDayBackup) { + if (bot.backlogsUrl.isNotBlank()) { + if (!history.contains(bot.today)) { + history.add(bot.today) + while (history.size > maxBacklogs) { + history.removeAt(0) + } + } + OutputStreamWriter( + Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8 + ).use { fw -> + rss = SyndFeedImpl() + rss.apply { + feedType = "rss_2.0" + title = "${bot.channel} IRC Links Backlogs" + description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}" + link = bot.backlogsUrl + publishedDate = Calendar.getInstance().time + } + var date: String + items.clear() + for (i in history.size - 1 downTo 0) { + date = history[i] item = SyndEntryImpl() - item.link = link - item.description = SyndContentImpl().apply { value = buff.toString() } - item.title = title - item.publishedDate = date - item.author = "${bot.channel.substring(1)}@${bot.ircServer} ($nick)" - item.categories = tags + item.apply { + link = bot.backlogsUrl + date + ".xml" + title = date + description = SyndContentImpl().apply { value = "Links for $date" } + } items.add(item) } + rss.entries = items + if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.") + output.output(rss, fw) } - rss.entries = items - if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the entries feed.") - output.output(rss, fw) + } else { + bot.logger.warn("Unable to generate the backlogs feed. No property configured.") } - OutputStreamWriter( - Files.newOutputStream( - Paths.get( - bot.logsDir + bot.today + XML_EXT - ) - ), StandardCharsets.UTF_8 - ).use { fw -> output.output(rss, fw) } - if (isDayBackup) { - if (bot.backlogsUrl.isNotBlank()) { - if (!history.contains(bot.today)) { - history.add(bot.today) - while (history.size > MAX_BACKLOGS) { - history.removeAt(0) - } - } - OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8 - ).use { fw -> - rss = SyndFeedImpl() - rss.apply { - feedType = "rss_2.0" - title = "${bot.channel} IRC Links Backlogs" - description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}" - link = bot.backlogsUrl - publishedDate = Calendar.getInstance().time - } - var date: String - items.clear() - for (i in history.size - 1 downTo 0) { - date = history[i] - item = SyndEntryImpl() - item.apply { - link = bot.backlogsUrl + date + ".xml" - title = date - description = SyndContentImpl().apply { value = "Links for $date" } - } - items.add(item) - } - rss.entries = items - if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.") - output.output(rss, fw) - } - } else { - bot.logger.warn("Unable to generate the backlogs feed. No property configured.") - } - } - } catch (e: FeedException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) - } catch (e: IOException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) - } - } else { - if (bot.logger.isWarnEnabled) { - bot.logger.warn("Unable to generate the entries feed. A required property is missing.") } + } catch (e: FeedException) { + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) + } catch (e: IOException) { + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) + } + } else { + if (bot.logger.isWarnEnabled) { + bot.logger.warn("Unable to generate the entries feed. A required property is missing.") } } } From a0530a8dc2e8b6befd6e1d87bd3c361cc9f7be6b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 7 Dec 2020 01:34:11 -0800 Subject: [PATCH 459/842] Cleanup. --- config/detekt/baseline.xml | 17 ++++++++--------- .../java/net/thauvin/erik/mobibot/FeedReader.kt | 10 ++++------ .../erik/mobibot/commands/links/LinksMgr.kt | 16 ---------------- .../java/net/thauvin/erik/mobibot/UtilsTest.kt | 4 ++-- 4 files changed, 14 insertions(+), 33 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index dc51202..ac048e7 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,13 +2,13 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> - <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>EmptyDefaultConstructor:Message.kt$Message$()</ID> - <ID>LongMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>LongMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>LongMethod:Mobibot.kt$Mobibot.Companion$ @JvmStatic fun main(args: Array<String>)</ID> <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>LongParameterList:Addons.kt$Addons$(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> @@ -22,6 +22,7 @@ <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID> <ID>MagicNumber:Cycle.kt$Cycle$10</ID> <ID>MagicNumber:Dice.kt$Dice$7</ID> + <ID>MagicNumber:FeedReader.kt$FeedReader$5</ID> <ID>MagicNumber:Ignore.kt$Ignore$8</ID> <ID>MagicNumber:Mobibot.kt$Mobibot$10</ID> <ID>MagicNumber:Mobibot.kt$Mobibot$1000L</ID> @@ -56,25 +57,23 @@ <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> - <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> - <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> + <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> <ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:Mobibot.kt$Mobibot$ fun connect()</ID> - <ID>NestedBlockDepth:Mobibot.kt$Mobibot$override fun onMessage( channel: String, sender: String, login: String, hostname: String, message: String )</ID> - <ID>NestedBlockDepth:Mobibot.kt$Mobibot$override fun onPrivateMessage( sender: String, login: String, hostname: String, message: String )</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID> <ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID> - <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr.Companion$ fun save(file: String, messages: List<TellMessage?>?, logger: Logger)</ID> + <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?, logger: Logger)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> - <ID>ReturnCount:Mobibot.kt$Mobibot$override fun onMessage( channel: String, sender: String, login: String, hostname: String, message: String )</ID> + <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt index a3f6247..d57d1b4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt @@ -47,6 +47,9 @@ class FeedReader( // URL to fetch private val url: String ) : Runnable { + // Maximum number of feed items to display + private val maxItems = 5 + /** * Fetches the Feed's items. */ @@ -61,7 +64,7 @@ class FeedReader( send(sender, "There is currently nothing to view.", false) } else { var i = 0 - while (i < items.size && i < MAX_ITEMS) { + while (i < items.size && i < maxItems) { send(sender, items[i].title, false) send(sender, Utils.helpIndent(Utils.green(items[i].link), false), false) i++ @@ -77,9 +80,4 @@ class FeedReader( } } } - - companion object { - // Maximum number of feed items to display - private const val MAX_ITEMS = 5 - } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index f246760..a3a3861 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -71,16 +71,10 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { @JvmField val history = mutableListOf<String>() - @JvmStatic var startDate: String = Utils.today() private set - // @JvmStatic - // fun addHistory(index: Int, entry: String) { - // history.add(index, entry) - // } - /** * Saves the entries. * @@ -91,16 +85,6 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { EntriesMgr.saveEntries(bot, entries, history, isDayBackup) } - // @JvmStatic - // fun removeEntry(index: Int) { - // entries.removeAt(index) - // } - // - // @JvmStatic - // fun getEntry(index: Int): EntryLink { - // return entries[index] - // } - @JvmStatic fun startup(current: String, backlogs: String, channel: String) { startDate = EntriesMgr.loadEntries(current, channel, entries) diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt index 6e2c269..fbe1ffb 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt @@ -73,8 +73,8 @@ class UtilsTest { @Test fun testBold() { - Assertions.assertThat(bold(1.toString())).`as`("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - Assertions.assertThat(bold(ascii)).`as`("bold(ascii").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + Assertions.assertThat(bold(1)).`as`("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + Assertions.assertThat(bold(ascii)).`as`("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) } @Test From d5634d7418dfb6730374ee73c29a7631fc46390e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 7 Dec 2020 01:34:32 -0800 Subject: [PATCH 460/842] Version 0.8.0-beta+372 --- version.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.properties b/version.properties index 0a176c6..8dcbee4 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Dec 06 15:33:26 PST 2020 -version.buildmeta=361 +#Mon Dec 07 01:27:15 PST 2020 +version.buildmeta=372 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+361 +version.semver=0.8.0-beta+372 From 7146bfcc60badadaa157e308e88e29d4fa873cb5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 01:34:02 -0800 Subject: [PATCH 461/842] Improved helpFormat() --- .../java/net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../java/net/thauvin/erik/mobibot/Mobibot.kt | 11 +++++------ src/main/java/net/thauvin/erik/mobibot/Utils.kt | 16 +++------------- .../net/thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Ignore.kt | 12 ++++++------ .../net/thauvin/erik/mobibot/commands/Info.java | 2 +- .../java/net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.java | 2 +- .../erik/mobibot/commands/links/Comment.kt | 8 ++++---- .../erik/mobibot/commands/links/LinksMgr.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 8 ++++---- .../thauvin/erik/mobibot/commands/links/Tags.kt | 2 +- .../thauvin/erik/mobibot/commands/links/View.kt | 2 +- .../thauvin/erik/mobibot/commands/tell/Tell.kt | 10 +++++----- .../erik/mobibot/modules/CurrencyConverter.kt | 8 ++++---- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../thauvin/erik/mobibot/modules/GoogleSearch.kt | 4 ++-- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../thauvin/erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Twitter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 2 +- .../net/thauvin/erik/mobibot/modules/Weather2.kt | 4 ++-- .../thauvin/erik/mobibot/modules/WorldTime.kt | 4 ++-- 32 files changed, 59 insertions(+), 70 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt index d57d1b4..23b124a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt @@ -66,7 +66,7 @@ class FeedReader( var i = 0 while (i < items.size && i < maxItems) { send(sender, items[i].title, false) - send(sender, Utils.helpIndent(Utils.green(items[i].link), false), false) + send(sender, Utils.helpFormat(Utils.green(items[i].link), false), false) i++ } } diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index 16c28c0..1900992 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -38,7 +38,7 @@ import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.ensureDir import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.helpIndent +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.isoLocalDate import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AddLog @@ -254,7 +254,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert send(sender, "For more information on a specific command, type:", isPrivate) send( sender, - helpIndent(helpFormat("%c ${Constants.HELP_CMD} <command>", nick, isPrivate)), + helpFormat(buildCmdSyntax("%c ${Constants.HELP_CMD} <command>", nick, isPrivate)), isPrivate ) send(sender, "The commands are:", isPrivate) @@ -468,11 +468,10 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert while (i < list.size) { send( nick, - helpIndent( + helpFormat( list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(" ", truncated = ""), - isBold - ), - isPrivate + isBold, + isIndent isPrivate ) i += maxPerLine } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index 1aa1829..b4b91f6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -138,22 +138,12 @@ object Utils { fun green(s: String?): String = colorize(s, Colors.DARK_GREEN) /** - * Formats a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's - * nick. - */ - @JvmStatic - fun helpFormat(text: String, botNick: String, isPrivate: Boolean): String { - val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) - return StringUtils.replaceEach(text, searchFlags, replace) - } - - /** - * Returns indented help string. + * Returns a formatted help string. */ @JvmStatic @JvmOverloads - fun helpIndent(help: String, isBold: Boolean = true): String { - return " " + if (isBold) bold(help) else help + fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { + return (if (isIndent) " " else "").plus(if (isBold) bold(help) else help) } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index 525ad33..49832f2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -38,7 +38,7 @@ import net.thauvin.erik.mobibot.Utils class Cycle(bot: Mobibot) : AbstractCommand(bot) { private val wait = 10 override val name = "cycle" - override val help = listOf("To have the bot leave the channel and come back:", Utils.helpIndent("%c $name")) + override val help = listOf("To have the bot leave the channel and come back:", Utils.helpFormat("%c $name")) override val isOp = true override val isPublic = false override val isVisible = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index de8d58b..10f8fe6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -47,17 +47,17 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { override val name = IGNORE_CMD override val help = listOf( "To ignore a link posted to the channel:", - Utils.helpIndent("https://www.foo.bar %n"), + Utils.helpFormat("https://www.foo.bar %n"), "To check your ignore status:", - Utils.helpIndent("%c $name"), + Utils.helpFormat("%c $name"), "To toggle your ignore status:", - Utils.helpIndent("%c $name $me") + Utils.helpFormat("%c $name $me") ) private val helpOp = listOf( "To ignore a link posted to the channel:", - Utils.helpIndent("https://www.foo.bar " + Utils.bold("%n"), false), + Utils.helpFormat("https://www.foo.bar " + Utils.bold("%n"), false), "To add/remove nicks from the ignored list:", - Utils.helpIndent("%c $name <nick> [<nick> ...]") + Utils.helpFormat("%c $name <nick> [<nick> ...]") ) override val isOp = false @@ -99,7 +99,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { ): Boolean { return if (isOp) { for (h in helpOp) { - bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivate), isPrivate) + bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) } true } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index e9c9975..d16bd92 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -61,7 +61,7 @@ public class Info extends AbstractCommand { @NotNull @Override public List<String> getHelp() { - return List.of("To view information about the bot:", Utils.helpIndent("%c " + getName())); + return List.of("To view information about the bot:", Utils.helpFormat("%c " + getName())); } @Override diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt index 4b27cd7..fa0861b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt @@ -39,7 +39,7 @@ class Me(bot: Mobibot) : AbstractCommand(bot) { override val name = "me" override val help = listOf( "To have the bot perform an action:", - Utils.helpIndent("%c $name <action>") + Utils.helpFormat("%c $name <action>") ) override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index 447552c..f3f3960 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -39,7 +39,7 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) { override val name = "modules" override val help = listOf( "To view a list of enabled modules:", - Utils.helpIndent("%c $name") + Utils.helpFormat("%c $name") ) override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt index 37c50df..31693c6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt @@ -39,7 +39,7 @@ class Msg(bot: Mobibot) : AbstractCommand(bot) { override val name = "msg" override val help = listOf( "To have the bot send a private message to someone:", - Utils.helpIndent("%c $name <nick> <text>") + Utils.helpFormat("%c $name <nick> <text>") ) override val isOp = true override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt index 6d12d2f..c6c7d2c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt @@ -39,7 +39,7 @@ class Nick(bot: Mobibot) : AbstractCommand(bot) { override val name = "nick" override val help = listOf( "To change the bot's nickname:", - Utils.helpIndent("%c $name <nick>") + Utils.helpFormat("%c $name <nick>") ) override val isOp = true override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt index e6341f8..57477ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -41,7 +41,7 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { override val name = "recap" override val help = listOf( "To list the last 10 public channel messages:", - Utils.helpIndent("%c $name") + Utils.helpFormat("%c $name") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt index be8d6f0..7651d9d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt @@ -39,7 +39,7 @@ class Say(bot: Mobibot) : AbstractCommand(bot) { override val name = "say" override val help = listOf( "To have the bot say something on the channel:", - Utils.helpIndent("%c $name <text>") + Utils.helpFormat("%c $name <text>") ) override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index 0315560..5afdf5a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -40,7 +40,7 @@ class Users(bot: Mobibot) : AbstractCommand(bot) { override val name = "users" override val help = listOf( "To list the users present on the channel:", - Utils.helpIndent("%c $name") + Utils.helpFormat("%c $name") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 4b08631..8a2f168 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -61,7 +61,7 @@ public class Versions extends AbstractCommand { @NotNull @Override public List<String> getHelp() { - return List.of("To view the versions data (bot, platform, java, etc.):", Utils.helpIndent("%c " + getName())); + return List.of("To view the versions data (bot, platform, java, etc.):", Utils.helpFormat("%c " + getName())); } @Override diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 65941cf..35b644c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -43,12 +43,12 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { override val name = COMMAND override val help = listOf( "To add a comment:", - Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), + Utils.helpFormat("${Constants.LINK_CMD}1:This is a comment"), "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", "To edit a comment, use its label: ", - Utils.helpIndent("${Constants.LINK_CMD}1.1:This is an edited comment"), + Utils.helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), "To delete a comment, use its label and a minus sign: ", - Utils.helpIndent("${Constants.LINK_CMD}1.1:-") + Utils.helpFormat("${Constants.LINK_CMD}1.1:-") ) override val isOp = false override val isPublic = true @@ -98,7 +98,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { bot.send(sender, "To change a comment's author:", isPrivate) bot.send( sender, - Utils.helpIndent("${Constants.LINK_CMD}1.1:?<nick>"), + Utils.helpFormat("${Constants.LINK_CMD}1.1:?<nick>"), isPrivate ) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index a3a3861..374b9d5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -144,7 +144,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { bot.send(sender, "Please specify a title, by typing:", isPrivate) bot.send( sender, - Utils.helpIndent("${EntriesUtils.buildLinkCmd(index)}:|This is the title"), + Utils.helpFormat("${EntriesUtils.buildLinkCmd(index)}:|This is the title"), isPrivate ) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 474108f..5582fbb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -44,15 +44,15 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { override val name = "posting" override val help = listOf( "Post a URL, by saying it on a line on its own:", - Utils.helpIndent("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + Utils.helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1", "To add a title, use its label and a pipe:", - Utils.helpIndent("${Constants.LINK_CMD}1:|This is the title"), + Utils.helpFormat("${Constants.LINK_CMD}1:|This is the title"), "To add a comment:", - Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"), + Utils.helpFormat("${Constants.LINK_CMD}1:This is a comment"), "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", "To edit a comment, see: ", - Utils.helpIndent("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + Utils.helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index 9a9a73e..e07fe21 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -43,7 +43,7 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { override val name = COMMAND override val help = listOf( "To categorize or tag a URL, use its label and a T:", - Utils.helpIndent("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") + Utils.helpFormat("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index dbafa4a..0ea0708 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -44,7 +44,7 @@ class View(bot: Mobibot) : AbstractCommand(bot) { override val name = VIEW_CMD override val help = listOf( "To list or search the current URL posts:", - Utils.helpIndent("%c $name [<start>] [<query>]") + Utils.helpFormat("%c $name [<start>] [<query>]") ) override val isOp = false override val isPublic = true diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index be54085..af6a603 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -118,9 +118,9 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { override val help: List<String> get() = listOf( "To send a message to someone when they join the channel:", - helpIndent("%c $name <nick> <message>"), + helpFormat("%c $name <nick> <message>"), "To view queued and sent messages:", - helpIndent("%c $name ${View.VIEW_CMD}"), + helpFormat("%c $name ${View.VIEW_CMD}"), "Messages are kept for " + bold(maxDays) + plural(maxDays.toLong(), " day.", " days.") ) @@ -293,7 +293,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { isPrivate ) } - bot.send(sender, helpIndent(message.message), isPrivate) + bot.send(sender, helpFormat(message.message), isPrivate) } } if (!hasMessage) { @@ -302,8 +302,8 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { bot.send(sender, "To delete one or all delivered messages:", isPrivate) bot.send( sender, - helpIndent( - helpFormat( + helpFormat( + buildCmdSyntax( "%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", bot.nick, isPrivate diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index d02e5d6..ded4a6b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -108,16 +108,16 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { send(sender, "To convert from one currency to another:", isPrivate) send( sender, - Utils.helpIndent( - Utils.helpFormat("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled) + Utils.helpFormat( + Utils.buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled) ), isPrivate ) send(sender, "For a listing of current rates:", isPrivate) send( sender, - Utils.helpIndent( - Utils.helpFormat("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) + Utils.helpFormat( + Utils.buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) ), isPrivate ) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt index 4f6a314..fb20e9d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt @@ -98,6 +98,6 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { init { commands.add(DICE_CMD) help.add("To roll the dice:") - help.add(Utils.helpIndent("%c $DICE_CMD")) + help.add(Utils.helpFormat("%c $DICE_CMD")) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 687ce17..bbaec32 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -101,7 +101,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { for (i in 0 until ja.length()) { val j = ja.getJSONObject(i) results.add(NoticeMessage(Utils.unescapeXml(j.getString("title")))) - results.add(NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN)) + results.add(NoticeMessage(Utils.helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) } } catch (e: IOException) { throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e) @@ -118,7 +118,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(GOOGLE_CMD) help.add("To search Google:") - help.add(Utils.helpIndent("%c $GOOGLE_CMD <query>")) + help.add(Utils.helpFormat("%c $GOOGLE_CMD <query>")) initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt index bc760b2..9743441 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt @@ -97,6 +97,6 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(JOKE_CMD) help.add("To retrieve a random joke:") - help.add(Utils.helpIndent("%c $JOKE_CMD")) + help.add(Utils.helpFormat("%c $JOKE_CMD")) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt index 32557bd..71a3037 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -164,6 +164,6 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { init { commands.add(LOOKUP_CMD) help.add("To perform a DNS lookup query:") - help.add(Utils.helpIndent("%c $LOOKUP_CMD <ip address or hostname>")) + help.add(Utils.helpFormat("%c $LOOKUP_CMD <ip address or hostname>")) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt index 980ece9..470a453 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt @@ -85,6 +85,6 @@ class Ping(bot: Mobibot) : AbstractModule(bot) { init { commands.add(PING_CMD) help.add("To ping the bot:") - help.add(Utils.helpIndent("%c $PING_CMD")) + help.add(Utils.helpFormat("%c $PING_CMD")) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 9bcc1ad..e22496a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -51,7 +51,7 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { with(help) { add("To play Rock Paper Scissors:") add( - Utils.helpIndent( + Utils.helpFormat( "%c ${Hands.ROCK.name.toLowerCase()} | ${Hands.PAPER.name.toLowerCase()}" + " | ${Hands.SCISSORS.name.toLowerCase()}" ) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index 5e4ae34..a43eca2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -206,7 +206,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(STOCK_CMD) help.add("To retrieve a stock quote:") - help.add(Utils.helpIndent("%c $STOCK_CMD <symbol|keywords>")) + help.add(Utils.helpFormat("%c $STOCK_CMD <symbol|keywords>")) initProperties(ALPHAVANTAGE_API_KEY_PROP) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index 9a7ac28..beb3793 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -234,7 +234,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(TWITTER_CMD) help.add("To post to Twitter:") - help.add(Utils.helpIndent("%c $TWITTER_CMD <message>")) + help.add(Utils.helpFormat("%c $TWITTER_CMD <message>")) properties[AUTOPOST_PROP] = "false" initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index fe43ef5..b239e87 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -65,7 +65,7 @@ public final class War extends AbstractModule { commands.add(WAR_CMD); help.add("To play war:"); - help.add(Utils.helpIndent("%c " + WAR_CMD)); + help.add(Utils.helpFormat("%c " + WAR_CMD)); } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index ab2e385..a6133b4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -191,9 +191,9 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(WEATHER_CMD) help.add("To display weather information:") - help.add(Utils.helpIndent("%c $WEATHER_CMD <city> [, <country code>]")) + help.add(Utils.helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) help.add("For example:") - help.add(Utils.helpIndent("%c $WEATHER_CMD paris, fr")) + help.add(Utils.helpFormat("%c $WEATHER_CMD paris, fr")) help.add("The default ISO 3166 country code is ${Utils.bold("US")}. Zip codes supported in most countries.") initProperties(OWM_API_KEY_PROP) } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index 3338439..7695b0b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -211,9 +211,9 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { init { help.add("To display a country's current date/time:") - help.add(Utils.helpIndent("%c $TIME_CMD") + " [<country code>]") + help.add(Utils.helpFormat("%c $TIME_CMD") + " [<country code>]") help.add("For a listing of the supported countries:") - help.add(Utils.helpIndent("%c $TIME_CMD")) + help.add(Utils.helpFormat("%c $TIME_CMD")) commands.add(TIME_CMD) } } From e01de36909f70512c6ad9e71ebe76e1983b42f33 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 01:39:19 -0800 Subject: [PATCH 462/842] Improved sendList() --- src/main/java/net/thauvin/erik/mobibot/Mobibot.kt | 10 ++++------ .../thauvin/erik/mobibot/commands/AbstractCommand.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/AddLog.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Info.java | 4 +--- .../java/net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Users.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Versions.java | 6 ++---- .../net/thauvin/erik/mobibot/modules/AbstractModule.kt | 2 +- .../thauvin/erik/mobibot/modules/CurrencyConverter.kt | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- 11 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index 1900992..7028c22 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -258,10 +258,10 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert isPrivate ) send(sender, "The commands are:", isPrivate) - sendList(sender, addons.names, 8, isPrivate, true) + sendList(sender, addons.names, 8, isPrivate, isBold = true, isIndent = true) if (isOp) { send(sender, "The op commands are:", isPrivate) - sendList(sender, addons.ops, 8, isPrivate, true) + sendList(sender, addons.ops, 8, isPrivate, isBold = true, isIndent = true) } } @@ -462,10 +462,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert list: List<String>, maxPerLine: Int, isPrivate: Boolean, - isBold: Boolean - ) { - var i = 0 - while (i < list.size) { + isBold: Boolean = false, + isIndent: Boolean = false while (i < list.size) { send( nick, helpFormat( diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 3168dde..862ca6a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -56,7 +56,7 @@ abstract class AbstractCommand(val bot: Mobibot) { open fun helpResponse(command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { if (!this.isOp || this.isOp == isOp) { for (h in help) { - bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivate), isPrivate) + bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) } return true } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt index 2848369..7f7f567 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -63,7 +63,7 @@ class AddLog(bot: Mobibot) : AbstractCommand(bot) { return } } - bot.sendList(sender, history, 4, isPrivate, false) + bot.sendList(sender, history, 4, isPrivate, isIndent = true) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 10f8fe6..91ea685 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -141,7 +141,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { if (ignored.size > 0) { bot.send(sender, "The following nicks are ignored:", isPrivate) - bot.sendList(sender, ignored.toList(), 8, isPrivate, true) + bot.sendList(sender, ignored.sorted(), 8, isPrivate, isIndent = true) } else { bot.send(sender, "No one is currently ${Utils.bold("ignored")}.", isPrivate) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index d16bd92..a9bf07b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -85,9 +85,7 @@ public class Info extends AbstractCommand { @NotNull final String args, final boolean isOp, final boolean isPrivate) { - for (final String v : version) { - getBot().send(sender, v, isPrivate); - } + getBot().sendList(sender, allVersions, 1, isPrivate, false, false); final StringBuilder info = new StringBuilder("Uptime: "); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index f3f3960..a9493fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -58,7 +58,7 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) { send(sender, "There are no enabled modules.", isPrivate) } else { send(sender, "The enabled modules are: ", isPrivate) - sendList(sender, modulesNames, 7, isPrivate, false) + sendList(sender, modulesNames, 7, isPrivate, isIndent = true) } } else { helpDefault(sender, isOp, isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index 5afdf5a..e3a868a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -64,6 +64,6 @@ class Users(bot: Mobibot) : AbstractCommand(bot) { } } - bot.sendList(sender, nicks.sorted(), 8, isPrivate, false) + bot.sendList(sender, nicks.sorted(), 8, isPrivate, isIndent = true) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 8a2f168..662d073 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -40,7 +40,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; public class Versions extends AbstractCommand { - private final List<String> verList = + private final List<String> allVersions = List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + " (" + System.getProperty("os.arch") + ')', @@ -86,9 +86,7 @@ public class Versions extends AbstractCommand { final boolean isOp, final boolean isPrivate) { if (isOp) { - for (final String v : verList) { - getBot().send(sender, v, isPrivate); - } + getBot().sendList(sender, allVersions, 1, isPrivate, false, false); } else { getBot().helpDefault(sender, false, isPrivate); } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt index eb97e4f..695549d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -76,7 +76,7 @@ abstract class AbstractModule(val bot: Mobibot) { */ open fun helpResponse(sender: String, isPrivate: Boolean) { for (h in help) { - bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) + bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index ded4a6b..279f748 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -86,7 +86,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { } } else if (args.contains(CURRENCY_RATES_KEYWORD)) { send(sender, "The currency rates for ${Utils.bold(pubDate)} are:", isPrivate) - sendList(sender, currencyRates(), 3, isPrivate, false) + sendList(sender, currencyRates(), 3, isPrivate, isIndent = true) } else { helpResponse(sender, isPrivate) } @@ -122,7 +122,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { isPrivate ) send(sender, "The supported currencies are: ", isPrivate) - sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, false) + sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, isIndent = true) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index 7695b0b..ed7ad74 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -191,7 +191,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { with(bot) { if (args.isEmpty()) { send(sender, "The supported countries/zones are: ", isPrivate) - sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false, isBold = false) + sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false) } else { val msg = worldTime(args) if (isPrivate) { From 629e26b2925a412ed32c8dcec8659df37081b7c6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 01:40:09 -0800 Subject: [PATCH 463/842] Cleanup. --- config/detekt/baseline.xml | 1 + .../java/net/thauvin/erik/mobibot/Addons.kt | 9 +++-- .../java/net/thauvin/erik/mobibot/Mobibot.kt | 15 ++++--- .../java/net/thauvin/erik/mobibot/Utils.kt | 40 +++++++++++++------ .../erik/mobibot/commands/AbstractCommand.kt | 14 +------ .../erik/mobibot/commands/ChannelFeed.kt | 4 +- .../thauvin/erik/mobibot/commands/Debug.kt | 3 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 5 +-- .../thauvin/erik/mobibot/commands/Info.java | 4 +- .../thauvin/erik/mobibot/commands/Recap.kt | 12 +----- .../thauvin/erik/mobibot/commands/Users.kt | 4 +- .../erik/mobibot/commands/links/LinksMgr.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 4 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 10 ++--- .../erik/mobibot/commands/tell/TellMessage.kt | 8 ---- .../mobibot/commands/tell/TellMessagesMgr.kt | 6 +-- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 14 +------ .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 8 ---- .../mobibot/commands/tell/TellMessageTest.kt | 1 - .../erik/mobibot/entries/EntryLinkTest.kt | 4 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- 25 files changed, 75 insertions(+), 103 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index ac048e7..03d17a4 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -12,6 +12,7 @@ <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> + <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>MagicNumber:AddLog.kt$AddLog$4</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 04dfcd8..9c8e769 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -69,8 +69,8 @@ class Addons { */ fun add(command: AbstractCommand, props: Properties) { with(command) { - if (hasProperties()) { - getPropertyKeys().forEach { + if (properties.isNotEmpty()) { + properties.keys.forEach { setProperty(it, props.getProperty(it, "")) } } @@ -111,13 +111,14 @@ class Addons { /** * Match a command. */ - fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean) { + fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean): Boolean { for (command in commands) { if (command.matches(message)) { command.commandResponse(sender, login, message, isOp, isPrivate) - break + return true } } + return false } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index 7028c22..ca7caa6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -397,10 +397,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert sleep(3) quitServer("The Bot Is Out There!") exitProcess(0) - } else { - if (!addons.exec(sender, login, cmd, args, isOp, true)) { - helpDefault(sender, isOp, true) - } + } else if (!addons.exec(sender, login, cmd, args, isOp, true)) { // Execute command or module + helpDefault(sender, isOp, true) } } @@ -463,13 +461,18 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, - isIndent: Boolean = false while (i < list.size) { + isIndent: Boolean = false + ) { + var i = 0 + while (i < list.size) { send( nick, helpFormat( list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(" ", truncated = ""), isBold, - isIndent isPrivate + isIndent + ), + isPrivate ) i += maxPerLine } diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index b4b91f6..7bf1580 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -38,6 +38,7 @@ import java.io.BufferedReader import java.io.File import java.io.IOException import java.io.InputStreamReader +import java.lang.NumberFormatException import java.net.URL import java.net.URLEncoder import java.nio.charset.StandardCharsets @@ -60,12 +61,28 @@ object Utils { @JvmStatic fun bold(i: Int): String = bold(i.toString()) + /** + * Makes the given long bold. + */ + @JvmStatic + fun bold(i: Long): String = bold(i.toString()) + /** * Makes the given string bold. */ @JvmStatic fun bold(s: String?): String = colorize(s, Colors.BOLD) + /** + * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's + * nick. + */ + @JvmStatic + fun buildCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { + val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) + return StringUtils.replaceEach(text, searchFlags, replace) + } + /** * Colorize a string. */ @@ -89,6 +106,7 @@ object Utils { /** * URL encodes the given string. */ + @JvmStatic fun encodeUrl(s: String): String = URLEncoder.encode(s, StandardCharsets.UTF_8) /** @@ -120,14 +138,10 @@ object Utils { */ @JvmStatic fun getIntProperty(property: String?, def: Int): Int { - return if (property == null) { + return try { + property?.toInt() ?: def + } catch (nfe: NumberFormatException) { def - } else { - try { - property.toInt() - } catch (ignore: NumberFormatException) { - def - } } } @@ -170,6 +184,12 @@ object Utils { } else s } + /** + * Returns the plural form of a word, if count > 1. + */ + @JvmStatic + fun plural(count: Int, word: String, plural: String): String = plural(count.toLong(), word, plural) + /** * Returns the plural form of a word, if count > 1. */ @@ -269,10 +289,6 @@ object Utils { */ @JvmStatic fun utcDateTime(date: LocalDateTime?): String { - return if (date != null) { - date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) - } else { - "" - } + return date?.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) ?: "" } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 862ca6a..338fbd6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -43,7 +43,7 @@ abstract class AbstractCommand(val bot: Mobibot) { abstract val isPublic: Boolean abstract val isVisible: Boolean - private val properties: MutableMap<String, String> = ConcurrentHashMap() + val properties: MutableMap<String, String> = ConcurrentHashMap() abstract fun commandResponse( sender: String, @@ -63,18 +63,6 @@ abstract class AbstractCommand(val bot: Mobibot) { return false } - open fun getProperty(key: String): String? { - return properties[key] - } - - open fun getPropertyKeys(): Set<String> { - return properties.keys - } - - open fun hasProperties(): Boolean { - return properties.isNotEmpty() - } - open fun initProperties(vararg keys: String) { keys.forEach { properties[it] = "" diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index f371422..3eee724 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -40,7 +40,7 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { override val name = channel override val help = listOf( "To list the last 5 posts from the channel's weblog feed:", - Utils.helpIndent("%c $channel") + Utils.helpFormat("%c $channel") ) override val isOp = false override val isPublic = true @@ -61,7 +61,7 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - with(getProperty(FEED_PROP)) { + with(properties[FEED_PROP]) { if (!isNullOrBlank()) { Thread(FeedReader(bot, sender, this)).start() } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt index 9bf3630..fa6b462 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt @@ -32,12 +32,13 @@ package net.thauvin.erik.mobibot.commands +import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import org.apache.logging.log4j.Level import org.apache.logging.log4j.core.config.Configurator class Debug(bot: Mobibot) : AbstractCommand(bot) { - override val name = "debug" + override val name = Constants.DEBUG_CMD override val help = emptyList<String>() override val isOp = true override val isPublic = false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 91ea685..1187286 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -35,7 +35,6 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.commands.links.LinksMgr -import java.util.* class Ignore(bot: Mobibot) : AbstractCommand(bot) { private val me = "me" @@ -67,7 +66,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { companion object { const val IGNORE_CMD = "ignore" const val IGNORE_PROP = IGNORE_CMD - private val ignored = TreeSet<String>() + private val ignored = mutableSetOf<String>() @JvmStatic fun isNotIgnored(nick: String): Boolean { @@ -150,7 +149,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { override fun setProperty(key: String, value: String) { super.setProperty(key, value) if (IGNORE_PROP == key) { - ignored.addAll(value.split(LinksMgr.LINK_MATCH.toRegex())) + ignored.addAll(value.split(LinksMgr.TAG_MATCH.toRegex())) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index a9bf07b..09043aa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -43,7 +43,7 @@ import java.lang.management.ManagementFactory; import java.util.List; public class Info extends AbstractCommand { - private final List<String> version = List.of( + private final List<String> allVersions = List.of( StringUtils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION + " (" + Utils.green(ReleaseInfo.WEBSITE) + ')', "Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')'); @@ -102,7 +102,7 @@ public class Info extends AbstractCommand { } } - info.append(", Recap: ").append(Recap.recapCount()).append(']'); + info.append(", Recap: ").append(Recap.recaps.size()).append(']'); getBot().send(sender, info.toString(), isPrivate); } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt index 57477ef..d695e46 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -48,19 +48,11 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { override val isVisible = true companion object { - private val recaps = mutableListOf<String>() - - @JvmStatic - fun recapCount(): Int { - return recaps.size - } + @JvmField + val recaps = mutableListOf<String>() /** * Stores the last 10 public messages and actions. - * - * @param sender The nick of the person who sent the private message. - * @param message The actual message sent. - * @param isAction Set to `true` if the message is an action. */ @JvmStatic fun storeRecap(sender: String, message: String, isAction: Boolean) { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index e3a868a..9fab49e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import org.jibble.pircbot.User class Users(bot: Mobibot) : AbstractCommand(bot) { override val name = "users" @@ -54,9 +53,8 @@ class Users(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - val users: Array<User> = bot.getUsers(bot.channel) val nicks = mutableListOf<String>() - users.forEach { user -> + bot.getUsers(bot.channel).forEach { user -> if (bot.isOp(user.nick)) { nicks.add("@${user.nick}") } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 374b9d5..80b4fbc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -89,7 +89,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { fun startup(current: String, backlogs: String, channel: String) { startDate = EntriesMgr.loadEntries(current, channel, entries) if (Utils.today() != startDate) { - this.entries.clear() + entries.clear() startDate = Utils.today() } EntriesMgr.loadBacklogs(backlogs, history) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 5582fbb..12a3f8d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -148,10 +148,10 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { private fun showEntry(index: Int) { val entry: EntryLink = entries[index] bot.send(EntriesUtils.buildLink(index, entry)) - if (entry.hasTags()) { + if (entry.tags.isNotEmpty()) { bot.send(EntriesUtils.buildTags(index, entry)) } - if (entry.hasComments()) { + if (entry.comments.isNotEmpty()) { val comments = entry.comments for (i in comments.indices) { bot.send(EntriesUtils.buildComment(index, i, comments[i])) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index e07fe21..14ae0d2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -76,7 +76,7 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { bot.send(sender, "Please ask a channel op to change the tags for you.", isPrivate) } } else { - if (entry.hasTags()) { + if (entry.tags.isNotEmpty()) { bot.send(EntriesUtils.buildTags(index, entry)) } else { bot.send(sender, "The entry has no tags. Why don't add some?", isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index af6a603..5627285 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -35,7 +35,7 @@ import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.helpIndent +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.reverseColor import net.thauvin.erik.mobibot.Utils.utcDateTime @@ -63,7 +63,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { */ private fun clean(): Boolean { if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.") - return TellMessagesMgr.clean(messages, maxDays) + return TellMessagesMgr.clean(messages, maxDays.toLong()) } // Delete message. @@ -88,7 +88,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { } else { var found = false for (message in messages) { - found = message.isMatchId(id) + found = (message.id == id) if (found && (message.sender.equals(sender, ignoreCase = true) || bot.isOp(sender))) { messages.remove(message) save() @@ -122,7 +122,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { "To view queued and sent messages:", helpFormat("%c $name ${View.VIEW_CMD}"), "Messages are kept for " + bold(maxDays) - + plural(maxDays.toLong(), " day.", " days.") + + plural(maxDays, " day.", " days.") ) override val isOp: Boolean get() = false @@ -313,7 +313,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { ) bot.send( sender, - "Messages are kept for ${bold(maxDays)}${plural(maxDays.toLong(), " day.", " days.")}", + "Messages are kept for ${bold(maxDays)}${plural(maxDays, " day.", " days.")}", isPrivate ) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 09dc5da..396b854 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -86,7 +86,6 @@ class TellMessage internal constructor( */ var receptionDate: LocalDateTime = LocalDateTime.MIN - /** * Matches the message sender or recipient. */ @@ -94,13 +93,6 @@ class TellMessage internal constructor( return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true) } - /** - * Match the message ID. - */ - fun isMatchId(id: String): Boolean { - return this.id == id - } - override fun toString(): String { return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " + "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index 8bc0b18..92e0e32 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -46,13 +46,13 @@ import java.time.LocalDateTime /** * The Tell Messages Manager. */ -internal object TellMessagesMgr { +object TellMessagesMgr { /** * Cleans the messages queue. */ - fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Int): Boolean { + fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean { val today = LocalDateTime.now(Clock.systemUTC()) - return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) } + return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) } } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index d9ca531..5f83aa2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -57,7 +57,7 @@ object EntriesUtils { fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ") .append('[').append(entry.nick).append(']') - if (isView && entry.hasComments()) { + if (isView && entry.comments.isNotEmpty()) { buff.append("[+").append(entry.comments.size).append(']') } buff.append(' ') diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt index a33a180..b676591 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -107,8 +107,8 @@ class EntryLink : Serializable { /** * Adds a new comment. */ - fun addComment(comment: String?, nick: String?): Int { - comments.add(EntryComment(comment!!, nick!!)) + fun addComment(comment: String, nick: String): Int { + comments.add(EntryComment(comment, nick)) return comments.size - 1 } @@ -139,16 +139,6 @@ class EntryLink : Serializable { return pinboardTags.toString() } - /** - * Returns true if the entry has comments. - */ - fun hasComments(): Boolean = comments.isNotEmpty() - - /** - * Returns true if the entry has tags. - */ - fun hasTags(): Boolean = tags.isNotEmpty() - /** * Returns true if a string is contained in the link, title, or nick. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt index b1ea6a2..5795480 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt @@ -75,6 +75,6 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { init { commands.add(CALC_CMD) help.add("To solve a mathematical calculation:") - help.add(Utils.helpIndent("%c $CALC_CMD <calculation>")) + help.add(Utils.helpFormat("%c $CALC_CMD <calculation>")) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt index f9a3d81..3e77d96 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -80,14 +80,6 @@ class ModuleException : Exception { } } - /** - * Return `true` if the exception has a cause. - */ - @Suppress("unused") - fun hasCause(): Boolean { - return cause != null - } - companion object { private const val serialVersionUID = 1L } diff --git a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 75a1139..57cdb46 100644 --- a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -58,7 +58,6 @@ class TellMessageTest { Assertions.assertThat(tellMessage.isMatch(sender)).`as`("match sender").isTrue Assertions.assertThat(tellMessage.isMatch(recipient)).`as`("match recipient").isTrue Assertions.assertThat(tellMessage.isMatch("foo")).`as`("foo is no match").isFalse - Assertions.assertThat(tellMessage.isMatchId(tellMessage.id)).`as`("is match ID").isTrue tellMessage.isReceived = true Assertions.assertThat(tellMessage.isReceived).`as`("is received").isTrue Assertions.assertThat(isValidDate(tellMessage.receptionDate)).`as`("received is valid date/time").isTrue diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index d92b58f..7e710f0 100644 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -67,7 +67,7 @@ class EntryLinkTest { while (entryLink.comments.size > 0) { entryLink.deleteComment(r.nextInt(entryLink.comments.size)) } - Assertions.assertThat(entryLink.hasComments()).`as`("hasComments()").isFalse + Assertions.assertThat(entryLink.comments.isNotEmpty()).`as`("hasComments()").isFalse entryLink.addComment("nothing", "nobody") entryLink.setComment(0, "something", "somebody") Assertions.assertThat(entryLink.getComment(0).nick).`as`("getNick(somebody)").isEqualTo("somebody") @@ -81,7 +81,7 @@ class EntryLinkTest { Assertions.assertThat(tag.name).`as`("tag.getName($i)").isEqualTo("tag" + (i + 1)) } Assertions.assertThat(entryLink.tags.size).`as`("getTags().size() is 5").isEqualTo(5) - Assertions.assertThat(entryLink.hasTags()).`as`("hasTags() is true").isTrue + Assertions.assertThat(entryLink.tags.isNotEmpty()).`as`("hasTags() is true").isTrue entryLink.setTags("-tag5") entryLink.setTags("+mobitopia") entryLink.setTags("tag4") diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 8592447..6797682 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -62,7 +62,7 @@ class GoogleSearchTest : LocalProperties() { .`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI") && !apiKey.isBlank() && !cseKey.isBlank()) { + if ("true" == System.getenv("CI") && apiKey.isNotBlank() && cseKey.isNotBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey)) } else { throw e diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index c47509b..08f12db 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -63,7 +63,7 @@ class StockQuoteTest : LocalProperties() { .isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI") && !apiKey.isBlank()) { + if ("true" == System.getenv("CI") && apiKey.isNotBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey)) } else { throw e From 4b52ab8c283c7928c217b8ead23c75f45c984521 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 01:40:29 -0800 Subject: [PATCH 464/842] Version 0.8.0-beta+399 --- version.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.properties b/version.properties index 8dcbee4..00ef133 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Dec 07 01:27:15 PST 2020 -version.buildmeta=372 +#Tue Dec 08 01:13:00 PST 2020 +version.buildmeta=399 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+372 +version.semver=0.8.0-beta+399 From aaecc8a9b4c71efbe9aba4ea5e6f148d2020d970 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 12:17:38 -0800 Subject: [PATCH 465/842] Added addons.help() --- config/detekt/baseline.xml | 3 +- .../java/net/thauvin/erik/mobibot/Addons.kt | 17 ++++++++ .../java/net/thauvin/erik/mobibot/Mobibot.kt | 40 +------------------ .../erik/mobibot/modules/AbstractModule.kt | 3 +- .../erik/mobibot/modules/CurrencyConverter.kt | 9 +++-- 5 files changed, 28 insertions(+), 44 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 03d17a4..bfccac4 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -56,7 +56,7 @@ <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean)</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean) : Boolean</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> @@ -75,6 +75,7 @@ <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> + <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 9c8e769..147aea0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -121,6 +121,23 @@ class Addons { return false } + /** + * Commands and Modules help. + */ + fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean { + for (command in commands) { + if (command.isVisible && command.name.startsWith(topic)) { + return command.helpResponse(topic, sender, isOp, isPrivate) + } + } + for (module in modules) { + if (module.commands.contains(topic)) { + return module.helpResponse(sender, isPrivate) + } + } + return false + } + /** * Sort commands and modules names. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index ca7caa6..a743c88 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -34,11 +34,11 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.PinboardUtils.addPin import net.thauvin.erik.mobibot.PinboardUtils.deletePin import net.thauvin.erik.mobibot.PinboardUtils.updatePin +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.ensureDir import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.isoLocalDate import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AddLog @@ -234,18 +234,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } } - /** - * Responds with the commands help, if any. - */ - private fun helpCommands(sender: String, topic: String, isPrivate: Boolean): Boolean { - for (command in addons.commands) { - if (command.isVisible && command.name.startsWith(topic)) { - return command.helpResponse(topic, sender, isOp(sender), isPrivate) - } - } - return false - } - /** * Responds with the default help. */ @@ -265,38 +253,14 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } } - /** - * Responds with the modules help, if any. - */ - private fun helpModules(sender: String, topic: String, isPrivate: Boolean): Boolean { - for (module in addons.modules) { - for (cmd in module.commands) { - if (topic == cmd) { - module.helpResponse(sender, isPrivate) - return true - } - } - } - return false - } /** * Responds with the default, commands or modules help. */ private fun helpResponse(sender: String, topic: String, isPrivate: Boolean) { val isOp = isOp(sender) - if (topic.isBlank()) { + if (topic.isBlank() || !addons.help(sender, topic.toLowerCase().trim(), isOp, isPrivate)) { helpDefault(sender, isOp, isPrivate) - } else { - // Command, Modules or Default - if (!helpCommands(sender, topic, isPrivate) && !helpModules( - sender, - topic.toLowerCase().trim(), - isPrivate - ) - ) { - helpDefault(sender, isOp, isPrivate) - } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 695549d..4fe3d8d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -74,10 +74,11 @@ abstract class AbstractModule(val bot: Mobibot) { /** * Responds with the module's help. */ - open fun helpResponse(sender: String, isPrivate: Boolean) { + open fun helpResponse(sender: String, isPrivate: Boolean) : Boolean { for (h in help) { bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) } + return true } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 279f748..6d9587a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -67,12 +67,12 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { * Converts the specified currencies. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { + bot.apply { if (EXCHANGE_RATES.isEmpty()) { try { loadRates() } catch (e: ModuleException) { - if (bot.logger.isWarnEnabled) logger.warn(e.debugMessage, e) + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) } } @@ -93,8 +93,8 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { } } - override fun helpResponse(sender: String, isPrivate: Boolean) { - with(bot) { + override fun helpResponse(sender: String, isPrivate: Boolean) : Boolean { + bot.apply { if (EXCHANGE_RATES.isEmpty()) { try { loadRates() @@ -125,6 +125,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, isIndent = true) } } + return true } companion object { From 19894dce1fbb2c64f55d9c2ce9d453ed020441b7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 12:29:47 -0800 Subject: [PATCH 466/842] Changed with(x) to x.apply --- config/detekt/baseline.xml | 2 +- src/main/java/net/thauvin/erik/mobibot/Addons.kt | 4 ++-- src/main/java/net/thauvin/erik/mobibot/FeedReader.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/Utils.kt | 3 +-- .../java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/tell/Tell.kt | 2 +- .../java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt | 4 ++-- .../java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt | 4 ++-- .../net/thauvin/erik/mobibot/modules/RockPaperScissors.kt | 6 +++--- .../java/net/thauvin/erik/mobibot/modules/StockQuote.kt | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt | 6 +++--- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt | 4 ++-- src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- 22 files changed, 31 insertions(+), 32 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index bfccac4..65149d6 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -56,7 +56,7 @@ <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean) : Boolean</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean): Boolean</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 147aea0..a9b91b0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -49,7 +49,7 @@ class Addons { * Add a module with properties. */ fun add(module: AbstractModule, props: Properties) { - with(module) { + module.apply { if (hasProperties()) { propertyKeys.forEach { setProperty(it, props.getProperty(it, "")) @@ -68,7 +68,7 @@ class Addons { * Add a command with properties. */ fun add(command: AbstractCommand, props: Properties) { - with(command) { + command.apply { if (properties.isNotEmpty()) { properties.keys.forEach { setProperty(it, props.getProperty(it, "")) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt index 23b124a..0d437ef 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt @@ -54,7 +54,7 @@ class FeedReader( * Fetches the Feed's items. */ override fun run() { - with(bot) { + bot.apply { try { val input = SyndFeedInput() XmlReader(URL(url)).use { reader -> diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt index addf76b..ad6cda7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -80,7 +80,7 @@ object PinboardUtils { @JvmStatic fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { val update = GlobalScope.async { - with(entry) { + entry.apply { if (oldUrl != link) { poster.deletePin(oldUrl) poster.addPin( diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index 7bf1580..a165328 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -38,7 +38,6 @@ import java.io.BufferedReader import java.io.File import java.io.IOException import java.io.InputStreamReader -import java.lang.NumberFormatException import java.net.URL import java.net.URLEncoder import java.nio.charset.StandardCharsets @@ -245,7 +244,7 @@ object Utils { val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( TimeUnit.MILLISECONDS.toHours(uptime) ) - with(info) { + info.apply { if (years > 0) { append(years).append(plural(years, " year ", " years ")) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 3eee724..f183340 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -61,7 +61,7 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - with(properties[FEED_PROP]) { + properties[FEED_PROP].apply { if (!isNullOrBlank()) { Thread(FeedReader(bot, sender, this)).start() } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index 49832f2..6390b3f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -51,7 +51,7 @@ class Cycle(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - with(bot) { + bot.apply { if (isOp) { send("$sender has just asked me to leave. I'll be back!") sleep(wait) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt index fa6b462..54b8d6e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt @@ -46,7 +46,7 @@ class Debug(bot: Mobibot) : AbstractCommand(bot) { override fun commandResponse(sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean) { if (isOp) { - with(bot) { + bot.apply { if (logger.isDebugEnabled) { Configurator.setLevel(logger.name, loggerLevel) } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index a9493fa..d42aa33 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -52,7 +52,7 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - with(bot) { + bot.apply { if (isOp) { if (modulesNames.isEmpty()) { send(sender, "There are no enabled modules.", isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 5627285..be1fb89 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -33,9 +33,9 @@ package net.thauvin.erik.mobibot.commands.tell import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.reverseColor import net.thauvin.erik.mobibot.Utils.utcDateTime diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 823d2ab..d4a63ac 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -103,7 +103,7 @@ object EntriesMgr { val items = feed.entries var entry: EntryLink for (i in items.indices.reversed()) { - with(items[i]) { + items[i].apply { entry = EntryLink( link, title, @@ -156,7 +156,7 @@ object EntriesMgr { var buff: StringBuilder var comment: EntryComment for (i in entries.size - 1 downTo 0) { - with(entries[i]) { + entries[i].apply { buff = StringBuilder() .append("Posted by <b>") .append(nick) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 5f83aa2..edbd5ac 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -61,7 +61,7 @@ object EntriesUtils { buff.append("[+").append(entry.comments.size).append(']') } buff.append(' ') - with(entry) { + entry.apply { if (Constants.NO_TITLE == title) { buff.append(title) } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 4fe3d8d..726f5e2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -74,7 +74,7 @@ abstract class AbstractModule(val bot: Mobibot) { /** * Responds with the module's help. */ - open fun helpResponse(sender: String, isPrivate: Boolean) : Boolean { + open fun helpResponse(sender: String, isPrivate: Boolean): Boolean { for (h in help) { bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 6d9587a..07e5bca 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -93,7 +93,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { } } - override fun helpResponse(sender: String, isPrivate: Boolean) : Boolean { + override fun helpResponse(sender: String, isPrivate: Boolean): Boolean { bot.apply { if (EXCHANGE_RATES.isEmpty()) { try { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt index fb20e9d..ace71ae 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt @@ -49,7 +49,7 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { val playerRoll = roll() val total = roll.first + roll.second val playerTotal = playerRoll.first + playerRoll.second - with(bot) { + bot.apply { send( channel, "$sender rolled two dice: ${Utils.bold(playerRoll.first)} and ${Utils.bold(playerRoll.second)}" diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index bbaec32..de38941 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -50,7 +50,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { * Searches Google. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { + bot.apply { if (args.isNotBlank()) { try { val results = searchGoogle( diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt index 71a3037..31dbc9f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -50,7 +50,7 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { isPrivate: Boolean ) { if (args.matches("(\\S.)+(\\S)+".toRegex())) { - with(bot) { + bot.apply { try { lookup(args).split(',').forEach { send(it.trim()) @@ -142,7 +142,7 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { fun whois(query: String, host: String): List<String> { val whoisClient = WhoisClient() val lines: List<String> - with(whoisClient) { + whoisClient.apply { try { defaultTimeout = Constants.CONNECT_TIMEOUT connect(host) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index e22496a..11071c2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -42,13 +42,13 @@ import kotlin.random.Random */ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { init { - with(commands) { + commands.apply { add(Hands.ROCK.name.toLowerCase()) add(Hands.PAPER.name.toLowerCase()) add(Hands.SCISSORS.name.toLowerCase()) } - with(help) { + help.apply { add("To play Rock Paper Scissors:") add( Utils.helpFormat( @@ -96,7 +96,7 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) { val hand = Hands.valueOf(cmd.toUpperCase()) val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] - with(bot) { + bot.apply { when { hand == botHand -> { send("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)}") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index a43eca2..101e387 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -50,7 +50,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { * Returns the specified stock quote from Alpha Avantage. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { + bot.apply { if (args.isNotBlank()) { try { val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) @@ -128,7 +128,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { val messages = mutableListOf<Message>() var response: String try { - with(messages) { + messages.apply { // Search for symbol/keywords response = Utils.urlReader( URL( diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index beb3793..4535329 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -86,7 +86,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Send a notification to the registered Twitter handle. */ fun notification(msg: String) { - with(bot) { + bot.apply { if (isEnabled && !handle.isNullOrBlank()) { Thread { try { @@ -120,7 +120,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Post an entry to twitter. */ fun postEntry(index: Int) { - with(bot) { + bot.apply { if (isAutoPost && hasEntry(index) && LinksMgr.entries.size >= index) { val entry = LinksMgr.entries[index] val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel" @@ -157,7 +157,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Posts to twitter. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { + bot.apply { try { send( sender, diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index a6133b4..3ee4532 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -124,7 +124,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { messages.add( PublicMessage("City: ${cwd.cityName} [${country.toUpperCase()}]") ) - with(cwd.mainData) { + cwd.mainData.apply { if (this != null) { if (hasTemp()) { messages.add(PublicMessage("Temperature: ${getTemps(temp)}")) @@ -135,7 +135,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } } if (cwd.hasWindData()) { - with(cwd.windData) { + cwd.windData.apply { if (this != null && hasSpeed() && speed != null) { messages.add(NoticeMessage("Wind: ${wind(speed!!)}")) } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index ed7ad74..487cba1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -188,7 +188,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { args: String, isPrivate: Boolean ) { - with(bot) { + bot.apply { if (args.isEmpty()) { send(sender, "The supported countries/zones are: ", isPrivate) sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false) From d33210c594a90f2c230b878c4ff93bd0f9adedd9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 14:29:51 -0800 Subject: [PATCH 467/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.kt | 4 +++- src/main/java/net/thauvin/erik/mobibot/Utils.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Info.java | 2 +- .../java/net/thauvin/erik/mobibot/commands/Versions.java | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index a743c88..ea26829 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -399,7 +399,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert * Sends a notice to the channel. */ fun send(notice: String?) { - if (notice != null) send(channel, notice, false) + notice?.let { send(channel, it, false) } } /** @@ -419,6 +419,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert /** * Send a formatted commands/modules, etc. list. */ + @JvmOverloads fun sendList( nick: String, list: List<String>, @@ -600,6 +601,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert init { System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", Constants.CONNECT_TIMEOUT.toString()) System.getProperties().setProperty("sun.net.client.defaultReadTimeout", Constants.CONNECT_TIMEOUT.toString()) + name = nickname ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) ircPort = getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index a165328..a8e7b63 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -156,7 +156,7 @@ object Utils { @JvmStatic @JvmOverloads fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { - return (if (isIndent) " " else "").plus(if (isBold) bold(help) else help) + return (if (isIndent) " " else "").plus(if (isBold) bold(help) else help) } /** diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index 09043aa..224daf3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -85,7 +85,7 @@ public class Info extends AbstractCommand { @NotNull final String args, final boolean isOp, final boolean isPrivate) { - getBot().sendList(sender, allVersions, 1, isPrivate, false, false); + getBot().sendList(sender, allVersions, 1, isPrivate); final StringBuilder info = new StringBuilder("Uptime: "); diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 662d073..7413156 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -41,10 +41,10 @@ import java.util.List; public class Versions extends AbstractCommand { private final List<String> allVersions = - List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + " (" + System.getProperty("os.arch") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + "Runtime: " + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version")); public Versions(@NotNull final Mobibot bot) { @@ -86,7 +86,7 @@ public class Versions extends AbstractCommand { final boolean isOp, final boolean isPrivate) { if (isOp) { - getBot().sendList(sender, allVersions, 1, isPrivate, false, false); + getBot().sendList(sender, allVersions, 1, isPrivate); } else { getBot().helpDefault(sender, false, isPrivate); } From e825f0e658e1d04ae03294c70e1f4b8cc1a3d306 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 14:30:07 -0800 Subject: [PATCH 468/842] Version 0.8.0-beta+411 --- version.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.properties b/version.properties index 00ef133..824070f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Dec 08 01:13:00 PST 2020 -version.buildmeta=399 +#Tue Dec 08 13:34:40 PST 2020 +version.buildmeta=411 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+399 +version.semver=0.8.0-beta+411 From 8815cde78fa31330066d97fd14ee5dace7a5285e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 8 Dec 2020 15:11:12 -0800 Subject: [PATCH 469/842] Reverted to using with(x) --- src/main/java/net/thauvin/erik/mobibot/Addons.kt | 4 ++-- src/main/java/net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../java/net/thauvin/erik/mobibot/PinboardUtils.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../net/thauvin/erik/mobibot/commands/ChannelFeed.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Debug.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/entries/EntriesMgr.kt | 10 +++++----- .../net/thauvin/erik/mobibot/entries/EntriesUtils.kt | 2 +- .../thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../net/thauvin/erik/mobibot/modules/GoogleSearch.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/Lookup.kt | 4 ++-- .../thauvin/erik/mobibot/modules/RockPaperScissors.kt | 6 +++--- .../net/thauvin/erik/mobibot/modules/StockQuote.kt | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/Twitter.kt | 6 +++--- .../java/net/thauvin/erik/mobibot/modules/Weather2.kt | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- 19 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index a9b91b0..147aea0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -49,7 +49,7 @@ class Addons { * Add a module with properties. */ fun add(module: AbstractModule, props: Properties) { - module.apply { + with(module) { if (hasProperties()) { propertyKeys.forEach { setProperty(it, props.getProperty(it, "")) @@ -68,7 +68,7 @@ class Addons { * Add a command with properties. */ fun add(command: AbstractCommand, props: Properties) { - command.apply { + with(command) { if (properties.isNotEmpty()) { properties.keys.forEach { setProperty(it, props.getProperty(it, "")) diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt index 0d437ef..23b124a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt @@ -54,7 +54,7 @@ class FeedReader( * Fetches the Feed's items. */ override fun run() { - bot.apply { + with(bot) { try { val input = SyndFeedInput() XmlReader(URL(url)).use { reader -> diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt index ad6cda7..addf76b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -80,7 +80,7 @@ object PinboardUtils { @JvmStatic fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { val update = GlobalScope.async { - entry.apply { + with(entry) { if (oldUrl != link) { poster.deletePin(oldUrl) poster.addPin( diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index a8e7b63..4e49c89 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -244,7 +244,7 @@ object Utils { val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( TimeUnit.MILLISECONDS.toHours(uptime) ) - info.apply { + with(info) { if (years > 0) { append(years).append(plural(years, " year ", " years ")) } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index f183340..3eee724 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -61,7 +61,7 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - properties[FEED_PROP].apply { + with(properties[FEED_PROP]) { if (!isNullOrBlank()) { Thread(FeedReader(bot, sender, this)).start() } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index 6390b3f..49832f2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -51,7 +51,7 @@ class Cycle(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - bot.apply { + with(bot) { if (isOp) { send("$sender has just asked me to leave. I'll be back!") sleep(wait) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt index 54b8d6e..fa6b462 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt @@ -46,7 +46,7 @@ class Debug(bot: Mobibot) : AbstractCommand(bot) { override fun commandResponse(sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean) { if (isOp) { - bot.apply { + with(bot) { if (logger.isDebugEnabled) { Configurator.setLevel(logger.name, loggerLevel) } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index d42aa33..a9493fa 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -52,7 +52,7 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - bot.apply { + with(bot) { if (isOp) { if (modulesNames.isEmpty()) { send(sender, "There are no enabled modules.", isPrivate) diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index d4a63ac..9cbd247 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -103,7 +103,7 @@ object EntriesMgr { val items = feed.entries var entry: EntryLink for (i in items.indices.reversed()) { - items[i].apply { + with(items[i]) { entry = EntryLink( link, title, @@ -145,7 +145,7 @@ object EntriesMgr { OutputStreamWriter( Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8 ).use { fw -> - rss.apply { + with(rss) { feedType = "rss_2.0" title = bot.channel + " IRC Links" description = "Links from ${bot.ircServer} on ${bot.channel}" @@ -156,7 +156,7 @@ object EntriesMgr { var buff: StringBuilder var comment: EntryComment for (i in entries.size - 1 downTo 0) { - entries[i].apply { + with(entries[i]) { buff = StringBuilder() .append("Posted by <b>") .append(nick) @@ -210,7 +210,7 @@ object EntriesMgr { Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8 ).use { fw -> rss = SyndFeedImpl() - rss.apply { + with(rss) { feedType = "rss_2.0" title = "${bot.channel} IRC Links Backlogs" description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}" @@ -222,7 +222,7 @@ object EntriesMgr { for (i in history.size - 1 downTo 0) { date = history[i] item = SyndEntryImpl() - item.apply { + with(item) { link = bot.backlogsUrl + date + ".xml" title = date description = SyndContentImpl().apply { value = "Links for $date" } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index edbd5ac..5f83aa2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -61,7 +61,7 @@ object EntriesUtils { buff.append("[+").append(entry.comments.size).append(']') } buff.append(' ') - entry.apply { + with(entry) { if (Constants.NO_TITLE == title) { buff.append(title) } else { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 07e5bca..a617f0b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -94,7 +94,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { } override fun helpResponse(sender: String, isPrivate: Boolean): Boolean { - bot.apply { + with(bot) { if (EXCHANGE_RATES.isEmpty()) { try { loadRates() diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt index ace71ae..fb20e9d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt @@ -49,7 +49,7 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { val playerRoll = roll() val total = roll.first + roll.second val playerTotal = playerRoll.first + playerRoll.second - bot.apply { + with(bot) { send( channel, "$sender rolled two dice: ${Utils.bold(playerRoll.first)} and ${Utils.bold(playerRoll.second)}" diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index de38941..bbaec32 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -50,7 +50,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { * Searches Google. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - bot.apply { + with(bot) { if (args.isNotBlank()) { try { val results = searchGoogle( diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt index 31dbc9f..71a3037 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -50,7 +50,7 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { isPrivate: Boolean ) { if (args.matches("(\\S.)+(\\S)+".toRegex())) { - bot.apply { + with(bot) { try { lookup(args).split(',').forEach { send(it.trim()) @@ -142,7 +142,7 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { fun whois(query: String, host: String): List<String> { val whoisClient = WhoisClient() val lines: List<String> - whoisClient.apply { + with(whoisClient) { try { defaultTimeout = Constants.CONNECT_TIMEOUT connect(host) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 11071c2..e22496a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -42,13 +42,13 @@ import kotlin.random.Random */ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { init { - commands.apply { + with(commands) { add(Hands.ROCK.name.toLowerCase()) add(Hands.PAPER.name.toLowerCase()) add(Hands.SCISSORS.name.toLowerCase()) } - help.apply { + with(help) { add("To play Rock Paper Scissors:") add( Utils.helpFormat( @@ -96,7 +96,7 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) { val hand = Hands.valueOf(cmd.toUpperCase()) val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] - bot.apply { + with(bot) { when { hand == botHand -> { send("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)}") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index 101e387..a43eca2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -50,7 +50,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { * Returns the specified stock quote from Alpha Avantage. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - bot.apply { + with(bot) { if (args.isNotBlank()) { try { val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) @@ -128,7 +128,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { val messages = mutableListOf<Message>() var response: String try { - messages.apply { + with(messages) { // Search for symbol/keywords response = Utils.urlReader( URL( diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index 4535329..beb3793 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -86,7 +86,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Send a notification to the registered Twitter handle. */ fun notification(msg: String) { - bot.apply { + with(bot) { if (isEnabled && !handle.isNullOrBlank()) { Thread { try { @@ -120,7 +120,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Post an entry to twitter. */ fun postEntry(index: Int) { - bot.apply { + with(bot) { if (isAutoPost && hasEntry(index) && LinksMgr.entries.size >= index) { val entry = LinksMgr.entries[index] val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel" @@ -157,7 +157,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Posts to twitter. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - bot.apply { + with(bot) { try { send( sender, diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index 3ee4532..a6133b4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -124,7 +124,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { messages.add( PublicMessage("City: ${cwd.cityName} [${country.toUpperCase()}]") ) - cwd.mainData.apply { + with(cwd.mainData) { if (this != null) { if (hasTemp()) { messages.add(PublicMessage("Temperature: ${getTemps(temp)}")) @@ -135,7 +135,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } } if (cwd.hasWindData()) { - cwd.windData.apply { + with(cwd.windData) { if (this != null && hasSpeed() && speed != null) { messages.add(NoticeMessage("Wind: ${wind(speed!!)}")) } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index 487cba1..ed7ad74 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -188,7 +188,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { args: String, isPrivate: Boolean ) { - bot.apply { + with(bot) { if (args.isEmpty()) { send(sender, "The supported countries/zones are: ", isPrivate) sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false) From 6b5e8ab1ff3aaf0e1e487921700669c7a0f92998 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 11 Mar 2021 18:16:17 -0800 Subject: [PATCH 470/842] Fixed tests. --- src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt | 4 ++-- .../java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt index ed57c8e..fdbb51c 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -43,8 +43,8 @@ class LookupTest { @Test @Throws(Exception::class) fun testLookup() { - val result = lookup("erik.thauvin.net") - Assertions.assertThat(result).`as`("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12") + val result = lookup("apple.com") + Assertions.assertThat(result).`as`("lookup(apple.com)").contains("17.253.144.10") } @Test diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 08f12db..24d91d5 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -48,7 +48,7 @@ class StockQuoteTest : LocalProperties() { val messages = getQuote("apple inc", apiKey) Assertions.assertThat(messages).`as`("response not empty").isNotEmpty Assertions.assertThat(messages[0].msg).`as`("same stock symbol") - .isEqualTo("Symbol: AAPL [Apple Inc.]") + .startsWith("Symbol: AAPL") Assertions.assertThat(messages[1].msg).`as`("price label") .startsWith(" Price: ") try { From 844253f2bcb4c575d044359d298e4fa050fc4766 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 11 Mar 2021 18:16:37 -0800 Subject: [PATCH 471/842] Added onVersion. --- src/main/java/net/thauvin/erik/mobibot/Mobibot.kt | 11 ++++++++++- version.properties | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index ea26829..be986bf 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -479,6 +479,14 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } } + /** + * Returns the bot's version. + */ + override fun onVersion(sourceNick: String, sourceLogin: String, sourceHostname: String, target: String) { + sendRawLine("NOTICE " + sourceNick + " :\u0001VERSION " + ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION + + "\u0001") + } + companion object { // Maximum number of times the bot will try to reconnect, if disconnected private const val MAX_RECONNECT = 10 @@ -614,7 +622,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert setVerbose(true) setAutoNickChange(true) login = p.getProperty("login", name) - version = ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION + // Set the real name + version = ReleaseInfo.PROJECT // setMessageDelay(1000); // Set NICKSERV identification diff --git a/version.properties b/version.properties index 824070f..0880ceb 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Dec 08 13:34:40 PST 2020 -version.buildmeta=411 +#Thu Mar 11 18:09:42 PST 2021 +version.buildmeta=428 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+411 +version.semver=0.8.0-beta+428 From c950d7349a249a1363028c9b33517c5a924b9319 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 12 Mar 2021 21:26:54 -0800 Subject: [PATCH 472/842] Updated dependencies. --- build.gradle | 28 +++++------ config/detekt/baseline.xml | 47 ++----------------- config/pmd.xml | 2 - gradle/wrapper/gradle-wrapper.properties | 2 +- .../thauvin/erik/mobibot/commands/Info.java | 5 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- version.properties | 6 +-- 7 files changed, 25 insertions(+), 67 deletions(-) diff --git a/build.gradle b/build.gradle index 8c0d5f3..8bc47e5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,16 @@ plugins { id 'application' id 'checkstyle' - id 'com.github.ben-manes.versions' version '0.36.0' - id 'com.github.spotbugs' version '4.6.0' + id 'com.github.ben-manes.versions' version '0.38.0' + id 'com.github.spotbugs' version '4.7.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.15.0-RC1' + id 'io.gitlab.arturbosch.detekt' version '1.16.0' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.4.20' - id 'org.jetbrains.kotlin.kapt' version '1.4.20' - id 'org.sonarqube' version '3.0' + id 'org.jetbrains.kotlin.jvm' version '1.4.31' + id 'org.jetbrains.kotlin.kapt' version '1.4.31' + id 'org.sonarqube' version '3.1.1' id 'pmd' } @@ -22,9 +22,9 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" ext.versions = [ jacoco : '0.8.6', - log4j : '2.14.0', - pmd : '6.29.0', - spotbugs: '4.2.0' + log4j : '2.14.1', + pmd : '6.32.0', + spotbugs: '4.2.2' ] repositories { @@ -48,9 +48,9 @@ dependencies { implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' implementation 'net.thauvin.erik:pinboard-poster:1.0.1' - implementation 'org.apache.commons:commons-lang3:3.11' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2' - implementation 'org.json:json:20201115' + implementation 'org.apache.commons:commons-lang3:3.12.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3' + implementation 'org.json:json:20210307' implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" @@ -60,8 +60,8 @@ dependencies { compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" - testImplementation 'org.assertj:assertj-core:3.18.1' - testImplementation 'org.testng:testng:7.3.0' + testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.testng:testng:7.4.0' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0' spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 65149d6..7d83c69 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -14,42 +14,6 @@ <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> - <ID>MagicNumber:AddLog.kt$AddLog$4</ID> - <ID>MagicNumber:Comment.kt$Comment$3</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID> - <ID>MagicNumber:Cycle.kt$Cycle$10</ID> - <ID>MagicNumber:Dice.kt$Dice$7</ID> - <ID>MagicNumber:FeedReader.kt$FeedReader$5</ID> - <ID>MagicNumber:Ignore.kt$Ignore$8</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$10</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$1000L</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$3</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$5</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$8</ID> - <ID>MagicNumber:Modules.kt$Modules$7</ID> - <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> - <ID>MagicNumber:Tell.kt$Tell$50</ID> - <ID>MagicNumber:Tell.kt$Tell$7</ID> - <ID>MagicNumber:Twitter.kt$Twitter$1000L</ID> - <ID>MagicNumber:Twitter.kt$Twitter$60L</ID> - <ID>MagicNumber:Users.kt$Users$8</ID> - <ID>MagicNumber:Utils.kt$Utils$30</ID> - <ID>MagicNumber:Utils.kt$Utils$365</ID> - <ID>MagicNumber:Utils.kt$Utils$7</ID> - <ID>MagicNumber:View.kt$View$8</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime$17</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> <ID>MemberNameEqualsClassName:Calc.kt$Calc.Companion$ @JvmStatic fun calc(query: String): String</ID> <ID>MemberNameEqualsClassName:Lookup.kt$Lookup.Companion$ @JvmStatic @Throws(UnknownHostException::class) fun lookup(query: String): String</ID> <ID>MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): Message</ID> @@ -74,19 +38,14 @@ <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> + <ID>PrintStackTrace:Mobibot.kt$Mobibot$e</ID> + <ID>PrintStackTrace:Mobibot.kt$Mobibot.Companion$e</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> + <ID>SwallowedException:Calc.kt$Calc.Companion$catch (e: IllegalArgumentException) { "No idea. This is the kind of math I don't get." }</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> - <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID> - <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID> - <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> - <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> - <ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID> - <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> - <ID>TooManyFunctions:Utils.kt$Utils$Utils</ID> </CurrentIssues> </SmellBaseline> diff --git a/config/pmd.xml b/config/pmd.xml index dc39dbb..b1174f9 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -237,7 +237,6 @@ <rule ref="category/java/performance.xml/AvoidArrayLoops"/> <rule ref="category/java/performance.xml/AvoidFileStream"/> <rule ref="category/java/performance.xml/AvoidInstantiatingObjectsInLoops"/> - <rule ref="category/java/performance.xml/AvoidUsingShortType"/> <rule ref="category/java/performance.xml/BigIntegerInstantiation"/> <rule ref="category/java/performance.xml/BooleanInstantiation"/> <rule ref="category/java/performance.xml/ByteInstantiation"/> @@ -250,7 +249,6 @@ <rule ref="category/java/performance.xml/LongInstantiation"/> <rule ref="category/java/performance.xml/OptimizableToArrayCall"/> <rule ref="category/java/performance.xml/RedundantFieldInitializer"/> - <rule ref="category/java/performance.xml/SimplifyStartsWith"/> <rule ref="category/java/performance.xml/ShortInstantiation"/> <rule ref="category/java/performance.xml/StringInstantiation"/> <rule ref="category/java/performance.xml/StringToString"/> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4d9ca16..442d913 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index 224daf3..e419d39 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -87,9 +87,10 @@ public class Info extends AbstractCommand { final boolean isPrivate) { getBot().sendList(sender, allVersions, 1, isPrivate); - final StringBuilder info = new StringBuilder("Uptime: "); + final StringBuilder info = new StringBuilder(29); - info.append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())) + info.append("Uptime: ") + .append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())) .append(" [Entries: ") .append(LinksMgr.entries.size()); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index ed7ad74..b03f416 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -64,7 +64,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() - return String.format("%c%03d", '@', beats) + return String.format(Locale.getDefault(), "%c%03d", '@', beats) } /** diff --git a/version.properties b/version.properties index 0880ceb..38c12ff 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Mar 11 18:09:42 PST 2021 -version.buildmeta=428 +#Fri Mar 12 21:10:57 PST 2021 +version.buildmeta=434 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+428 +version.semver=0.8.0-beta+434 From aaf44db38a7c67f0ced31434d9decc6cf9444725 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 26 Apr 2021 15:23:21 -0700 Subject: [PATCH 473/842] Upgraded to Kotlin 1.5.0 --- build.gradle | 14 ++--- config/pmd.xml | 4 -- gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/net/thauvin/erik/mobibot/Mobibot.kt | 8 +-- .../net/thauvin/erik/mobibot/PinboardUtils.kt | 6 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 10 ++-- .../erik/mobibot/commands/links/View.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 8 +-- .../erik/mobibot/modules/RockPaperScissors.kt | 16 +++--- .../erik/mobibot/modules/StockQuote.kt | 57 ++++++++++--------- .../net/thauvin/erik/mobibot/modules/War.java | 2 + .../thauvin/erik/mobibot/modules/Weather2.kt | 11 ++-- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- version.properties | 6 +- 16 files changed, 77 insertions(+), 75 deletions(-) diff --git a/build.gradle b/build.gradle index 8bc47e5..1702a51 100644 --- a/build.gradle +++ b/build.gradle @@ -8,8 +8,8 @@ plugins { id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.4.31' - id 'org.jetbrains.kotlin.kapt' version '1.4.31' + id 'org.jetbrains.kotlin.jvm' version '1.5.0' + id 'org.jetbrains.kotlin.kapt' version '1.5.0' id 'org.sonarqube' version '3.1.1' id 'pmd' } @@ -23,8 +23,8 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" ext.versions = [ jacoco : '0.8.6', log4j : '2.14.1', - pmd : '6.32.0', - spotbugs: '4.2.2' + pmd : '6.34.0', + spotbugs: '4.2.3' ] repositories { @@ -44,12 +44,12 @@ dependencies { implementation 'com.rometools:rome:1.15.0' implementation 'commons-cli:commons-cli:1.4' - implementation 'commons-net:commons-net:3.7.2' + implementation 'commons-net:commons-net:3.8.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'net.thauvin.erik:pinboard-poster:1.0.1' + implementation 'net.thauvin.erik:pinboard-poster:1.0.3' implementation 'org.apache.commons:commons-lang3:3.12.0' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC' implementation 'org.json:json:20210307' implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' diff --git a/config/pmd.xml b/config/pmd.xml index b1174f9..2760bff 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -30,7 +30,6 @@ <rule ref="category/java/bestpractices.xml/SystemPrintln"/> <rule ref="category/java/bestpractices.xml/UnusedAssignment"/> <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/> - <rule ref="category/java/bestpractices.xml/UnusedImports"/> <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/> <rule ref="category/java/bestpractices.xml/UnusedPrivateField"/> <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/> @@ -59,8 +58,6 @@ <rule ref="category/java/codestyle.xml/BooleanGetMethodName"/> <rule ref="category/java/codestyle.xml/CallSuperInConstructor"/> <rule ref="category/java/codestyle.xml/ControlStatementBraces"/> - <rule ref="category/java/codestyle.xml/DontImportJavaLang"/> - <rule ref="category/java/codestyle.xml/DuplicateImports"/> <rule ref="category/java/codestyle.xml/EmptyMethodInAbstractClassShouldBeAbstract"/> <rule ref="category/java/codestyle.xml/ExtendsObject"/> <rule ref="category/java/codestyle.xml/FieldDeclarationsShouldBeAtStartOfClass"/> @@ -163,7 +160,6 @@ <rule ref="category/java/errorprone.xml/FinalizeOverloaded"/> <rule ref="category/java/errorprone.xml/FinalizeShouldBeProtected"/> <rule ref="category/java/errorprone.xml/IdempotentOperations"/> - <rule ref="category/java/errorprone.xml/ImportFromSamePackage"/> <rule ref="category/java/errorprone.xml/InstantiationToGetClass"/> <rule ref="category/java/errorprone.xml/InvalidLogMessageFormat"/> <rule ref="category/java/errorprone.xml/JumbledIncrementer"/> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 442d913..f371643 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index be986bf..2d8b974 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -259,7 +259,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert */ private fun helpResponse(sender: String, topic: String, isPrivate: Boolean) { val isOp = isOp(sender) - if (topic.isBlank() || !addons.help(sender, topic.toLowerCase().trim(), isOp, isPrivate)) { + if (topic.isBlank() || !addons.help(sender, topic.lowercase().trim(), isOp, isPrivate)) { helpDefault(sender, isOp, isPrivate) } } @@ -317,7 +317,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert tell.send(sender, true) if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command> val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) - val cmd = cmds[0].toLowerCase() + val cmd = cmds[0].lowercase() val args = if (cmds.size > 1) { cmds[1].trim() } else "" @@ -342,7 +342,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert ) { if (logger.isDebugEnabled) logger.debug(">>> $sender : $message") val cmds = message.split(" ".toRegex(), 2) - val cmd = cmds[0].toLowerCase() + val cmd = cmds[0].lowercase() val args = if (cmds.size > 1) { cmds[1].trim() } else "" @@ -559,7 +559,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert e.printStackTrace(System.err) exitProcess(1) } - val nickname = p.getProperty("nick", Mobibot::class.java.name.toLowerCase()) + val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) val channel = p.getProperty("channel") val logsDir = ensureDir(p.getProperty("logs", "."), false) diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt index addf76b..d5ce4c5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -51,7 +51,7 @@ object PinboardUtils { */ @JvmStatic fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking { - val add = GlobalScope.async { + val add = async { poster.addPin( entry.link, entry.title, @@ -68,7 +68,7 @@ object PinboardUtils { */ @JvmStatic fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking { - val delete = GlobalScope.async { + val delete = async { poster.deletePin(entry.link) } delete.await() @@ -79,7 +79,7 @@ object PinboardUtils { */ @JvmStatic fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { - val update = GlobalScope.async { + val update = async { with(entry) { if (oldUrl != link) { poster.deletePin(oldUrl) diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 1187286..57191bc 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -70,7 +70,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { @JvmStatic fun isNotIgnored(nick: String): Boolean { - return !ignored.contains(nick.toLowerCase()) + return !ignored.contains(nick.lowercase()) } } @@ -82,8 +82,8 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { isPrivate: Boolean ) { if (!isOp) { - val nick = sender.toLowerCase() - val isMe = args.toLowerCase().startsWith(me) + val nick = sender.lowercase() + val isMe = args.lowercase().startsWith(me) ignoreNick(bot, nick, isMe, isPrivate) } else { ignoreOp(bot, sender, args, isPrivate) @@ -125,10 +125,10 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { private fun ignoreOp(bot: Mobibot, sender: String, args: String, isPrivate: Boolean) { if (args.isNotEmpty()) { - val nicks = args.toLowerCase().split(" ") + val nicks = args.lowercase().split(" ") for (nick in nicks) { val ignore = if (me == nick) { - nick.toLowerCase() + nick.lowercase() } else { nick } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index 0ea0708..db34ffb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -70,7 +70,7 @@ class View(bot: Mobibot) : AbstractCommand(bot) { private fun showPosts(bot: Mobibot, args: String, sender: String) { val max = entries.size - var lcArgs = args.toLowerCase() + var lcArgs = args.lowercase() var i = 0 if (lcArgs.isEmpty() && max > maxEntries) { i = max - maxEntries diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt index b676591..9596534 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -172,7 +172,7 @@ class EntryLink : Serializable { var category: SyndCategoryImpl for (tag in tags) { if (!tag.isNullOrBlank()) { - val t = tag.toLowerCase() + val t = tag.lowercase() val mod = t[0] if (mod == '-') { // Don't remove the channel tag diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index a617f0b..96534a8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -157,8 +157,8 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { if (cmds[3] == cmds[1] || "0" == cmds[0]) { PublicMessage("You're kidding, right?") } else { - val to = cmds[1].toUpperCase() - val from = cmds[3].toUpperCase() + val to = cmds[1].uppercase() + val from = cmds[3].uppercase() if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) { try { val amt = cmds[0].replace(",", "").toDouble() @@ -166,10 +166,10 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { val doubleTo = EXCHANGE_RATES[from]!!.toDouble() PublicMessage( NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1) - + " ${cmds[1].toUpperCase()} = " + + " ${cmds[1].uppercase()} = " + NumberFormat.getCurrencyInstance(Constants.LOCALE) .format(amt * doubleTo / doubleFrom).substring(1) - + " ${cmds[3].toUpperCase()}" + + " ${cmds[3].uppercase()}" ) } catch (e: NumberFormatException) { ErrorMessage("Let's try with some real numbers next time, okay?") diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index e22496a..940c3a7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -43,17 +43,17 @@ import kotlin.random.Random class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { init { with(commands) { - add(Hands.ROCK.name.toLowerCase()) - add(Hands.PAPER.name.toLowerCase()) - add(Hands.SCISSORS.name.toLowerCase()) + add(Hands.ROCK.name.lowercase()) + add(Hands.PAPER.name.lowercase()) + add(Hands.SCISSORS.name.lowercase()) } with(help) { add("To play Rock Paper Scissors:") add( Utils.helpFormat( - "%c ${Hands.ROCK.name.toLowerCase()} | ${Hands.PAPER.name.toLowerCase()}" - + " | ${Hands.SCISSORS.name.toLowerCase()}" + "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" ) ) } @@ -82,8 +82,8 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { companion object { // For testing. fun winLoseOrDraw(player: String, bot: String): String { - val hand = Hands.valueOf(player.toUpperCase()) - val botHand = Hands.valueOf(bot.toUpperCase()) + val hand = Hands.valueOf(player.uppercase()) + val botHand = Hands.valueOf(bot.uppercase()) return when { hand == botHand -> "draw" @@ -94,7 +94,7 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { } override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) { - val hand = Hands.valueOf(cmd.toUpperCase()) + val hand = Hands.valueOf(cmd.uppercase()) val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] with(bot) { when { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index a43eca2..3244e41 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -121,7 +121,8 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { - throw ModuleException("${STOCK_CMD.capitalize()} is disabled. The API key is missing.") + throw ModuleException( + "${STOCK_CMD.replaceFirstChar { it.uppercase() }} is disabled. The API key is missing.") } return if (symbol.isNotBlank()) { val debugMessage = "getQuote($symbol)" @@ -131,10 +132,10 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { with(messages) { // Search for symbol/keywords response = Utils.urlReader( - URL( - "${ALAPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey=" - + Utils.encodeUrl(apiKey) - ) + URL( + "${ALAPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + + "&apikey=" + Utils.encodeUrl(apiKey) + ) ) var json = getJsonResponse(response, debugMessage) val symbols = json.getJSONArray("bestMatches") @@ -145,11 +146,11 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { // Get quote for symbol response = Utils.urlReader( - URL( - "${ALAPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" - + Utils.encodeUrl(symbolInfo.getString("1. symbol")) - + "&apikey=" + Utils.encodeUrl(apiKey) - ) + URL( + "${ALAPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" + + Utils.encodeUrl(symbolInfo.getString("1. symbol")) + + "&apikey=" + Utils.encodeUrl(apiKey) + ) ) json = getJsonResponse(response, debugMessage) val quote = json.getJSONObject("Global Quote") @@ -158,36 +159,36 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { return messages } add( - PublicMessage( - "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) - + " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']' - ) + PublicMessage( + "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + + " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']' + ) ) add(PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))) add( - PublicMessage( - " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")) - ) + PublicMessage( + " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")) + ) ) add(NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))) add(NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))) add(NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))) add( - NoticeMessage( - " Volume: " + Utils.unescapeXml(quote.getString("06. volume")) - ) + NoticeMessage( + " Volume: " + Utils.unescapeXml(quote.getString("06. volume")) + ) ) add( - NoticeMessage( - " Latest: " - + Utils.unescapeXml(quote.getString("07. latest trading day")) - ) + NoticeMessage( + " Latest: " + + Utils.unescapeXml(quote.getString("07. latest trading day")) + ) ) add( - NoticeMessage( - " Change: " + Utils.unescapeXml(quote.getString("09. change")) - + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']' - ) + NoticeMessage( + " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + + Utils.unescapeXml(quote.getString("10. change percent")) + ']' + ) ) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index b239e87..2cfe28e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import org.jetbrains.annotations.NotNull; @@ -71,6 +72,7 @@ public final class War extends AbstractModule { /** * {@inheritDoc} */ + @SuppressFBWarnings("DMI_RANDOM_USED_ONLY_ONCE") @Override public void commandResponse(@NotNull final String sender, @NotNull final String cmd, diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index a6133b4..f3b740d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -100,7 +100,8 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { - throw ModuleException("${WEATHER_CMD.capitalize()} is disabled. The API key is missing.") + throw ModuleException( + "${WEATHER_CMD.replaceFirstChar { it.uppercase() }} is disabled. The API key is missing.") } val owm = OWM(apiKey) val messages = mutableListOf<Message>() @@ -122,7 +123,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } if (cwd.hasCityName()) { messages.add( - PublicMessage("City: ${cwd.cityName} [${country.toUpperCase()}]") + PublicMessage("City: ${cwd.cityName} [${country.uppercase()}]") ) with(cwd.mainData) { if (this != null) { @@ -147,7 +148,9 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { if (list != null) { for (w in list) { if (w != null) { - condition.append(' ').append(w.getDescription().capitalize()).append('.') + condition.append(' ') + .append(w.getDescription().replaceFirstChar { it.uppercase() }) + .append('.') } } messages.add(NoticeMessage(condition.toString())) @@ -162,7 +165,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { messages.add( NoticeMessage( "https://openweathermap.org/find?q=" - + Utils.encodeUrl("$city,${country.toUpperCase()}"), + + Utils.encodeUrl("$city,${country.uppercase()}"), Colors.GREEN ) ) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index b03f416..e1f11c3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -72,7 +72,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { */ @JvmStatic fun worldTime(query: String): Message { - val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).toUpperCase()] + val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).uppercase()] val response: String = if (tz != null) { if (BEATS_KEYWORD == tz) { "The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD) diff --git a/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt index 04490ed..564b7fb 100644 --- a/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt @@ -69,7 +69,7 @@ open class LocalProperties { } private fun keyToEnv(key: String): String { - return key.replace('-', '_').toUpperCase() + return key.replace('-', '_').uppercase() } } } diff --git a/version.properties b/version.properties index 38c12ff..8b8d651 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Mar 12 21:10:57 PST 2021 -version.buildmeta=434 +#Mon Apr 26 15:10:03 PDT 2021 +version.buildmeta=452 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+434 +version.semver=0.8.0-beta+452 From 3f75d60c5f4efcfc53bfccd073172bd7b5fd0ed1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 26 Apr 2021 16:20:07 -0700 Subject: [PATCH 474/842] Updated detekt baseline location. --- build.gradle | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 1702a51..d48195b 100644 --- a/build.gradle +++ b/build.gradle @@ -127,7 +127,7 @@ pmd { } detekt { - baseline = file("${projectDir}/config/detekt/baseline.xml") + baseline = file("${rootProject.projectDir}/config/detekt/baseline.xml") } javadoc { diff --git a/version.properties b/version.properties index 8b8d651..4cc2ed0 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Apr 26 15:10:03 PDT 2021 -version.buildmeta=452 +#Mon Apr 26 16:18:54 PDT 2021 +version.buildmeta=454 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+452 +version.semver=0.8.0-beta+454 From 7b2b496a94041a09ee450c717c3f88ebba07ac4d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 26 Apr 2021 18:02:54 -0700 Subject: [PATCH 475/842] Prevent detekt from from failing, for now. --- build.gradle | 3 ++- version.properties | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index d48195b..0644bfb 100644 --- a/build.gradle +++ b/build.gradle @@ -127,7 +127,8 @@ pmd { } detekt { - baseline = file("${rootProject.projectDir}/config/detekt/baseline.xml") + ignoreFailures = true // Baseline is incomplete and fails on various CIs, not sure why. + baseline = file("${projectDir}/config/detekt/baseline.xml") } javadoc { diff --git a/version.properties b/version.properties index 4cc2ed0..6d5aa2a 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Apr 26 16:18:54 PDT 2021 -version.buildmeta=454 +#Mon Apr 26 18:00:07 PDT 2021 +version.buildmeta=462 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+454 +version.semver=0.8.0-beta+462 From 8b8cbb39f8524c255d3c125b43d0116fbfd946a5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 26 Apr 2021 22:06:13 -0700 Subject: [PATCH 476/842] Added Utils.capitalize() --- src/main/java/net/thauvin/erik/mobibot/Utils.kt | 10 +++++++++- .../java/net/thauvin/erik/mobibot/commands/Info.java | 3 +-- .../net/thauvin/erik/mobibot/modules/GoogleSearch.kt | 3 +-- .../net/thauvin/erik/mobibot/modules/StockQuote.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/Weather2.kt | 4 ++-- src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt | 7 +++++++ version.properties | 6 +++--- 7 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index 4e49c89..62f1695 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -81,7 +81,15 @@ object Utils { val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) return StringUtils.replaceEach(text, searchFlags, replace) } - + + /** + * Capitalize a string. + */ + @JvmStatic + fun capitalize(s: String?): String { + return s!!.replaceFirstChar { it.uppercase() } + } + /** * Colorize a string. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index e419d39..5af7d4b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -36,7 +36,6 @@ import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.ReleaseInfo; import net.thauvin.erik.mobibot.Utils; import net.thauvin.erik.mobibot.commands.links.LinksMgr; -import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import java.lang.management.ManagementFactory; @@ -44,7 +43,7 @@ import java.util.List; public class Info extends AbstractCommand { private final List<String> allVersions = List.of( - StringUtils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION + Utils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION + " (" + Utils.green(ReleaseInfo.WEBSITE) + ')', "Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')'); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index bbaec32..a801f55 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -35,7 +35,6 @@ import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage -import org.apache.commons.lang3.StringUtils import org.jibble.pircbot.Colors import org.json.JSONException import org.json.JSONObject @@ -87,7 +86,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message> { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { - throw ModuleException("${StringUtils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.") + throw ModuleException("${Utils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.") } return if (query.isNotBlank()) { val results = mutableListOf<Message>() diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index 3244e41..e9c2905 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -122,7 +122,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${STOCK_CMD.replaceFirstChar { it.uppercase() }} is disabled. The API key is missing.") + "${Utils.capitalize(STOCK_CMD)} is disabled. The API key is missing.") } return if (symbol.isNotBlank()) { val debugMessage = "getQuote($symbol)" diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index f3b740d..5a164b5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -101,7 +101,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${WEATHER_CMD.replaceFirstChar { it.uppercase() }} is disabled. The API key is missing.") + "${Utils.capitalize(WEATHER_CMD)} is disabled. The API key is missing.") } val owm = OWM(apiKey) val messages = mutableListOf<Message>() @@ -149,7 +149,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { for (w in list) { if (w != null) { condition.append(' ') - .append(w.getDescription().replaceFirstChar { it.uppercase() }) + .append(Utils.capitalize(w.getDescription())) .append('.') } } diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt index fbe1ffb..f62663d 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.capitalize import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.ensureDir @@ -77,6 +78,12 @@ class UtilsTest { Assertions.assertThat(bold(ascii)).`as`("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) } + @Test + fun testCapitalize() { + Assertions.assertThat(capitalize("test")).`as`("capitalize(test)").isEqualTo("Test") + Assertions.assertThat(capitalize("Test")).`as`("capitalize(Test)").isEqualTo("Test") + } + @Test fun testColorize() { Assertions.assertThat(colorize(ascii, Colors.REVERSE)).`as`("colorize(reverse)").isEqualTo( diff --git a/version.properties b/version.properties index 6d5aa2a..cffabaf 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Apr 26 18:00:07 PDT 2021 -version.buildmeta=462 +#Mon Apr 26 22:03:01 PDT 2021 +version.buildmeta=474 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+462 +version.semver=0.8.0-beta+474 From ae6220d421c68a8eb54594c135a7030dc4100e16 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 26 Apr 2021 22:15:54 -0700 Subject: [PATCH 477/842] Updated copyright. --- LICENSE.txt | 2 +- licenses/LICENSE.txt | 2 +- src/main/java/net/thauvin/erik/mobibot/Addons.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/Constants.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/FeedReader.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/Mobibot.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java | 2 +- src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../net/thauvin/erik/mobibot/commands/AbstractCommand.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Info.java | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Me.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Say.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/commands/Users.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/Versions.java | 2 +- .../java/net/thauvin/erik/mobibot/commands/links/Comment.kt | 2 +- .../net/thauvin/erik/mobibot/commands/links/LinksMgr.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/links/Posting.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/links/Tags.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/links/View.kt | 2 +- .../java/net/thauvin/erik/mobibot/commands/tell/Tell.kt | 2 +- .../thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt | 2 +- .../java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt | 2 +- .../java/net/thauvin/erik/mobibot/entries/EntryComment.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/AbstractModule.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../net/thauvin/erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/StockQuote.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/War.java | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- src/main/java/net/thauvin/erik/mobibot/msg/Message.kt | 2 +- src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt | 2 +- src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/tell/TellMessageTest.kt | 2 +- .../java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt | 2 +- src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt | 2 +- version.properties | 6 +++--- 60 files changed, 62 insertions(+), 62 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 37785b0..1cd28ec 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/licenses/LICENSE.txt b/licenses/LICENSE.txt index 37785b0..1cd28ec 100644 --- a/licenses/LICENSE.txt +++ b/licenses/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 147aea0..7651d91 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.java * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.kt b/src/main/java/net/thauvin/erik/mobibot/Constants.kt index 575e29a..317cc35 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt index 23b124a..0cba397 100644 --- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index 2d8b974..a6e4be9 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt index d5ce4c5..29460ed 100644 --- a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -1,7 +1,7 @@ /* * PinboardUtils.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index dbc7cb9..8396fce 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -1,7 +1,7 @@ /* * TwitterOAuth.java * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt index 5db5a93..0b8ad2a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt @@ -1,7 +1,7 @@ /* * TwitterTimer.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index 62f1695..a549ac3 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 338fbd6..2c02589 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt index 7f7f567..261a154 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -1,7 +1,7 @@ /* * AddLog.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 3eee724..637aec5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * Feed.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt index 49832f2..a62b87f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt index fa6b462..8c3c97b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt @@ -1,7 +1,7 @@ /* * Debug.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt index 57191bc..cdcc77f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index 5af7d4b..b758885 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -1,7 +1,7 @@ /* * InfoJ.java * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt index fa0861b..c668fe0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt index a9493fa..d2d780b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt index 31693c6..4e7aa4d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt index c6c7d2c..fa1ff2c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt index d695e46..364fc5f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt index 7651d9d..af08502 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt index 9fab49e..759b54c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 7413156..685684f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -1,7 +1,7 @@ /* * Version.java * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt index 35b644c..8b55a5d 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Links.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 80b4fbc..2ba9ae5 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -1,7 +1,7 @@ /* * Links.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt index 12a3f8d..ef993cb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Links.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt index 14ae0d2..1481166 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Links.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt index db34ffb..7bca9bd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * Links.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt index be1fb89..0890a25 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index 92e0e32..c11e23e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgr.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 9cbd247..0a3136a 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -1,7 +1,7 @@ /* * EntriesMgr.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt index 0052a0f..c1c4416 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt index 9596534..cbc6879 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 726f5e2..3448fa8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt index 5795480..31c928f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 96534a8..9542395 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt index fb20e9d..42eb680 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index a801f55..eeff387 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt index 9743441..8ac1786 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt index 71a3037..150888b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt index 470a453..ecfdf47 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 940c3a7..cb8a825 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt index e9c2905..f8e1a8f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt index 97a07e5..44a5e24 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt @@ -1,7 +1,7 @@ /* * ThreadedModule.java * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt index beb3793..a479fd1 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -1,7 +1,7 @@ /* * Twitter.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 2cfe28e..af9e1d4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,7 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt index 5a164b5..23f9665 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt index e1f11c3..ccda94b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt index 9987bd8..6cdf592 100644 --- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt index 564b7fb..0c7095e 100644 --- a/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index 5f58c91..d8ec28c 100644 --- a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -1,7 +1,7 @@ /* * PinboardUtilsTest.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 57cdb46..80dd85b 100644 --- a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 7e710f0..a7d3264 100644 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt index 6cc59a5..5a194a6 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 6797682..4394ceb 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 1c71cb0..bad8ca4 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 24d91d5..36c1680 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/version.properties b/version.properties index cffabaf..1e1926e 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Apr 26 22:03:01 PDT 2021 -version.buildmeta=474 +#Mon Apr 26 22:11:16 PDT 2021 +version.buildmeta=475 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+474 +version.semver=0.8.0-beta+475 From 2f4e6bdb2d5e9fff94aea29e87efa918054d939b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 27 Apr 2021 11:38:08 -0700 Subject: [PATCH 478/842] Cleaned up null-safety checks. --- src/main/java/net/thauvin/erik/mobibot/Utils.kt | 4 +--- src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt | 4 ++-- src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt | 2 ++ version.properties | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt index a549ac3..d5c3486 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt @@ -86,9 +86,7 @@ object Utils { * Capitalize a string. */ @JvmStatic - fun capitalize(s: String?): String { - return s!!.replaceFirstChar { it.uppercase() } - } + fun capitalize(s: String?): String? = s?.replaceFirstChar { it.uppercase() } /** * Colorize a string. diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt index cbc6879..08d10e7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -152,8 +152,8 @@ class EntryLink : Serializable { * Sets a comment. */ fun setComment(index: Int, comment: String?, nick: String?) { - if (index < comments.size) { - comments[index] = EntryComment(comment!!, nick!!) + if (index < comments.size && (comment != null) && !nick.isNullOrBlank()) { + comments[index] = EntryComment(comment, nick) } } diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt index f62663d..145ae7a 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt @@ -82,6 +82,8 @@ class UtilsTest { fun testCapitalize() { Assertions.assertThat(capitalize("test")).`as`("capitalize(test)").isEqualTo("Test") Assertions.assertThat(capitalize("Test")).`as`("capitalize(Test)").isEqualTo("Test") + Assertions.assertThat(capitalize(null)).`as`("captitalize(null)").isNull() + Assertions.assertThat(capitalize("")).`as`("capitalize()").isEqualTo("") } @Test diff --git a/version.properties b/version.properties index 1e1926e..8e5338f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Apr 26 22:11:16 PDT 2021 -version.buildmeta=475 +#Tue Apr 27 17:59:17 PDT 2021 +version.buildmeta=487 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+475 +version.semver=0.8.0-beta+487 From 21cbecff8e29ea1a67eec0357e68ef541a5e8afa Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 28 Apr 2021 08:23:14 -0700 Subject: [PATCH 479/842] Cleanup tests. --- .../net/thauvin/erik/mobibot/UtilsTest.kt | 66 +++++++++---------- .../mobibot/commands/tell/TellMessageTest.kt | 22 +++---- .../erik/mobibot/entries/EntryLinkTest.kt | 22 +++---- .../thauvin/erik/mobibot/modules/CalcTest.kt | 14 ++-- .../mobibot/modules/CurrencyConverterTest.kt | 16 ++--- .../thauvin/erik/mobibot/modules/DiceTest.kt | 12 +--- .../erik/mobibot/modules/GoogleSearchTest.kt | 17 ++--- .../thauvin/erik/mobibot/modules/JokeTest.kt | 6 +- .../erik/mobibot/modules/LookupTest.kt | 6 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 6 +- .../mobibot/modules/RockPaperScissorsTest.kt | 29 +++----- .../erik/mobibot/modules/StockQuoteTest.kt | 18 +++-- .../erik/mobibot/modules/TwitterTest.kt | 4 +- .../erik/mobibot/modules/Weather2Test.kt | 17 ++--- .../erik/mobibot/modules/WordTimeTest.kt | 8 +-- version.properties | 6 +- 16 files changed, 120 insertions(+), 149 deletions(-) diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt index 145ae7a..8a33943 100644 --- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt @@ -48,7 +48,7 @@ import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.Utils.utcDateTime import org.apache.commons.lang3.StringUtils -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.jibble.pircbot.Colors import org.testng.annotations.BeforeClass import org.testng.annotations.Test @@ -74,108 +74,106 @@ class UtilsTest { @Test fun testBold() { - Assertions.assertThat(bold(1)).`as`("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - Assertions.assertThat(bold(ascii)).`as`("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(bold(1)).`as`("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(bold(ascii)).`as`("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) } @Test fun testCapitalize() { - Assertions.assertThat(capitalize("test")).`as`("capitalize(test)").isEqualTo("Test") - Assertions.assertThat(capitalize("Test")).`as`("capitalize(Test)").isEqualTo("Test") - Assertions.assertThat(capitalize(null)).`as`("captitalize(null)").isNull() - Assertions.assertThat(capitalize("")).`as`("capitalize()").isEqualTo("") + assertThat(capitalize("test")).`as`("capitalize(test)").isEqualTo("Test") + assertThat(capitalize("Test")).`as`("capitalize(Test)").isEqualTo("Test") + assertThat(capitalize(null)).`as`("captitalize(null)").isNull() + assertThat(capitalize("")).`as`("capitalize()").isEqualTo("") } @Test fun testColorize() { - Assertions.assertThat(colorize(ascii, Colors.REVERSE)).`as`("colorize(reverse)").isEqualTo( + assertThat(colorize(ascii, Colors.REVERSE)).`as`("colorize(reverse)").isEqualTo( Colors.REVERSE + ascii + Colors.REVERSE ) - Assertions.assertThat(colorize(ascii, Colors.RED)).`as`("colorize(red)") - .isEqualTo(Colors.RED + ascii + Colors.NORMAL) - Assertions.assertThat(colorize(null, Colors.RED)).`as`("colorize(null)").isEqualTo(Colors.NORMAL) + assertThat(colorize(ascii, Colors.RED)).`as`("colorize(red)").isEqualTo(Colors.RED + ascii + Colors.NORMAL) + assertThat(colorize(null, Colors.RED)).`as`("colorize(null)").isEqualTo(Colors.NORMAL) } @Test fun testCyan() { - Assertions.assertThat(cyan(ascii)).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) + assertThat(cyan(ascii)).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) } @Test fun testEnsureDir() { - Assertions.assertThat(ensureDir("dir", false)).`as`("ensureDir(dir, false)") - .isEqualTo("dir" + File.separatorChar) - Assertions.assertThat(ensureDir("https://erik.thauvin.net", true)).`as`("ensureDir(erik.thauvin.net, true)") + assertThat(ensureDir("dir", false)).`as`("ensureDir(dir, false)").isEqualTo("dir" + File.separatorChar) + assertThat(ensureDir("https://erik.thauvin.net", true)).`as`("ensureDir(erik.thauvin.net, true)") .isEqualTo("https://erik.thauvin.net/") } @Test fun testGetIntProperty() { - Assertions.assertThat(getIntProperty("10", 1)).`as`("getIntProperty(10, 1)").isEqualTo(10) - Assertions.assertThat(getIntProperty("a", 1)).`as`("getIntProperty(a, 1)").isEqualTo(1) + assertThat(getIntProperty("10", 1)).`as`("getIntProperty(10, 1)").isEqualTo(10) + assertThat(getIntProperty("a", 1)).`as`("getIntProperty(a, 1)").isEqualTo(1) } @Test fun testGreen() { - Assertions.assertThat(green(ascii)).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) + assertThat(green(ascii)).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) } @Test fun testIsoLocalDate() { - Assertions.assertThat(isoLocalDate(cal.time)).`as`("isoLocalDate(date)").isEqualTo("1952-02-17") - Assertions.assertThat(isoLocalDate(localDateTime)).`as`("isoLocalDate(localDate)").isEqualTo("1952-02-17") + assertThat(isoLocalDate(cal.time)).`as`("isoLocalDate(date)").isEqualTo("1952-02-17") + assertThat(isoLocalDate(localDateTime)).`as`("isoLocalDate(localDate)").isEqualTo("1952-02-17") } @Test fun testObfuscate() { - Assertions.assertThat(obfuscate(ascii).length).`as`("obfuscate is right length").isEqualTo(ascii.length) - Assertions.assertThat(obfuscate(ascii)).`as`("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) - Assertions.assertThat(obfuscate(" ")).`as`("obfuscate(blank)").isEqualTo(" ") + assertThat(obfuscate(ascii).length).`as`("obfuscate is right length").isEqualTo(ascii.length) + assertThat(obfuscate(ascii)).`as`("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) + assertThat(obfuscate(" ")).`as`("obfuscate(blank)").isEqualTo(" ") } @Test fun testPlural() { val week = "week" val weeks = "weeks" - Assertions.assertThat(plural(-1, week, weeks)).`as`("plural(-1)").isEqualTo(week) - Assertions.assertThat(plural(0, week, weeks)).`as`("plural(0)").isEqualTo(week) - Assertions.assertThat(plural(1, week, weeks)).`as`("plural(1)").isEqualTo(week) - Assertions.assertThat(plural(2, week, weeks)).`as`("plural(2)").isEqualTo(weeks) + assertThat(plural(-1, week, weeks)).`as`("plural(-1)").isEqualTo(week) + assertThat(plural(0, week, weeks)).`as`("plural(0)").isEqualTo(week) + assertThat(plural(1, week, weeks)).`as`("plural(1)").isEqualTo(week) + assertThat(plural(2, week, weeks)).`as`("plural(2)").isEqualTo(weeks) } @Test fun testReverseColor() { - Assertions.assertThat(reverseColor(ascii)).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) + assertThat(reverseColor(ascii)).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) } @Test fun testToday() { - Assertions.assertThat(today()).isEqualTo(isoLocalDate(LocalDateTime.now())) + assertThat(today()).isEqualTo(isoLocalDate(LocalDateTime.now())) } @Test fun testUnescapeXml() { - Assertions.assertThat(unescapeXml("<a name="test & ''">")).isEqualTo( + assertThat(unescapeXml("<a name="test & ''">")).isEqualTo( "<a name=\"test & ''\">" ) } @Test fun testUptime() { - Assertions.assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(uptime(547800300076L)) + assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(uptime(547800300076L)) } @Test @Throws(IOException::class) fun testUrlReader() { - Assertions.assertThat(urlReader(URL("https://postman-echo.com/status/200"))).`as`("urlReader()").isEqualTo( + assertThat(urlReader(URL("https://postman-echo.com/status/200"))).`as`("urlReader()").isEqualTo( "{\"status\":200}" ) } @Test fun testUtcDateTime() { - Assertions.assertThat(utcDateTime(cal.time)).`as`("utcDateTime(date)").isEqualTo("1952-02-17 12:30") - Assertions.assertThat(utcDateTime(localDateTime)).`as`("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") + assertThat(utcDateTime(cal.time)).`as`("utcDateTime(date)").isEqualTo("1952-02-17 12:30") + assertThat(utcDateTime(localDateTime)).`as`("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 80dd85b..f0f0054 100644 --- a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot.commands.tell -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test import java.time.Duration import java.time.LocalDateTime @@ -51,17 +51,17 @@ class TellMessageTest { val recipient = "recipient" val sender = "sender" val tellMessage = TellMessage(sender, recipient, message) - Assertions.assertThat(tellMessage.sender).`as`(sender).isEqualTo(sender) - Assertions.assertThat(tellMessage.recipient).`as`(recipient).isEqualTo(recipient) - Assertions.assertThat(tellMessage.message).`as`(message).isEqualTo(message) - Assertions.assertThat(isValidDate(tellMessage.queued)).`as`("queued is valid date/time").isTrue - Assertions.assertThat(tellMessage.isMatch(sender)).`as`("match sender").isTrue - Assertions.assertThat(tellMessage.isMatch(recipient)).`as`("match recipient").isTrue - Assertions.assertThat(tellMessage.isMatch("foo")).`as`("foo is no match").isFalse + assertThat(tellMessage.sender).`as`(sender).isEqualTo(sender) + assertThat(tellMessage.recipient).`as`(recipient).isEqualTo(recipient) + assertThat(tellMessage.message).`as`(message).isEqualTo(message) + assertThat(isValidDate(tellMessage.queued)).`as`("queued is valid date/time").isTrue + assertThat(tellMessage.isMatch(sender)).`as`("match sender").isTrue + assertThat(tellMessage.isMatch(recipient)).`as`("match recipient").isTrue + assertThat(tellMessage.isMatch("foo")).`as`("foo is no match").isFalse tellMessage.isReceived = true - Assertions.assertThat(tellMessage.isReceived).`as`("is received").isTrue - Assertions.assertThat(isValidDate(tellMessage.receptionDate)).`as`("received is valid date/time").isTrue + assertThat(tellMessage.isReceived).`as`("is received").isTrue + assertThat(isValidDate(tellMessage.receptionDate)).`as`("received is valid date/time").isTrue tellMessage.isNotified = true - Assertions.assertThat(tellMessage.isNotified).`as`("is notified").isTrue + assertThat(tellMessage.isNotified).`as`("is notified").isTrue } } diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index a7d3264..dcb9378 100644 --- a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.entries import com.rometools.rome.feed.synd.SyndCategory -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test import java.security.SecureRandom @@ -56,37 +56,37 @@ class EntryLinkTest { entryLink.addComment("c$i", "u$i") i++ } - Assertions.assertThat(entryLink.comments.size).`as`("getComments().size() == 5").isEqualTo(i) + assertThat(entryLink.comments.size).`as`("getComments().size() == 5").isEqualTo(i) i = 0 for (comment in entryLink.comments) { - Assertions.assertThat(comment.comment).`as`("getComment($i)").isEqualTo("c$i") - Assertions.assertThat(comment.nick).`as`("getNick($i)").isEqualTo("u$i") + assertThat(comment.comment).`as`("getComment($i)").isEqualTo("c$i") + assertThat(comment.nick).`as`("getNick($i)").isEqualTo("u$i") i++ } val r = SecureRandom() while (entryLink.comments.size > 0) { entryLink.deleteComment(r.nextInt(entryLink.comments.size)) } - Assertions.assertThat(entryLink.comments.isNotEmpty()).`as`("hasComments()").isFalse + assertThat(entryLink.comments.isNotEmpty()).`as`("hasComments()").isFalse entryLink.addComment("nothing", "nobody") entryLink.setComment(0, "something", "somebody") - Assertions.assertThat(entryLink.getComment(0).nick).`as`("getNick(somebody)").isEqualTo("somebody") - Assertions.assertThat(entryLink.getComment(0).comment).`as`("getComment(something)").isEqualTo("something") + assertThat(entryLink.getComment(0).nick).`as`("getNick(somebody)").isEqualTo("somebody") + assertThat(entryLink.getComment(0).comment).`as`("getComment(something)").isEqualTo("something") } @Test fun testTags() { val tags: List<SyndCategory> = entryLink.tags for ((i, tag) in tags.withIndex()) { - Assertions.assertThat(tag.name).`as`("tag.getName($i)").isEqualTo("tag" + (i + 1)) + assertThat(tag.name).`as`("tag.getName($i)").isEqualTo("tag" + (i + 1)) } - Assertions.assertThat(entryLink.tags.size).`as`("getTags().size() is 5").isEqualTo(5) - Assertions.assertThat(entryLink.tags.isNotEmpty()).`as`("hasTags() is true").isTrue + assertThat(entryLink.tags.size).`as`("getTags().size() is 5").isEqualTo(5) + assertThat(entryLink.tags.isNotEmpty()).`as`("hasTags() is true").isTrue entryLink.setTags("-tag5") entryLink.setTags("+mobitopia") entryLink.setTags("tag4") entryLink.setTags("-mobitopia") - Assertions.assertThat(entryLink.pinboardTags).`as`("getPinboardTags()") + assertThat(entryLink.pinboardTags).`as`("getPinboardTags()") .isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt index 643dbd6..45f3848 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.modules.Calc.Companion.calc -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -42,13 +42,9 @@ import org.testng.annotations.Test class CalcTest { @Test fun testCalc() { - Assertions.assertThat(calc("1 + 1")).`as`("calc(1+1)") - .isEqualTo("1+1 = %s", Utils.bold(2)) - Assertions.assertThat(calc("1 -3")).`as`("calc(1 -3)") - .isEqualTo("1-3 = %s", Utils.bold(-2)) - Assertions.assertThat(calc("pi+π+e+φ")).`as`("calc(pi+π+e+φ)") - .isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) - Assertions.assertThat(calc("one + one")).`as`("calc(one + one)") - .startsWith("No idea.") + assertThat(calc("1 + 1")).`as`("calc(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)) + assertThat(calc("1 -3")).`as`("calc(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)) + assertThat(calc("pi+π+e+φ")).`as`("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) + assertThat(calc("one + one")).`as`("calc(one + one)").startsWith("No idea.") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index edcbb65..0fbfedd 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.BeforeClass import org.testng.annotations.Test @@ -50,15 +50,11 @@ class CurrencyConverterTest { @Test fun testConvertCurrency() { - Assertions.assertThat(convertCurrency("100 USD to EUR").msg) + assertThat(convertCurrency("100 USD to EUR").msg) .`as`("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR") - Assertions.assertThat(convertCurrency("100 USD to USD").msg) - .`as`("100 USD to USD").contains("You're kidding, right?") - Assertions.assertThat(convertCurrency("100 USD").msg) - .`as`("100 USD").contains("Invalid query.") - Assertions.assertThat(currencyRates().size) - .`as`("currencyRates().size() == 33").isEqualTo(33) - Assertions.assertThat(currencyRates()) - .`as`("currencyRates().get(EUR)").contains(" EUR: 1") + assertThat(convertCurrency("100 USD to USD").msg).`as`("100 USD to USD").contains("You're kidding, right?") + assertThat(convertCurrency("100 USD").msg).`as`("100 USD").contains("Invalid query.") + assertThat(currencyRates().size).`as`("currencyRates().size() == 33").isEqualTo(33) + assertThat(currencyRates()).`as`("currencyRates().get(EUR)").contains(" EUR: 1") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt index 5a194a6..cea3e35 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -39,14 +39,8 @@ import org.testng.annotations.Test class DiceTest { @Test fun testWinLoseOrTie() { - assertThat( - Dice.winLoseOrTie(6, 6) - ).`as`("6 vs. 6").isEqualTo(Dice.Result.TIE) - assertThat( - Dice.winLoseOrTie(6, 5) - ).`as`("6 vs. 5").isEqualTo(Dice.Result.WIN) - assertThat( - Dice.winLoseOrTie(5, 6) - ).`as`("5 vs. 6").isEqualTo(Dice.Result.LOSE) + assertThat(Dice.winLoseOrTie(6, 6)).`as`("6 vs. 6").isEqualTo(Dice.Result.TIE) + assertThat(Dice.winLoseOrTie(6, 5)).`as`("6 vs. 5").isEqualTo(Dice.Result.WIN) + assertThat(Dice.winLoseOrTie(5, 6)).`as`("5 vs. 6").isEqualTo(Dice.Result.LOSE) } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 4394ceb..c7f0aef 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -33,7 +33,8 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test /** @@ -47,18 +48,18 @@ class GoogleSearchTest : LocalProperties() { val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) try { var messages = searchGoogle("mobibot site:github.com", apiKey, cseKey) - Assertions.assertThat(messages).`as`("mobibot results not empty").isNotEmpty - Assertions.assertThat(messages[0].msg).`as`("found mobitopia").contains("mobibot") + assertThat(messages).`as`("mobibot results not empty").isNotEmpty + assertThat(messages[0].msg).`as`("found mobitopia").contains("mobibot") messages = searchGoogle("aapl", apiKey, cseKey) - Assertions.assertThat(messages).`as`("aapl results not empty").isNotEmpty - Assertions.assertThat(messages[0].msg).`as`("found apple").containsIgnoringCase("apple") - Assertions.assertThatThrownBy { searchGoogle("test", "", "apiKey") } + assertThat(messages).`as`("aapl results not empty").isNotEmpty + assertThat(messages[0].msg).`as`("found apple").containsIgnoringCase("apple") + assertThatThrownBy { searchGoogle("test", "", "apiKey") } .`as`("no API key") .isInstanceOf(ModuleException::class.java).hasNoCause() - Assertions.assertThatThrownBy { searchGoogle("test", "apiKey", "") } + assertThatThrownBy { searchGoogle("test", "apiKey", "") } .`as`("no CSE API key") .isInstanceOf(ModuleException::class.java).hasNoCause() - Assertions.assertThatThrownBy { searchGoogle("", "apikey", "apiKey") } + assertThatThrownBy { searchGoogle("", "apikey", "apiKey") } .`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt index 5a2f221..36156e2 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -42,7 +42,7 @@ class JokeTest { @Test @Throws(ModuleException::class) fun testRamdomJoke() { - Assertions.assertThat(randomJoke().msg.isNotEmpty()).`as`("randomJoke() > 0").isTrue - Assertions.assertThat(randomJoke().msg).`as`("randomJoke()").containsIgnoringCase("chuck") + assertThat(randomJoke().msg.isNotEmpty()).`as`("randomJoke() > 0").isTrue + assertThat(randomJoke().msg).`as`("randomJoke()").containsIgnoringCase("chuck") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt index fdbb51c..b623d83 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.modules.Lookup.Companion.lookup import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -44,14 +44,14 @@ class LookupTest { @Throws(Exception::class) fun testLookup() { val result = lookup("apple.com") - Assertions.assertThat(result).`as`("lookup(apple.com)").contains("17.253.144.10") + assertThat(result).`as`("lookup(apple.com)").contains("17.253.144.10") } @Test @Throws(Exception::class) fun testWhois() { val result = whois("17.178.96.59", Lookup.WHOIS_HOST) - Assertions.assertThat(result.stream().anyMatch { m: String -> m.contains("Apple Inc.") }) + assertThat(result.stream().anyMatch { m: String -> m.contains("Apple Inc.") }) .`as`("whois(17.178.96.59/Apple Inc.").isTrue } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt index 1b0c460..b849192 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -41,13 +41,13 @@ import org.testng.annotations.Test class PingTest { @Test fun testPingsArray() { - Assertions.assertThat(Ping.PINGS).`as`("Pings array is not empty.").isNotEmpty + assertThat(Ping.PINGS).`as`("Pings array is not empty.").isNotEmpty } @Test fun testRandomPing() { for (i in 0..9) { - Assertions.assertThat(randomPing()).`as`("Random ping $i").isIn(Ping.PINGS) + assertThat(randomPing()).`as`("Random ping $i").isIn(Ping.PINGS) } } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index bad8ca4..34cf7bb 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -38,26 +38,13 @@ import org.testng.annotations.Test class RockPaperScissorsTest { @Test fun testWinLoseOrDraw() { - assertThat( - RockPaperScissors.winLoseOrDraw("scissors", "paper") - ).`as`("scissors vs. paper").isEqualTo("win") - assertThat( - RockPaperScissors.winLoseOrDraw("paper", "rock") - ).`as`("paper vs. rock").isEqualTo("win") - assertThat( - RockPaperScissors.winLoseOrDraw("rock", "scissors") - ).`as`("rock vs. scissors").isEqualTo("win") - assertThat( - RockPaperScissors.winLoseOrDraw("paper", "scissors") - ).`as`("paper vs. scissors").isEqualTo("lose") - assertThat( - RockPaperScissors.winLoseOrDraw("rock", "paper") - ).`as`("rock vs. paper").isEqualTo("lose") - assertThat( - RockPaperScissors.winLoseOrDraw("scissors", "rock") - ).`as`("scissors vs. rock").isEqualTo("lose") - assertThat( - RockPaperScissors.winLoseOrDraw("scissors", "scissors") - ).`as`("scissors vs. scissors").isEqualTo("draw") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper")).`as`("scissors vs. paper").isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock")).`as`("paper vs. rock").isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors")).`as`("rock vs. scissors").isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors")).`as`("paper vs. scissors").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper")).`as`("rock vs. paper").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock")).`as`("scissors vs. rock").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "scissors")) + .`as`("scissors vs. scissors").isEqualTo("draw") } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 36c1680..5053d22 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -33,7 +33,8 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test /** @@ -46,20 +47,17 @@ class StockQuoteTest : LocalProperties() { val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP) try { val messages = getQuote("apple inc", apiKey) - Assertions.assertThat(messages).`as`("response not empty").isNotEmpty - Assertions.assertThat(messages[0].msg).`as`("same stock symbol") - .startsWith("Symbol: AAPL") - Assertions.assertThat(messages[1].msg).`as`("price label") - .startsWith(" Price: ") + assertThat(messages).`as`("response not empty").isNotEmpty + assertThat(messages[0].msg).`as`("same stock symbol").startsWith("Symbol: AAPL") + assertThat(messages[1].msg).`as`("price label").startsWith(" Price: ") try { getQuote("blahfoo", apiKey) } catch (e: ModuleException) { - Assertions.assertThat(e.message).`as`("invalid symbol") - .containsIgnoringCase(StockQuote.INVALID_SYMBOL) + assertThat(e.message).`as`("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL) } - Assertions.assertThatThrownBy { getQuote("test", "") }.`as`("no API key") + assertThatThrownBy { getQuote("test", "") }.`as`("no API key") .isInstanceOf(ModuleException::class.java).hasNoCause() - Assertions.assertThatThrownBy { getQuote("", "apikey") }.`as`("no symbol") + assertThatThrownBy { getQuote("", "apikey") }.`as`("no symbol") .isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt index da9b26a..4c309c6 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.Twitter.Companion.twitterPost -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test import java.net.InetAddress import java.net.UnknownHostException @@ -56,7 +56,7 @@ class TwitterTest : LocalProperties() { @Throws(ModuleException::class) fun testPostTwitter() { val msg = "Testing Twitter API from $ci" - Assertions.assertThat( + assertThat( twitterPost( getProperty(Twitter.CONSUMER_KEY_PROP), getProperty(Twitter.CONSUMER_SECRET_PROP), diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 4364585..0d9570c 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -34,7 +34,8 @@ package net.thauvin.erik.mobibot.modules import net.aksingh.owmjapis.api.APIException import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test /** @@ -45,16 +46,16 @@ class Weather2Test : LocalProperties() { @Throws(ModuleException::class) fun testWeather() { var messages = getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP)) - Assertions.assertThat(messages[0].msg).`as`("is Everett").contains("Everett").contains("US") - Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Search").endsWith("98204%2CUS") + assertThat(messages[0].msg).`as`("is Everett").contains("Everett").contains("US") + assertThat(messages[messages.size - 1].msg).`as`("is City Search").endsWith("98204%2CUS") messages = getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP)) - Assertions.assertThat(messages[0].msg).`as`("is UK").contains("London").contains("UK") - Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Code").endsWith("4517009") - Assertions.assertThatThrownBy { getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)) } + assertThat(messages[0].msg).`as`("is UK").contains("London").contains("UK") + assertThat(messages[messages.size - 1].msg).`as`("is City Code").endsWith("4517009") + assertThatThrownBy { getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)) } .`as`("Montpellier not found").hasCauseInstanceOf(APIException::class.java) - Assertions.assertThatThrownBy { getWeather("test", "") } + assertThatThrownBy { getWeather("test", "") } .`as`("no API key").isInstanceOf(ModuleException::class.java).hasNoCause() messages = getWeather("", "apikey") - Assertions.assertThat(messages[0].isError).`as`("no query").isTrue + assertThat(messages[0].isError).`as`("no query").isTrue } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index b6a40aa..dd696c9 100644 --- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.modules.WorldTime.Companion.worldTime -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -42,8 +42,8 @@ import org.testng.annotations.Test class WordTimeTest { @Test fun testWorldTime() { - Assertions.assertThat(worldTime("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles")) - Assertions.assertThat(worldTime("BLAH").isError).`as`("BLAH").isTrue - Assertions.assertThat(worldTime("BEATS").msg).`as`("BEATS").contains("@") + assertThat(worldTime("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles")) + assertThat(worldTime("BLAH").isError).`as`("BLAH").isTrue + assertThat(worldTime("BEATS").msg).`as`("BEATS").contains("@") } } diff --git a/version.properties b/version.properties index 8e5338f..bf0ed79 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Apr 27 17:59:17 PDT 2021 -version.buildmeta=487 +#Wed Apr 28 08:21:37 PDT 2021 +version.buildmeta=494 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+487 +version.semver=0.8.0-beta+494 From b8cecdad1daa61c80b6cb19ba5eeddc2dbd34a8e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 28 Apr 2021 17:37:05 -0700 Subject: [PATCH 480/842] Cleanup. --- src/main/java/net/thauvin/erik/mobibot/Addons.kt | 4 +--- src/main/java/net/thauvin/erik/mobibot/commands/Info.java | 2 +- .../java/net/thauvin/erik/mobibot/commands/Versions.java | 2 +- version.properties | 6 +++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 7651d91..72f4dcd 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -98,11 +98,9 @@ class Addons { } } for (module in modules) { - if ((isPrivate && module.isPrivateMsgEnabled) || !isPrivate) { - if (module.commands.contains(cmd)) { + if (((isPrivate && module.isPrivateMsgEnabled) || !isPrivate) && module.commands.contains(cmd)) { module.commandResponse(sender, cmd, args, isPrivate) return true - } } } return false diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java index b758885..8597c8c 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java @@ -1,5 +1,5 @@ /* - * InfoJ.java + * Info.java * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java index 685684f..a86493f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java @@ -1,5 +1,5 @@ /* - * Version.java + * Versions.java * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/version.properties b/version.properties index bf0ed79..8936e89 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Apr 28 08:21:37 PDT 2021 -version.buildmeta=494 +#Wed Apr 28 17:17:23 PDT 2021 +version.buildmeta=498 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+494 +version.semver=0.8.0-beta+498 From 2c78d713eb63a58c7527efb1782650a8ece782bf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 28 Apr 2021 19:50:29 -0700 Subject: [PATCH 481/842] Using Jacoco snapshot. Using src/main/java and src/main/kotlin. --- build.gradle | 5 +++-- .../{java => kotlin}/net/thauvin/erik/mobibot/Addons.kt | 0 .../{java => kotlin}/net/thauvin/erik/mobibot/Constants.kt | 0 .../{java => kotlin}/net/thauvin/erik/mobibot/FeedReader.kt | 0 .../{java => kotlin}/net/thauvin/erik/mobibot/Mobibot.kt | 0 .../net/thauvin/erik/mobibot/PinboardUtils.kt | 0 .../net/thauvin/erik/mobibot/TwitterTimer.kt | 0 src/main/{java => kotlin}/net/thauvin/erik/mobibot/Utils.kt | 0 .../net/thauvin/erik/mobibot/commands/AbstractCommand.kt | 0 .../net/thauvin/erik/mobibot/commands/AddLog.kt | 0 .../net/thauvin/erik/mobibot/commands/ChannelFeed.kt | 0 .../net/thauvin/erik/mobibot/commands/Cycle.kt | 0 .../net/thauvin/erik/mobibot/commands/Debug.kt | 0 .../net/thauvin/erik/mobibot/commands/Ignore.kt | 0 .../net/thauvin/erik/mobibot/commands/Me.kt | 0 .../net/thauvin/erik/mobibot/commands/Modules.kt | 0 .../net/thauvin/erik/mobibot/commands/Msg.kt | 0 .../net/thauvin/erik/mobibot/commands/Nick.kt | 0 .../net/thauvin/erik/mobibot/commands/Recap.kt | 0 .../net/thauvin/erik/mobibot/commands/Say.kt | 0 .../net/thauvin/erik/mobibot/commands/Users.kt | 0 .../net/thauvin/erik/mobibot/commands/links/Comment.kt | 0 .../net/thauvin/erik/mobibot/commands/links/LinksMgr.kt | 0 .../net/thauvin/erik/mobibot/commands/links/Posting.kt | 0 .../net/thauvin/erik/mobibot/commands/links/Tags.kt | 0 .../net/thauvin/erik/mobibot/commands/links/View.kt | 0 .../net/thauvin/erik/mobibot/commands/tell/Tell.kt | 0 .../net/thauvin/erik/mobibot/commands/tell/TellMessage.kt | 0 .../thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt | 0 .../net/thauvin/erik/mobibot/entries/EntriesMgr.kt | 0 .../net/thauvin/erik/mobibot/entries/EntriesUtils.kt | 0 .../net/thauvin/erik/mobibot/entries/EntryComment.kt | 0 .../net/thauvin/erik/mobibot/entries/EntryLink.kt | 0 .../net/thauvin/erik/mobibot/modules/AbstractModule.kt | 0 .../net/thauvin/erik/mobibot/modules/Calc.kt | 0 .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 0 .../net/thauvin/erik/mobibot/modules/Dice.kt | 0 .../net/thauvin/erik/mobibot/modules/GoogleSearch.kt | 0 .../net/thauvin/erik/mobibot/modules/Joke.kt | 0 .../net/thauvin/erik/mobibot/modules/Lookup.kt | 0 .../net/thauvin/erik/mobibot/modules/ModuleException.kt | 0 .../net/thauvin/erik/mobibot/modules/Ping.kt | 0 .../net/thauvin/erik/mobibot/modules/RockPaperScissors.kt | 0 .../net/thauvin/erik/mobibot/modules/StockQuote.kt | 0 .../net/thauvin/erik/mobibot/modules/ThreadedModule.kt | 0 .../net/thauvin/erik/mobibot/modules/Twitter.kt | 0 .../net/thauvin/erik/mobibot/modules/Weather2.kt | 0 .../net/thauvin/erik/mobibot/modules/WorldTime.kt | 0 .../net/thauvin/erik/mobibot/msg/ErrorMessage.kt | 0 .../net/thauvin/erik/mobibot/msg/Message.kt | 0 .../net/thauvin/erik/mobibot/msg/NoticeMessage.kt | 0 .../net/thauvin/erik/mobibot/msg/PrivateMessage.kt | 0 .../net/thauvin/erik/mobibot/msg/PublicMessage.kt | 0 .../net/thauvin/erik/mobibot/LocalProperties.kt | 0 .../net/thauvin/erik/mobibot/PinboardUtilsTest.kt | 0 .../{java => kotlin}/net/thauvin/erik/mobibot/UtilsTest.kt | 0 .../thauvin/erik/mobibot/commands/tell/TellMessageTest.kt | 0 .../net/thauvin/erik/mobibot/entries/EntryLinkTest.kt | 0 .../net/thauvin/erik/mobibot/modules/CalcTest.kt | 0 .../thauvin/erik/mobibot/modules/CurrencyConverterTest.kt | 0 .../net/thauvin/erik/mobibot/modules/DiceTest.kt | 0 .../net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt | 0 .../net/thauvin/erik/mobibot/modules/JokeTest.kt | 0 .../net/thauvin/erik/mobibot/modules/LookupTest.kt | 0 .../net/thauvin/erik/mobibot/modules/PingTest.kt | 0 .../thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt | 0 .../net/thauvin/erik/mobibot/modules/StockQuoteTest.kt | 0 .../net/thauvin/erik/mobibot/modules/TwitterTest.kt | 0 .../net/thauvin/erik/mobibot/modules/Weather2Test.kt | 0 .../net/thauvin/erik/mobibot/modules/WordTimeTest.kt | 0 version.properties | 6 +++--- 71 files changed, 6 insertions(+), 5 deletions(-) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/Addons.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/Constants.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/FeedReader.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/Mobibot.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/PinboardUtils.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/TwitterTimer.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/Utils.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/AbstractCommand.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/AddLog.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/ChannelFeed.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Cycle.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Debug.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Ignore.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Me.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Modules.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Msg.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Nick.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Recap.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Say.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/Users.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/links/Comment.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/links/Posting.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/links/Tags.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/links/View.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/tell/Tell.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/entries/EntriesMgr.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/entries/EntriesUtils.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/entries/EntryComment.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/entries/EntryLink.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/AbstractModule.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/Calc.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/Dice.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/GoogleSearch.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/Joke.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/Lookup.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/ModuleException.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/Ping.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/StockQuote.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/ThreadedModule.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/Twitter.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/Weather2.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/modules/WorldTime.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/msg/ErrorMessage.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/msg/Message.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/msg/NoticeMessage.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/msg/PrivateMessage.kt (100%) rename src/main/{java => kotlin}/net/thauvin/erik/mobibot/msg/PublicMessage.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/LocalProperties.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/PinboardUtilsTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/UtilsTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/CalcTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/DiceTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/JokeTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/LookupTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/PingTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/TwitterTest.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/Weather2Test.kt (100%) rename src/test/{java => kotlin}/net/thauvin/erik/mobibot/modules/WordTimeTest.kt (100%) diff --git a/build.gradle b/build.gradle index 0644bfb..b7f043f 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,6 @@ final def deployDir = 'deploy' final def semverProcessor = "net.thauvin.erik:semver:1.2.0" ext.versions = [ - jacoco : '0.8.6', log4j : '2.14.1', pmd : '6.34.0', spotbugs: '4.2.3' @@ -31,6 +30,7 @@ repositories { mavenLocal() mavenCentral() jcenter() + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } } dependencies { @@ -170,10 +170,11 @@ sonarqube { } jacoco { - toolVersion = versions.jacoco + toolVersion = '0.8.7-SNAPSHOT' } jacocoTestReport { + dependsOn test reports { html.enabled = true xml.enabled = true diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/Addons.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/Constants.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/FeedReader.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/Mobibot.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/PinboardUtils.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/TwitterTimer.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/Utils.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/AddLog.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/ChannelFeed.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Debug.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Debug.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Debug.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Ignore.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Me.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Modules.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Msg.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Nick.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Recap.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Say.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/Users.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/links/Comment.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/links/Posting.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/links/Tags.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/links/View.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/entries/EntryComment.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/msg/Message.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt similarity index 100% rename from src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/LocalProperties.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/PinboardUtilsTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/UtilsTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt similarity index 100% rename from src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt diff --git a/version.properties b/version.properties index 8936e89..2ade38c 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Apr 28 17:17:23 PDT 2021 -version.buildmeta=498 +#Wed Apr 28 19:39:18 PDT 2021 +version.buildmeta=525 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+498 +version.semver=0.8.0-beta+525 From 8a945b2dc6d0a533468e19d5e958a5bcc04ca706 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 29 Apr 2021 19:07:41 -0700 Subject: [PATCH 482/842] Switched to detekt snapshot for now. --- build.gradle | 6 +++--- config/detekt/baseline.xml | 1 + version.properties | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index b7f043f..65a28a8 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.38.0' - id 'com.github.spotbugs' version '4.7.0' + id 'com.github.spotbugs' version '4.7.1' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.16.0' id 'jacoco' @@ -10,7 +10,7 @@ plugins { id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.5.0' id 'org.jetbrains.kotlin.kapt' version '1.5.0' - id 'org.sonarqube' version '3.1.1' + id 'org.sonarqube' version '3.2.0' id 'pmd' } @@ -29,7 +29,6 @@ ext.versions = [ repositories { mavenLocal() mavenCentral() - jcenter() maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } } @@ -127,6 +126,7 @@ pmd { } detekt { + toolVersion = "main-SNAPSHOT" ignoreFailures = true // Baseline is incomplete and fails on various CIs, not sure why. baseline = file("${projectDir}/config/detekt/baseline.xml") } diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 7d83c69..91a5e9c 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,6 +2,7 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> + <ID>ComplexCondition:Addons.kt$Addons$((isPrivate && module.isPrivateMsgEnabled) || !isPrivate) && module.commands.contains(cmd)</ID> <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> diff --git a/version.properties b/version.properties index 2ade38c..b0273e9 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Apr 28 19:39:18 PDT 2021 -version.buildmeta=525 +#Thu Apr 29 18:56:59 PDT 2021 +version.buildmeta=526 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+525 +version.semver=0.8.0-beta+526 From f92f7d26b1d5f5a056e4cc719ac0b927e633b897 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 29 Apr 2021 19:59:08 -0700 Subject: [PATCH 483/842] Renamed ReleaseInfo.mustache to version.mustache. Added deploy script. --- ReleaseInfo.mustache | 32 ----------- deploy.sh | 13 +++++ .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- version.mustache | 54 +++++++++---------- version.properties | 6 +-- 5 files changed, 42 insertions(+), 65 deletions(-) delete mode 100644 ReleaseInfo.mustache create mode 100755 deploy.sh diff --git a/ReleaseInfo.mustache b/ReleaseInfo.mustache deleted file mode 100644 index b2672e4..0000000 --- a/ReleaseInfo.mustache +++ /dev/null @@ -1,32 +0,0 @@ -/* -* This file is automatically generated. -* Do not modify! -- ALL CHANGES WILL BE ERASED! -*/ -package {{packageName}} - -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.Instant - -/** -* Provides semantic version information. -* -* @author [Semantic Version Annotation Processor](https://github.com/ethauvin/semver) -*/ -object ReleaseInfo{ - const val PROJECT = "{{project}}" - const val VERSION = "{{version}}" - - @JvmField - val BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()) - - const val MAJOR = {{major}} - const val MINOR = {{minor}} - const val PATCH = {{patch}} - const val BUILDMETA = "{{buildMeta}}" - const val PRERELEASE = "{{preRelease}}" - - const val WEBSITE = "https://www.mobitopia.org/mobibot/" - const val AUTHOR = "Erik C. Thauvin" - const val AUTHOR_URL = "https://erik.thauvin.net/" -} diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..9ac698d --- /dev/null +++ b/deploy.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +DEPLOYDIR=/home/erik/mobitopia/mobibot + +if [ -f "deploy/mobibot.jar" ] +then + /bin/cp deploy/mobibot.jar $DEPLOYDIR + rm -rf $DEPLOYDIR/lib/*.jar + cp deploy/lib/*.jar $DEPLOYDIR/lib + chmod 755 $DEPLOYDIR/*.jar $DEPLOYDIR/lib/*.jar +else + echo "mobibot.jar not found." +fi diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index a6e4be9..a230ec3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -106,7 +106,7 @@ import kotlin.system.exitProcess /** * Implements the #mobitopia bot. */ -@Version(properties = "version.properties", className = "ReleaseInfo", template = "ReleaseInfo.mustache", type = "kt") +@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : PircBot() { // Commands and Modules private val addons = Addons() diff --git a/version.mustache b/version.mustache index a090f01..b2672e4 100644 --- a/version.mustache +++ b/version.mustache @@ -1,36 +1,32 @@ /* - * This file is automatically generated. - * Do not modify! -- ALL CHANGES WILL BE ERASED! - */ -package {{packageName}}; +* This file is automatically generated. +* Do not modify! -- ALL CHANGES WILL BE ERASED! +*/ +package {{packageName}} -import java.time.*; +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.Instant /** - * Provides semantic version information. - * - * @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a> - */ -public final class {{className}} { - public static final String PROJECT = "{{project}}"; - public static final String VERSION = "{{version}}"; - public static final LocalDateTime BUILDDATE = - LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()); +* Provides semantic version information. +* +* @author [Semantic Version Annotation Processor](https://github.com/ethauvin/semver) +*/ +object ReleaseInfo{ + const val PROJECT = "{{project}}" + const val VERSION = "{{version}}" + + @JvmField + val BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()) - public static final int MAJOR = {{major}}; - public static final int MINOR = {{minor}}; - public static final int PATCH = {{patch}}; - public static final String BUILDMETA = "{{buildMeta}}"; - public static final String PRERELEASE = "{{preRelease}}"; + const val MAJOR = {{major}} + const val MINOR = {{minor}} + const val PATCH = {{patch}} + const val BUILDMETA = "{{buildMeta}}" + const val PRERELEASE = "{{preRelease}}" - public static final String WEBSITE = "https://www.mobitopia.org/mobibot/"; - public static final String AUTHOR = "Erik C. Thauvin"; - public static final String AUTHOR_URL = "https://erik.thauvin.net/"; - - /** - * Disables the default constructor. - */ - private {{className}}() { - throw new UnsupportedOperationException("Illegal constructor call."); - } + const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val AUTHOR = "Erik C. Thauvin" + const val AUTHOR_URL = "https://erik.thauvin.net/" } diff --git a/version.properties b/version.properties index b0273e9..4842225 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Apr 29 18:56:59 PDT 2021 -version.buildmeta=526 +#Thu Apr 29 19:35:53 PDT 2021 +version.buildmeta=528 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+526 +version.semver=0.8.0-beta+528 From b58cd6b11e99f45be533533609d61e4adbaa0bd3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 29 Apr 2021 20:22:39 -0700 Subject: [PATCH 484/842] Moved to OpenJDK 15.0.2 --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 20a8cc6..41e99be 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,11 +30,11 @@ defaults_gradle: &defaults_gradle path: build/reports/ jobs: - build_gradle_jdk14: + build_gradle_jdk15: <<: *defaults docker: - - image: cimg/openjdk:14.0.1 + - image: cimg/openjdk:15.0.2 <<: *defaults_gradle @@ -51,5 +51,5 @@ workflows: gradle: jobs: - build_gradle_jdk11 - - build_gradle_jdk14 + - build_gradle_jdk15 From 864bf99c8eb4d84c7fa4e625fea6c43cc57f114f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 1 May 2021 10:38:29 -0700 Subject: [PATCH 485/842] Fixed detekt baseline. --- build.gradle | 1 - config/detekt/baseline.xml | 44 ++++++++++++++++++++++++++++++++++++++ version.properties | 6 +++--- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 65a28a8..34cd8a7 100644 --- a/build.gradle +++ b/build.gradle @@ -127,7 +127,6 @@ pmd { detekt { toolVersion = "main-SNAPSHOT" - ignoreFailures = true // Baseline is incomplete and fails on various CIs, not sure why. baseline = file("${projectDir}/config/detekt/baseline.xml") } diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 91a5e9c..263aa71 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -15,6 +15,42 @@ <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> + <ID>MagicNumber:AddLog.kt$AddLog$4</ID> + <ID>MagicNumber:Comment.kt$Comment$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID> + <ID>MagicNumber:Cycle.kt$Cycle$10</ID> + <ID>MagicNumber:Dice.kt$Dice$7</ID> + <ID>MagicNumber:FeedReader.kt$FeedReader$5</ID> + <ID>MagicNumber:Ignore.kt$Ignore$8</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$10</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$1000L</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$3</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$5</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$8</ID> + <ID>MagicNumber:Modules.kt$Modules$7</ID> + <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> + <ID>MagicNumber:Tell.kt$Tell$50</ID> + <ID>MagicNumber:Tell.kt$Tell$7</ID> + <ID>MagicNumber:Twitter.kt$Twitter$1000L</ID> + <ID>MagicNumber:Twitter.kt$Twitter$60L</ID> + <ID>MagicNumber:Users.kt$Users$8</ID> + <ID>MagicNumber:Utils.kt$Utils$30</ID> + <ID>MagicNumber:Utils.kt$Utils$365</ID> + <ID>MagicNumber:Utils.kt$Utils$7</ID> + <ID>MagicNumber:View.kt$View$8</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime$17</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> <ID>MemberNameEqualsClassName:Calc.kt$Calc.Companion$ @JvmStatic fun calc(query: String): String</ID> <ID>MemberNameEqualsClassName:Lookup.kt$Lookup.Companion$ @JvmStatic @Throws(UnknownHostException::class) fun lookup(query: String): String</ID> <ID>MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): Message</ID> @@ -48,5 +84,13 @@ <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> + <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID> + <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID> + <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> + <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> + <ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID> + <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> + <ID>TooManyFunctions:Utils.kt$Utils$Utils</ID> </CurrentIssues> </SmellBaseline> diff --git a/version.properties b/version.properties index 4842225..e99ce5a 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Apr 29 19:35:53 PDT 2021 -version.buildmeta=528 +#Fri Apr 30 21:34:46 PDT 2021 +version.buildmeta=538 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+528 +version.semver=0.8.0-beta+538 From 67f618da7141991e4cae3ff9c76d813f344bcfce Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 1 May 2021 13:42:12 -0700 Subject: [PATCH 486/842] Updated detekt to 1.7.0-RC1 --- build.gradle | 2 +- config/detekt/baseline.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 34cd8a7..25c909e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'com.github.ben-manes.versions' version '0.38.0' id 'com.github.spotbugs' version '4.7.1' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.16.0' + id 'io.gitlab.arturbosch.detekt' version '1.17.0-RC1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 263aa71..b5b10a7 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -80,6 +80,9 @@ <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>SwallowedException:Calc.kt$Calc.Companion$catch (e: IllegalArgumentException) { "No idea. This is the kind of math I don't get." }</ID> + <ID>SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank() && cseKey.isNotBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey)) } else { throw e } }</ID> + <ID>SwallowedException:StockQuoteTest.kt$StockQuoteTest$catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey)) } else { throw e } }</ID> + <ID>SwallowedException:TwitterTest.kt$TwitterTest$catch (e: UnknownHostException) { "Unknown Host" }</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> From 9f3dc273f0ab190d7e021718560b66603684b15b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 1 May 2021 21:46:33 -0700 Subject: [PATCH 487/842] Cleanup. --- config/detekt/baseline.xml | 9 -------- .../net/thauvin/erik/mobibot/Mobibot.kt | 18 +++++++-------- .../net/thauvin/erik/mobibot/modules/Calc.kt | 22 ++++++++++++------- .../thauvin/erik/mobibot/modules/Lookup.kt | 4 ++-- .../thauvin/erik/mobibot/modules/WorldTime.kt | 6 ++--- .../thauvin/erik/mobibot/modules/CalcTest.kt | 15 ++++++++----- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 4 ++-- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/TwitterTest.kt | 2 +- .../erik/mobibot/modules/WordTimeTest.kt | 10 ++++----- version.properties | 6 ++--- 12 files changed, 49 insertions(+), 51 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index b5b10a7..2080b7b 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -51,9 +51,6 @@ <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> - <ID>MemberNameEqualsClassName:Calc.kt$Calc.Companion$ @JvmStatic fun calc(query: String): String</ID> - <ID>MemberNameEqualsClassName:Lookup.kt$Lookup.Companion$ @JvmStatic @Throws(UnknownHostException::class) fun lookup(query: String): String</ID> - <ID>MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): Message</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> @@ -75,14 +72,8 @@ <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> - <ID>PrintStackTrace:Mobibot.kt$Mobibot$e</ID> - <ID>PrintStackTrace:Mobibot.kt$Mobibot.Companion$e</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> - <ID>SwallowedException:Calc.kt$Calc.Companion$catch (e: IllegalArgumentException) { "No idea. This is the kind of math I don't get." }</ID> - <ID>SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank() && cseKey.isNotBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey)) } else { throw e } }</ID> - <ID>SwallowedException:StockQuoteTest.kt$StockQuoteTest$catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank()) { throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey)) } else { throw e } }</ID> - <ID>SwallowedException:TwitterTest.kt$TwitterTest$catch (e: UnknownHostException) { "Unknown Host" }</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index a230ec3..ffa13a5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -202,6 +202,9 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert try { connect(ircServer, ircPort) } catch (e: Exception) { + if (logger.isDebugEnabled) { + logger.debug("Unable to connect to $ircServer, will try again.", e) + } var retries = 0 while (retries++ < MAX_RECONNECT && !isConnected) { sleep(10) @@ -212,7 +215,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert if (logger.isDebugEnabled) { logger.debug("Unable to reconnect to $ircServer, after $MAX_RECONNECT retries.", ex) } - e.printStackTrace(System.err) + System.err.println("An error has occurred while reconnecting; ${ex.message}") exitProcess(1) } } @@ -528,7 +531,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert commandLine = parser.parse(options, args) } catch (e: ParseException) { System.err.println("CLI Parsing failed. Reason: ${e.message}") - e.printStackTrace(System.err) exitProcess(1) } when { @@ -550,13 +552,11 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert ).use { fis -> p.load(fis) } - } catch (e: FileNotFoundException) { + } catch (ignore: FileNotFoundException) { System.err.println("Unable to find properties file.") - e.printStackTrace(System.err) exitProcess(1) - } catch (e: IOException) { + } catch (ignore: IOException) { System.err.println("Unable to open properties file.") - e.printStackTrace(System.err) exitProcess(1) } val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) @@ -574,9 +574,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert ), true ) System.setOut(stdout) - } catch (e: IOException) { + } catch (ignore: IOException) { System.err.println("Unable to open output (stdout) log file.") - e.printStackTrace(System.err) exitProcess(1) } try { @@ -586,9 +585,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert ), true ) System.setErr(stderr) - } catch (e: IOException) { + } catch (ignore: IOException) { System.err.println("Unable to open error (stderr) log file.") - e.printStackTrace(System.err) exitProcess(1) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 31c928f..9704461 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.objecthunter.exp4j.ExpressionBuilder +import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils import java.text.DecimalFormat @@ -47,7 +48,15 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { isPrivate: Boolean ) { if (args.isNotBlank()) { - bot.send(calc(args)) + try { + bot.send(calculate(args)) + } catch (e: IllegalArgumentException) { + if (bot.logger.isWarnEnabled) bot.logger.warn("Failed to calcualte: $args", e) + bot.send("No idea. This is the kind of math I don't get.") + } catch (e: UnknownFunctionOrVariableException) { + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to calcualte: $args", e) + bot.send("No idea. I must've some form of Dyscalculia.") + } } else { helpResponse(sender, isPrivate) } @@ -61,14 +70,11 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { * Performs a calculation. e.g.: 1 + 1 * 2 */ @JvmStatic - fun calc(query: String): String { + @Throws(IllegalArgumentException::class) + fun calculate(query: String): String { val decimalFormat = DecimalFormat("#.##") - return try { - val calc = ExpressionBuilder(query).build() - query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate())) - } catch (e: IllegalArgumentException) { - "No idea. This is the kind of math I don't get." - } + val calc = ExpressionBuilder(query).build() + return query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate())) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 150888b..6ead8e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -52,7 +52,7 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { if (args.matches("(\\S.)+(\\S)+".toRegex())) { with(bot) { try { - lookup(args).split(',').forEach { + nslookup(args).split(',').forEach { send(it.trim()) } } catch (ignore: UnknownHostException) { @@ -105,7 +105,7 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { */ @JvmStatic @Throws(UnknownHostException::class) - fun lookup(query: String): String { + fun nslookup(query: String): String { val buffer = StringBuilder() val results = InetAddress.getAllByName(query) var hostInfo: String diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index ccda94b..d93f0b1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -68,10 +68,10 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { } /** - * Returns the world time. + * Returns the time for the given timezone/city. */ @JvmStatic - fun worldTime(query: String): Message { + fun time(query: String): Message { val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).uppercase()] val response: String = if (tz != null) { if (BEATS_KEYWORD == tz) { @@ -193,7 +193,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { send(sender, "The supported countries/zones are: ", isPrivate) sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false) } else { - val msg = worldTime(args) + val msg = time(args) if (isPrivate) { send(sender, msg.msg, true) } else { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 45f3848..a397f5a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -31,9 +31,11 @@ */ package net.thauvin.erik.mobibot.modules +import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.modules.Calc.Companion.calc +import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test /** @@ -41,10 +43,11 @@ import org.testng.annotations.Test */ class CalcTest { @Test - fun testCalc() { - assertThat(calc("1 + 1")).`as`("calc(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)) - assertThat(calc("1 -3")).`as`("calc(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)) - assertThat(calc("pi+π+e+φ")).`as`("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) - assertThat(calc("one + one")).`as`("calc(one + one)").startsWith("No idea.") + fun testCalculate() { + assertThat(calculate("1 + 1")).`as`("calculate(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)) + assertThat(calculate("1 -3")).`as`("calculate(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)) + assertThat(calculate("pi+π+e+φ")).`as`("calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) + assertThatThrownBy { calculate("one + one") }.`as`("calculate(one+one)") + .isInstanceOf(UnknownFunctionOrVariableException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index c7f0aef..2dca819 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -64,7 +64,7 @@ class GoogleSearchTest : LocalProperties() { } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank() && cseKey.isNotBlank()) { - throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey)) + throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey), e) } else { throw e } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index b623d83..3b94181 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.modules.Lookup.Companion.lookup +import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test @@ -43,7 +43,7 @@ class LookupTest { @Test @Throws(Exception::class) fun testLookup() { - val result = lookup("apple.com") + val result = nslookup("apple.com") assertThat(result).`as`("lookup(apple.com)").contains("17.253.144.10") } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 5053d22..c12b33e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -62,7 +62,7 @@ class StockQuoteTest : LocalProperties() { } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank()) { - throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey)) + throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey), e) } else { throw e } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 4c309c6..10a9d79 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -47,7 +47,7 @@ class TwitterTest : LocalProperties() { val ciName = System.getenv("CI_NAME") return ciName ?: try { InetAddress.getLocalHost().hostName - } catch (e: UnknownHostException) { + } catch (ignore: UnknownHostException) { "Unknown Host" } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index dd696c9..4243383 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.modules.WorldTime.Companion.worldTime +import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test @@ -41,9 +41,9 @@ import org.testng.annotations.Test */ class WordTimeTest { @Test - fun testWorldTime() { - assertThat(worldTime("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles")) - assertThat(worldTime("BLAH").isError).`as`("BLAH").isTrue - assertThat(worldTime("BEATS").msg).`as`("BEATS").contains("@") + fun testTime() { + assertThat(time("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles")) + assertThat(time("BLAH").isError).`as`("BLAH").isTrue + assertThat(time("BEATS").msg).`as`("BEATS").contains("@") } } diff --git a/version.properties b/version.properties index e99ce5a..bff0913 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Apr 30 21:34:46 PDT 2021 -version.buildmeta=538 +#Sat May 01 21:41:40 PDT 2021 +version.buildmeta=568 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+538 +version.semver=0.8.0-beta+568 From d6df365e4b79354e84bb98f361f20b49505baa76 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 3 May 2021 15:29:43 -0700 Subject: [PATCH 488/842] Cleanup. --- build.gradle | 23 ++--- config/detekt/baseline.xml | 41 +------- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 5 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 1 + .../net/thauvin/erik/mobibot/Mobibot.kt | 5 + .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 + .../thauvin/erik/mobibot/commands/AddLog.kt | 1 + .../thauvin/erik/mobibot/commands/Cycle.kt | 1 + .../thauvin/erik/mobibot/commands/Ignore.kt | 1 + .../thauvin/erik/mobibot/commands/Modules.kt | 1 + .../thauvin/erik/mobibot/commands/Recap.kt | 1 + .../thauvin/erik/mobibot/commands/Users.kt | 1 + .../erik/mobibot/commands/links/Comment.kt | 1 + .../erik/mobibot/commands/links/View.kt | 1 + .../erik/mobibot/commands/tell/Tell.kt | 5 +- .../erik/mobibot/entries/EntriesMgr.kt | 93 +++++++++++-------- .../erik/mobibot/modules/CurrencyConverter.kt | 4 + .../net/thauvin/erik/mobibot/modules/Dice.kt | 1 + .../thauvin/erik/mobibot/modules/Twitter.kt | 1 + .../thauvin/erik/mobibot/modules/Weather2.kt | 2 + .../thauvin/erik/mobibot/modules/WorldTime.kt | 3 + version.properties | 6 +- 22 files changed, 102 insertions(+), 98 deletions(-) diff --git a/build.gradle b/build.gradle index 25c909e..acceb2d 100644 --- a/build.gradle +++ b/build.gradle @@ -40,21 +40,22 @@ dependencies { compileOnly 'pircbot:pircbot:1.5.0:sources' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - - implementation 'com.rometools:rome:1.15.0' - implementation 'commons-cli:commons-cli:1.4' - implementation 'commons-net:commons-net:3.8.0' - implementation 'net.aksingh:owm-japis:2.5.3.0' - implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'org.apache.commons:commons-lang3:3.12.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC' - implementation 'org.json:json:20210307' - implementation 'org.jsoup:jsoup:1.13.1' - implementation 'org.twitter4j:twitter4j-core:4.0.7' + implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" + implementation 'commons-cli:commons-cli:1.4' + implementation 'commons-net:commons-net:3.8.0' + implementation 'org.apache.commons:commons-lang3:3.12.0' + + implementation 'com.rometools:rome:1.15.0' + implementation 'net.aksingh:owm-japis:2.5.3.0' + implementation 'net.objecthunter:exp4j:0.4.8' + implementation 'net.thauvin.erik:pinboard-poster:1.0.3' + implementation 'org.json:json:20210307' + implementation 'org.jsoup:jsoup:1.13.1' + implementation 'org.twitter4j:twitter4j-core:4.0.7' compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 2080b7b..afd1e14 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,7 +2,6 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> - <ID>ComplexCondition:Addons.kt$Addons$((isPrivate && module.isPrivateMsgEnabled) || !isPrivate) && module.commands.contains(cmd)</ID> <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> @@ -15,49 +14,14 @@ <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> - <ID>MagicNumber:AddLog.kt$AddLog$4</ID> - <ID>MagicNumber:Comment.kt$Comment$3</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID> - <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID> - <ID>MagicNumber:Cycle.kt$Cycle$10</ID> - <ID>MagicNumber:Dice.kt$Dice$7</ID> - <ID>MagicNumber:FeedReader.kt$FeedReader$5</ID> - <ID>MagicNumber:Ignore.kt$Ignore$8</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$10</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$1000L</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$3</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$5</ID> - <ID>MagicNumber:Mobibot.kt$Mobibot$8</ID> - <ID>MagicNumber:Modules.kt$Modules$7</ID> - <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> - <ID>MagicNumber:Tell.kt$Tell$50</ID> - <ID>MagicNumber:Tell.kt$Tell$7</ID> - <ID>MagicNumber:Twitter.kt$Twitter$1000L</ID> - <ID>MagicNumber:Twitter.kt$Twitter$60L</ID> - <ID>MagicNumber:Users.kt$Users$8</ID> - <ID>MagicNumber:Utils.kt$Utils$30</ID> - <ID>MagicNumber:Utils.kt$Utils$365</ID> - <ID>MagicNumber:Utils.kt$Utils$7</ID> - <ID>MagicNumber:View.kt$View$8</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID> - <ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime$17</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean): Boolean</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @Suppress("MagicNumber") @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$// Daily backup private fun dailyBackup( bot: Mobibot, history: MutableList<String> )</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> <ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> @@ -85,6 +49,5 @@ <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> <ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID> <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> - <ID>TooManyFunctions:Utils.kt$Utils$Utils</ID> </CurrentIssues> </SmellBaseline> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 72f4dcd..c2151a0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -97,8 +97,9 @@ class Addons { return true } } - for (module in modules) { - if (((isPrivate && module.isPrivateMsgEnabled) || !isPrivate) && module.commands.contains(cmd)) { + val mods = if (isPrivate) modules.filter { it.isPrivateMsgEnabled } else modules + for (module in mods) { + if (module.commands.contains(cmd)) { module.commandResponse(sender, cmd, args, isPrivate) return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 0cba397..4e20d32 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -48,6 +48,7 @@ class FeedReader( private val url: String ) : Runnable { // Maximum number of feed items to display + @Suppress("MagicNumber") private val maxItems = 5 /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index ffa13a5..bf30f72 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -207,6 +207,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } var retries = 0 while (retries++ < MAX_RECONNECT && !isConnected) { + @Suppress("MagicNumber") sleep(10) try { connect(ircServer, ircPort) @@ -240,6 +241,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert /** * Responds with the default help. */ + @Suppress("MagicNumber") fun helpDefault(sender: String, isOp: Boolean, isPrivate: Boolean) { send(sender, "Type a URL on $channel to post it.", isPrivate) send(sender, "For more information on a specific command, type:", isPrivate) @@ -305,6 +307,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert if (weblogUrl.isNotBlank()) { version = weblogUrl } + @Suppress("MagicNumber") sleep(5) connect() } @@ -361,6 +364,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert timer.cancel() twitter.shutdown() twitter.notification("$name stopped by $sender on $channel") + @Suppress("MagicNumber") sleep(3) quitServer("The Bot Is Out There!") exitProcess(0) @@ -467,6 +471,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert */ fun sleep(secs: Int) { try { + @Suppress("MagicNumber") Thread.sleep(secs * 1000L) } catch (ignore: InterruptedException) { // Do nothing diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index d5c3486..2304bc0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -51,6 +51,7 @@ import java.util.stream.Collectors /** * Miscellaneous utilities. */ +@Suppress("TooManyFunctions") object Utils { private val searchFlags = arrayOf("%c", "%n") @@ -234,6 +235,7 @@ object Utils { /** * Converts milliseconds to year month week day hour and minutes. */ + @Suppress("MagicNumber") @JvmStatic fun uptime(uptime: Long): String { val info = StringBuilder() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt index 261a154..c0c0275 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -63,6 +63,7 @@ class AddLog(bot: Mobibot) : AbstractCommand(bot) { return } } + @Suppress("MagicNumber") bot.sendList(sender, history, 4, isPrivate, isIndent = true) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index a62b87f..32c06e5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -36,6 +36,7 @@ import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils class Cycle(bot: Mobibot) : AbstractCommand(bot) { + @Suppress("MagicNumber") private val wait = 10 override val name = "cycle" override val help = listOf("To have the bot leave the channel and come back:", Utils.helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index cdcc77f..cb635f2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -140,6 +140,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { if (ignored.size > 0) { bot.send(sender, "The following nicks are ignored:", isPrivate) + @Suppress("MagicNumber") bot.sendList(sender, ignored.sorted(), 8, isPrivate, isIndent = true) } else { bot.send(sender, "No one is currently ${Utils.bold("ignored")}.", isPrivate) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index d2d780b..50ddc93 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -58,6 +58,7 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) { send(sender, "There are no enabled modules.", isPrivate) } else { send(sender, "The enabled modules are: ", isPrivate) + @Suppress("MagicNumber") sendList(sender, modulesNames, 7, isPrivate, isIndent = true) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 364fc5f..cbcf5d0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -60,6 +60,7 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + " - $sender" + (if (isAction) " " else ": ") + message ) + @Suppress("MagicNumber") if (recaps.size > 10) { recaps.removeAt(0) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 759b54c..1d1ec6e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -62,6 +62,7 @@ class Users(bot: Mobibot) : AbstractCommand(bot) { } } + @Suppress("MagicNumber") bot.sendList(sender, nicks.sorted(), 8, isPrivate, isIndent = true) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 8b55a5d..9a8685a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -65,6 +65,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { + @Suppress("MagicNumber") val cmds = args.substring(1).split("[.:]".toRegex(), 3) val index = cmds[0].toInt() - 1 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 7bca9bd..0627d93 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -40,6 +40,7 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink class View(bot: Mobibot) : AbstractCommand(bot) { + @Suppress("MagicNumber") private val maxEntries = 8 override val name = VIEW_CMD override val help = listOf( diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 0890a25..80b7f79 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -53,9 +53,11 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { private val serializedObject: String // Maximum number of days to keep messages + @Suppress("MagicNumber") private var maxDays = 7 // Message maximum queue size + @Suppress("MagicNumber") private var maxSize = 50 /** @@ -227,8 +229,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { save() } } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified - ) { + && !message.isNotified) { bot.send( nickname, "Your message ${reverseColor("[ID " + message.id + ']')} was sent to " + diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 0a3136a..00fe777 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -71,6 +71,53 @@ object EntriesMgr { // Maximum number of backlogs to keep private const val maxBacklogs = 10 + // Daily backup + private fun dailyBackup( + bot: Mobibot, + history: MutableList<String> + ) { + if (bot.backlogsUrl.isNotBlank()) { + if (!history.contains(bot.today)) { + history.add(bot.today) + while (history.size > maxBacklogs) { + history.removeAt(0) + } + } + OutputStreamWriter( + Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8 + ).use { fw -> + val output = SyndFeedOutput() + val rss: SyndFeed = SyndFeedImpl() + val items: MutableList<SyndEntry> = mutableListOf() + var item: SyndEntry + with(rss) { + feedType = "rss_2.0" + title = "${bot.channel} IRC Links Backlogs" + description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}" + link = bot.backlogsUrl + publishedDate = Calendar.getInstance().time + } + var date: String + items.clear() + for (i in history.size - 1 downTo 0) { + date = history[i] + item = SyndEntryImpl() + with(item) { + link = bot.backlogsUrl + date + ".xml" + title = date + description = SyndContentImpl().apply { value = "Links for $date" } + } + items.add(item) + } + rss.entries = items + if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.") + output.output(rss, fw) + } + } else { + bot.logger.warn("Unable to generate the backlogs feed. No property configured.") + } + } + /** * Loads the backlogs. */ @@ -139,7 +186,7 @@ object EntriesMgr { if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { try { val output = SyndFeedOutput() - var rss: SyndFeed = SyndFeedImpl() + val rss: SyndFeed = SyndFeedImpl() val items: MutableList<SyndEntry> = mutableListOf() var item: SyndEntry OutputStreamWriter( @@ -153,12 +200,12 @@ object EntriesMgr { publishedDate = Calendar.getInstance().time language = "en" } - var buff: StringBuilder + val buff: StringBuilder = StringBuilder() var comment: EntryComment for (i in entries.size - 1 downTo 0) { with(entries[i]) { - buff = StringBuilder() - .append("Posted by <b>") + buff.setLength(0) + buff.append("Posted by <b>") .append(nick) .append("</b> on <a href=\"irc://") .append(bot.ircServer).append('/') @@ -199,43 +246,7 @@ object EntriesMgr { ), StandardCharsets.UTF_8 ).use { fw -> output.output(rss, fw) } if (isDayBackup) { - if (bot.backlogsUrl.isNotBlank()) { - if (!history.contains(bot.today)) { - history.add(bot.today) - while (history.size > maxBacklogs) { - history.removeAt(0) - } - } - OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8 - ).use { fw -> - rss = SyndFeedImpl() - with(rss) { - feedType = "rss_2.0" - title = "${bot.channel} IRC Links Backlogs" - description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}" - link = bot.backlogsUrl - publishedDate = Calendar.getInstance().time - } - var date: String - items.clear() - for (i in history.size - 1 downTo 0) { - date = history[i] - item = SyndEntryImpl() - with(item) { - link = bot.backlogsUrl + date + ".xml" - title = date - description = SyndContentImpl().apply { value = "Links for $date" } - } - items.add(item) - } - rss.entries = items - if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.") - output.output(rss, fw) - } - } else { - bot.logger.warn("Unable to generate the backlogs feed. No property configured.") - } + dailyBackup(bot, history) } } catch (e: FeedException) { if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 9542395..acf3669 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -86,6 +86,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { } } else if (args.contains(CURRENCY_RATES_KEYWORD)) { send(sender, "The currency rates for ${Utils.bold(pubDate)} are:", isPrivate) + @Suppress("MagicNumber") sendList(sender, currencyRates(), 3, isPrivate, isIndent = true) } else { helpResponse(sender, isPrivate) @@ -122,6 +123,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { isPrivate ) send(sender, "The supported currencies are: ", isPrivate) + @Suppress("MagicNumber") sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, isIndent = true) } } @@ -150,6 +152,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { /** * Converts from a currency to another. */ + @Suppress("MagicNumber") @JvmStatic fun convertCurrency(query: String): Message { val cmds = query.split(" ") @@ -185,6 +188,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { fun currencyRates(): List<String> { val rates = mutableListOf<String>() for ((key, value) in EXCHANGE_RATES) { + @Suppress("MagicNumber") rates.add(" $key: ${StringUtils.leftPad(value, 8)}") } return rates diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 42eb680..53c39ad 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -73,6 +73,7 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { } private fun roll(): Pair<Int, Int> { + @Suppress("MagicNumber") return Pair(Random.nextInt(1, 7), Random.nextInt(1, 7)) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index a479fd1..7fdefd4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -145,6 +145,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { if (bot.logger.isDebugEnabled) { bot.logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkCmd(index)) } + @Suppress("MagicNumber") bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 23f9665..8c01cff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -89,6 +89,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } private fun getTemps(d: Double?): String { + @Suppress("MagicNumber") val c = (d!! - 32) * 5 / 9 return "${d.roundToInt()} °F, ${c.roundToInt()} °C" } @@ -186,6 +187,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } private fun wind(w: Double): String { + @Suppress("MagicNumber") val kmh = w * 1.60934 return "${w.roundToInt()} mph, ${kmh.roundToInt()} km/h" } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index d93f0b1..0b21462 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -60,6 +60,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { /** * Returns the current Internet (beat) Time. */ + @Suppress("MagicNumber") private fun internetTime(): String { val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 @@ -171,6 +172,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["ZULU"] = "Zulu" countries["INTERNET"] = BEATS_KEYWORD countries["BEATS"] = BEATS_KEYWORD + @Suppress("MagicNumber") ZoneId.getAvailableZoneIds().stream() .filter { tz: String -> !tz.contains("/") && tz.length == 3 && !countries.containsKey(tz) @@ -191,6 +193,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { with(bot) { if (args.isEmpty()) { send(sender, "The supported countries/zones are: ", isPrivate) + @Suppress("MagicNumber") sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false) } else { val msg = time(args) diff --git a/version.properties b/version.properties index bff0913..9d6cb44 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat May 01 21:41:40 PDT 2021 -version.buildmeta=568 +#Mon May 03 14:17:39 PDT 2021 +version.buildmeta=576 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+568 +version.semver=0.8.0-beta+576 From c99210f624129c2b6abe6fdd5a4c920a8dd1e701 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 May 2021 01:03:33 -0700 Subject: [PATCH 489/842] Added Bitcoin module. --- config/detekt/baseline.xml | 3 + .../net/thauvin/erik/mobibot/Mobibot.kt | 2 + .../thauvin/erik/mobibot/modules/Bitcoin.kt | 123 ++++++++++++++++++ .../erik/mobibot/modules/BitcoinTest.kt | 55 ++++++++ version.properties | 6 +- 5 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index afd1e14..a4065f6 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -15,6 +15,7 @@ <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> + <ID>NestedBlockDepth:Bitcoin.kt$Bitcoin$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean): Boolean</ID> @@ -38,10 +39,12 @@ <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> + <ID>ThrowsCount:Bitcoin.kt$Bitcoin.Companion$ @JvmStatic @Throws(ModuleException::class) fun marketPrice(currency: String): List<Message></ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> + <ID>TooGenericExceptionCaught:Bitcoin.kt$Bitcoin.Companion$e: NullPointerException</ID> <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID> <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index bf30f72..72f0e89 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -64,6 +64,7 @@ import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.commands.tell.Tell import net.thauvin.erik.mobibot.entries.EntriesMgr import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.modules.Bitcoin import net.thauvin.erik.mobibot.modules.Calc import net.thauvin.erik.mobibot.modules.CurrencyConverter import net.thauvin.erik.mobibot.modules.Dice @@ -677,6 +678,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(View(this), p) // Load the modules + addons.add(Bitcoin(this), p) addons.add(Calc(this), p) addons.add(CurrencyConverter(this), p) addons.add(Dice(this), p) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt new file mode 100644 index 0000000..0289bf2 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt @@ -0,0 +1,123 @@ +/* + * Bitcoin.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.net.URL + +/** + * The Bitcoin module. + */ +class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { + // Currencies + private val currencies = listOf( + "USD", "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "DKK", "EUR", "GBP", "HKD", "INR", "ISK", "JPY", "KRW", + "NZD", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD"); + + /** + * Returns the bitcoin market price from [Blockchain.info](https://blockchain.info/ticker). + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + with(bot) { + val arg = args.trim().uppercase() + @Suppress("MagicNumber") + if (!currencies.contains(arg)) { + helpResponse(sender, isPrivate) + send(sender, "The supported currencies are: ", isPrivate) + @Suppress("MagicNumber") + sendList(sender, currencies, 12, isPrivate, isIndent = true) + } else { + try { + val messages = marketPrice(arg) + for (msg in messages) { + send(sender, msg) + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + send(e.message) + } + } + } + } + + companion object { + // Blockchain Ticker URL + private const val TICKER_URL = "https://blockchain.info/ticker" + + // Quote command + private const val BITCOIN_CMD = "bitcoin" + + /** + * Retrieves the bitcoin market price. + */ + @JvmStatic + @Throws(ModuleException::class) + fun marketPrice(currency: String): List<Message> { + val debugMessage = "marketPrice($currency)" + val messages = mutableListOf<Message>() + try { + val response = Utils.urlReader(URL("$TICKER_URL")) + val json = JSONObject(response) + val bpi = json.getJSONObject(currency.trim().uppercase()) + val symbol = bpi.getString("symbol"); + with(messages) { + add(PublicMessage("BTC: $symbol" + bpi.getBigDecimal("last") + " [$currency]")) + add(NoticeMessage(" 15m: $symbol" + bpi.getBigDecimal("15m"))) + add(NoticeMessage(" Buy: $symbol" + bpi.getBigDecimal("buy"))) + add(NoticeMessage(" Sell: $symbol" + bpi.getBigDecimal("sell"))) + } + return messages + } catch (e: IOException) { + throw ModuleException(debugMessage, "An IO error has occurred retrieving the bitcoin market price.", e) + } catch (e: NullPointerException) { + throw ModuleException(debugMessage, "An error has occurred retrieving the bitcoin market price.", e) + } catch (e: org.json.JSONException) { + throw ModuleException( + debugMessage, "A parsing error has occurred retriving the bitcoin market price.", e) + } + } + } + + init { + commands.add(BITCOIN_CMD) + help.add("To retrieve the bitcoin market price:") + help.add(Utils.helpFormat("%c $BITCOIN_CMD <USD|GBP|EUR|...>")) + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt new file mode 100644 index 0000000..8453420 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt @@ -0,0 +1,55 @@ +/* + * BitcoinTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Bitcoin.Companion.marketPrice +import org.assertj.core.api.Assertions.assertThat +import org.testng.annotations.Test + +/** + * The `BitcoinTest` class. + */ +class BitcoinTest : LocalProperties() { + @Test + @Throws(ModuleException::class) + fun testMarketPrice() { + var messages = marketPrice("USD") + assertThat(messages).`as`("not empty").isNotEmpty + assertThat(messages[0].msg).`as`("btc & $").startsWith("BTC").contains("$") + assertThat(messages[1].msg).`as`("15m").contains("15m") + + messages = marketPrice("GBP") + assertThat(messages[0].msg).`as`("BPB btc & £").startsWith("BTC").contains("£") + assertThat(messages[1].msg).`as`("GBP 15m").contains("15m") + } +} diff --git a/version.properties b/version.properties index 9d6cb44..dcde182 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon May 03 14:17:39 PDT 2021 -version.buildmeta=576 +#Tue May 04 01:01:14 PDT 2021 +version.buildmeta=606 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+576 +version.semver=0.8.0-beta+606 From bd14a1e15ed2c0febd3243f4def62f9f2b476571 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 May 2021 11:05:23 -0700 Subject: [PATCH 490/842] Included currencies in help response. --- .../thauvin/erik/mobibot/modules/Bitcoin.kt | 41 +++++++++++++++---- .../erik/mobibot/modules/BitcoinTest.kt | 4 +- version.properties | 6 +-- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt index 0289bf2..428917d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt @@ -48,21 +48,38 @@ import java.net.URL class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { // Currencies private val currencies = listOf( - "USD", "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "DKK", "EUR", "GBP", "HKD", "INR", "ISK", "JPY", "KRW", + "USD", "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "DKK", "EUR", "GBP", "HKD", "INR", "ISK", "JPY", "KRW", "NZD", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD"); + override fun helpResponse(sender: String, isPrivate: Boolean): Boolean { + with(bot) { + send(sender, "To retrieve the bitcoin market price:", isPrivate) + send( + sender, + Utils.helpFormat( + Utils.buildCmdSyntax( + "%c $BITCOIN_CMD <USD|GBP|EUR|...>", + nick, + isPrivateMsgEnabled) + ), + isPrivate + ) + send(sender, "The supported currencies are: ", isPrivate) + @Suppress("MagicNumber") + sendList(sender, currencies, 12, isPrivate, isIndent = true) + } + return true + } + /** * Returns the bitcoin market price from [Blockchain.info](https://blockchain.info/ticker). */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { with(bot) { - val arg = args.trim().uppercase() + val arg = args.trim().uppercase() @Suppress("MagicNumber") if (!currencies.contains(arg)) { helpResponse(sender, isPrivate) - send(sender, "The supported currencies are: ", isPrivate) - @Suppress("MagicNumber") - sendList(sender, currencies, 12, isPrivate, isIndent = true) } else { try { val messages = marketPrice(arg) @@ -81,9 +98,15 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { // Blockchain Ticker URL private const val TICKER_URL = "https://blockchain.info/ticker" - // Quote command + // Bitcoin command private const val BITCOIN_CMD = "bitcoin" + // BTC command + private const val BTC_CMD = "btc" + + // XBT command + private const val XBT_CMD = "xbt" + /** * Retrieves the bitcoin market price. */ @@ -98,7 +121,7 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { val bpi = json.getJSONObject(currency.trim().uppercase()) val symbol = bpi.getString("symbol"); with(messages) { - add(PublicMessage("BTC: $symbol" + bpi.getBigDecimal("last") + " [$currency]")) + add(PublicMessage("BITCOIN: $symbol" + bpi.getBigDecimal("last") + " [$currency]")) add(NoticeMessage(" 15m: $symbol" + bpi.getBigDecimal("15m"))) add(NoticeMessage(" Buy: $symbol" + bpi.getBigDecimal("buy"))) add(NoticeMessage(" Sell: $symbol" + bpi.getBigDecimal("sell"))) @@ -117,7 +140,7 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(BITCOIN_CMD) - help.add("To retrieve the bitcoin market price:") - help.add(Utils.helpFormat("%c $BITCOIN_CMD <USD|GBP|EUR|...>")) + commands.add(BTC_CMD) + commands.add(XBT_CMD) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt index 8453420..31f7ede 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt @@ -45,11 +45,11 @@ class BitcoinTest : LocalProperties() { fun testMarketPrice() { var messages = marketPrice("USD") assertThat(messages).`as`("not empty").isNotEmpty - assertThat(messages[0].msg).`as`("btc & $").startsWith("BTC").contains("$") + assertThat(messages[0].msg).`as`("bitcoin & $").startsWith("BITCOIN").contains("$") assertThat(messages[1].msg).`as`("15m").contains("15m") messages = marketPrice("GBP") - assertThat(messages[0].msg).`as`("BPB btc & £").startsWith("BTC").contains("£") + assertThat(messages[0].msg).`as`("£").contains("£").contains("GBP") assertThat(messages[1].msg).`as`("GBP 15m").contains("15m") } } diff --git a/version.properties b/version.properties index dcde182..cb3f1db 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 04 01:01:14 PDT 2021 -version.buildmeta=606 +#Tue May 04 11:01:30 PDT 2021 +version.buildmeta=610 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+606 +version.semver=0.8.0-beta+610 From bf985d56260c8869324c0193ea881398ff6cf21d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 May 2021 17:57:47 -0700 Subject: [PATCH 491/842] Fixed Google search test. --- .../net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 2dca819..4d8abd5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -47,9 +47,9 @@ class GoogleSearchTest : LocalProperties() { val apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP) val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) try { - var messages = searchGoogle("mobibot site:github.com", apiKey, cseKey) - assertThat(messages).`as`("mobibot results not empty").isNotEmpty - assertThat(messages[0].msg).`as`("found mobitopia").contains("mobibot") + var messages = searchGoogle("mobitopia", apiKey, cseKey) + assertThat(messages).`as`("mobitopia results not empty").isNotEmpty + assertThat(messages[0].msg).`as`("found freenode").contains("freenode") messages = searchGoogle("aapl", apiKey, cseKey) assertThat(messages).`as`("aapl results not empty").isNotEmpty assertThat(messages[0].msg).`as`("found apple").containsIgnoringCase("apple") diff --git a/version.properties b/version.properties index cb3f1db..2fc39d3 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 04 11:01:30 PDT 2021 -version.buildmeta=610 +#Tue May 04 17:52:51 PDT 2021 +version.buildmeta=618 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+610 +version.semver=0.8.0-beta+618 From df7384570e4f0be82b826a8ebbba2cf8ee7a0905 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 May 2021 21:58:49 -0700 Subject: [PATCH 492/842] Fixed formatting. --- .../net/thauvin/erik/mobibot/modules/Bitcoin.kt | 15 ++++++++------- version.properties | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt index 428917d..f191c40 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt @@ -41,6 +41,7 @@ import org.json.JSONException import org.json.JSONObject import java.io.IOException import java.net.URL +import java.text.DecimalFormat /** * The Bitcoin module. @@ -104,8 +105,9 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { // BTC command private const val BTC_CMD = "btc" - // XBT command - private const val XBT_CMD = "xbt" + private fun JSONObject.getDecimal(key: String): String { + return DecimalFormat("0.00").format(this.getBigDecimal(key)) + } /** * Retrieves the bitcoin market price. @@ -121,10 +123,10 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { val bpi = json.getJSONObject(currency.trim().uppercase()) val symbol = bpi.getString("symbol"); with(messages) { - add(PublicMessage("BITCOIN: $symbol" + bpi.getBigDecimal("last") + " [$currency]")) - add(NoticeMessage(" 15m: $symbol" + bpi.getBigDecimal("15m"))) - add(NoticeMessage(" Buy: $symbol" + bpi.getBigDecimal("buy"))) - add(NoticeMessage(" Sell: $symbol" + bpi.getBigDecimal("sell"))) + add(PublicMessage("BITCOIN: $symbol" + bpi.getDecimal("last") + " [$currency]")) + add(NoticeMessage(" 15m: $symbol" + bpi.getDecimal("15m"))) + add(NoticeMessage(" Buy: $symbol" + bpi.getDecimal("buy"))) + add(NoticeMessage(" Sell: $symbol" + bpi.getDecimal("sell"))) } return messages } catch (e: IOException) { @@ -141,6 +143,5 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(BITCOIN_CMD) commands.add(BTC_CMD) - commands.add(XBT_CMD) } } diff --git a/version.properties b/version.properties index 2fc39d3..c49a803 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 04 17:52:51 PDT 2021 -version.buildmeta=618 +#Tue May 04 21:29:29 PDT 2021 +version.buildmeta=628 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+618 +version.semver=0.8.0-beta+628 From 87cb58102aef104275dc6bcccce89971d4a98ccd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 May 2021 23:10:47 -0700 Subject: [PATCH 493/842] Only return BTC 15m, etc. if different from current price. --- .../kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt | 8 +++++--- .../net/thauvin/erik/mobibot/modules/BitcoinTest.kt | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt index f191c40..1fa2cbf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt @@ -124,9 +124,11 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { val symbol = bpi.getString("symbol"); with(messages) { add(PublicMessage("BITCOIN: $symbol" + bpi.getDecimal("last") + " [$currency]")) - add(NoticeMessage(" 15m: $symbol" + bpi.getDecimal("15m"))) - add(NoticeMessage(" Buy: $symbol" + bpi.getDecimal("buy"))) - add(NoticeMessage(" Sell: $symbol" + bpi.getDecimal("sell"))) + if (bpi.getBigDecimal("15m") != bpi.getBigDecimal("last")) { + add(NoticeMessage(" 15m: $symbol" + bpi.getDecimal("15m"))) + add(NoticeMessage(" Buy: $symbol" + bpi.getDecimal("buy"))) + add(NoticeMessage(" Sell: $symbol" + bpi.getDecimal("sell"))) + } } return messages } catch (e: IOException) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt index 31f7ede..3866d81 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt @@ -46,10 +46,10 @@ class BitcoinTest : LocalProperties() { var messages = marketPrice("USD") assertThat(messages).`as`("not empty").isNotEmpty assertThat(messages[0].msg).`as`("bitcoin & $").startsWith("BITCOIN").contains("$") - assertThat(messages[1].msg).`as`("15m").contains("15m") + //assertThat(messages[1].msg).`as`("15m").contains("15m") messages = marketPrice("GBP") assertThat(messages[0].msg).`as`("£").contains("£").contains("GBP") - assertThat(messages[1].msg).`as`("GBP 15m").contains("15m") + //assertThat(messages[1].msg).`as`("GBP 15m").contains("15m") } } From 35399c3f789708942f53e6b0e8ae2889098e206b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 May 2021 23:11:19 -0700 Subject: [PATCH 494/842] Updated to jacoco 0.8.7 --- build.gradle | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index acceb2d..a1c957e 100644 --- a/build.gradle +++ b/build.gradle @@ -170,7 +170,7 @@ sonarqube { } jacoco { - toolVersion = '0.8.7-SNAPSHOT' + toolVersion = '0.8.7' } jacocoTestReport { diff --git a/version.properties b/version.properties index c49a803..4e4295c 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 04 21:29:29 PDT 2021 -version.buildmeta=628 +#Tue May 04 23:03:56 PDT 2021 +version.buildmeta=631 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+628 +version.semver=0.8.0-beta+631 From 1f965a1833cca19054f0d0d9746f9eef08210238 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 4 May 2021 23:41:00 -0700 Subject: [PATCH 495/842] BTC price output cleanup. --- src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt | 2 +- version.properties | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt index 1fa2cbf..db94db6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt @@ -123,7 +123,7 @@ class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { val bpi = json.getJSONObject(currency.trim().uppercase()) val symbol = bpi.getString("symbol"); with(messages) { - add(PublicMessage("BITCOIN: $symbol" + bpi.getDecimal("last") + " [$currency]")) + add(PublicMessage("Bitcoin [BTC]: $symbol" + bpi.getDecimal("last") + " [$currency]")) if (bpi.getBigDecimal("15m") != bpi.getBigDecimal("last")) { add(NoticeMessage(" 15m: $symbol" + bpi.getDecimal("15m"))) add(NoticeMessage(" Buy: $symbol" + bpi.getDecimal("buy"))) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt index 3866d81..2394bb5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt @@ -45,7 +45,7 @@ class BitcoinTest : LocalProperties() { fun testMarketPrice() { var messages = marketPrice("USD") assertThat(messages).`as`("not empty").isNotEmpty - assertThat(messages[0].msg).`as`("bitcoin & $").startsWith("BITCOIN").contains("$") + assertThat(messages[0].msg).`as`("bitcoin, BTC, $").startsWith("Bitcoin").contains("BTC").contains("$") //assertThat(messages[1].msg).`as`("15m").contains("15m") messages = marketPrice("GBP") diff --git a/version.properties b/version.properties index 4e4295c..80c6644 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 04 23:03:56 PDT 2021 -version.buildmeta=631 +#Tue May 04 23:36:08 PDT 2021 +version.buildmeta=632 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+631 +version.semver=0.8.0-beta+632 From f580f0f7f24fecea598ec93bace0d2b017047f94 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 6 May 2021 02:39:27 -0700 Subject: [PATCH 496/842] Implemented CryptoPrices (Coinbase API), replacing the Bitcoin module. --- build.gradle | 5 + config/detekt/baseline.xml | 6 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 4 +- .../thauvin/erik/mobibot/modules/Bitcoin.kt | 149 ------------------ .../erik/mobibot/modules/CryptoPrices.kt | 103 ++++++++++++ .../{BitcoinTest.kt => CryptoPricesTest.kt} | 40 +++-- version.properties | 6 +- 7 files changed, 145 insertions(+), 168 deletions(-) delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt rename src/test/kotlin/net/thauvin/erik/mobibot/modules/{BitcoinTest.kt => CryptoPricesTest.kt} (56%) diff --git a/build.gradle b/build.gradle index a1c957e..d55ba00 100644 --- a/build.gradle +++ b/build.gradle @@ -50,9 +50,14 @@ dependencies { implementation 'org.apache.commons:commons-lang3:3.12.0' implementation 'com.rometools:rome:1.15.0' + implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' + + implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' + + implementation 'org.json:json:20210307' implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index a4065f6..5dab69a 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -15,8 +15,8 @@ <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> - <ID>NestedBlockDepth:Bitcoin.kt$Bitcoin$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> + <ID>NestedBlockDepth:CryptoPrices.kt$CryptoPrices$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean): Boolean</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @Suppress("MagicNumber") @JvmStatic fun convertCurrency(query: String): Message</ID> @@ -39,12 +39,12 @@ <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> - <ID>ThrowsCount:Bitcoin.kt$Bitcoin.Companion$ @JvmStatic @Throws(ModuleException::class) fun marketPrice(currency: String): List<Message></ID> + <ID>ThrowsCount:CryptoPrices.kt$CryptoPrices.Companion$ @JvmStatic @Throws(ModuleException::class) fun marketPrice(base: String, currency: String): Price</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>TooGenericExceptionCaught:Bitcoin.kt$Bitcoin.Companion$e: NullPointerException</ID> + <ID>TooGenericExceptionCaught:CryptoPrices.kt$CryptoPrices$e: Exception</ID> <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID> <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 72f0e89..1efd54b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -64,7 +64,7 @@ import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.commands.tell.Tell import net.thauvin.erik.mobibot.entries.EntriesMgr import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.modules.Bitcoin +import net.thauvin.erik.mobibot.modules.CryptoPrices import net.thauvin.erik.mobibot.modules.Calc import net.thauvin.erik.mobibot.modules.CurrencyConverter import net.thauvin.erik.mobibot.modules.Dice @@ -678,8 +678,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(View(this), p) // Load the modules - addons.add(Bitcoin(this), p) addons.add(Calc(this), p) + addons.add(CryptoPrices(this), p) addons.add(CurrencyConverter(this), p) addons.add(Dice(this), p) addons.add(GoogleSearch(this), p) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt deleted file mode 100644 index db94db6..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Bitcoin.kt - * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage -import net.thauvin.erik.mobibot.msg.PublicMessage -import org.json.JSONException -import org.json.JSONObject -import java.io.IOException -import java.net.URL -import java.text.DecimalFormat - -/** - * The Bitcoin module. - */ -class Bitcoin(bot: Mobibot) : ThreadedModule(bot) { - // Currencies - private val currencies = listOf( - "USD", "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "DKK", "EUR", "GBP", "HKD", "INR", "ISK", "JPY", "KRW", - "NZD", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD"); - - override fun helpResponse(sender: String, isPrivate: Boolean): Boolean { - with(bot) { - send(sender, "To retrieve the bitcoin market price:", isPrivate) - send( - sender, - Utils.helpFormat( - Utils.buildCmdSyntax( - "%c $BITCOIN_CMD <USD|GBP|EUR|...>", - nick, - isPrivateMsgEnabled) - ), - isPrivate - ) - send(sender, "The supported currencies are: ", isPrivate) - @Suppress("MagicNumber") - sendList(sender, currencies, 12, isPrivate, isIndent = true) - } - return true - } - - /** - * Returns the bitcoin market price from [Blockchain.info](https://blockchain.info/ticker). - */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { - val arg = args.trim().uppercase() - @Suppress("MagicNumber") - if (!currencies.contains(arg)) { - helpResponse(sender, isPrivate) - } else { - try { - val messages = marketPrice(arg) - for (msg in messages) { - send(sender, msg) - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - send(e.message) - } - } - } - } - - companion object { - // Blockchain Ticker URL - private const val TICKER_URL = "https://blockchain.info/ticker" - - // Bitcoin command - private const val BITCOIN_CMD = "bitcoin" - - // BTC command - private const val BTC_CMD = "btc" - - private fun JSONObject.getDecimal(key: String): String { - return DecimalFormat("0.00").format(this.getBigDecimal(key)) - } - - /** - * Retrieves the bitcoin market price. - */ - @JvmStatic - @Throws(ModuleException::class) - fun marketPrice(currency: String): List<Message> { - val debugMessage = "marketPrice($currency)" - val messages = mutableListOf<Message>() - try { - val response = Utils.urlReader(URL("$TICKER_URL")) - val json = JSONObject(response) - val bpi = json.getJSONObject(currency.trim().uppercase()) - val symbol = bpi.getString("symbol"); - with(messages) { - add(PublicMessage("Bitcoin [BTC]: $symbol" + bpi.getDecimal("last") + " [$currency]")) - if (bpi.getBigDecimal("15m") != bpi.getBigDecimal("last")) { - add(NoticeMessage(" 15m: $symbol" + bpi.getDecimal("15m"))) - add(NoticeMessage(" Buy: $symbol" + bpi.getDecimal("buy"))) - add(NoticeMessage(" Sell: $symbol" + bpi.getDecimal("sell"))) - } - } - return messages - } catch (e: IOException) { - throw ModuleException(debugMessage, "An IO error has occurred retrieving the bitcoin market price.", e) - } catch (e: NullPointerException) { - throw ModuleException(debugMessage, "An error has occurred retrieving the bitcoin market price.", e) - } catch (e: org.json.JSONException) { - throw ModuleException( - debugMessage, "A parsing error has occurred retriving the bitcoin market price.", e) - } - } - } - - init { - commands.add(BITCOIN_CMD) - commands.add(BTC_CMD) - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt new file mode 100644 index 0000000..20c42b8 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -0,0 +1,103 @@ +/* + * CryptoPrices.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import okhttp3.OkHttpClient +import okhttp3.Request +import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice +import net.thauvin.erik.crypto.CryptoException +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.net.URL +import java.text.DecimalFormat +import java.time.format.DateTimeFormatter +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.time.ZoneId +import java.time.ZoneOffset + +data class Price(val base: String, val currency: String, val amount: Double) + +/** + * The Cryptocurrency Prices module. + */ +class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { + val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneOffset.UTC) + val decimalFormat = DecimalFormat("0.00") + + /** + * Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price). + */ + override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + val debugMessage = "crypto($cmd $args)" + with(bot) { + if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { + val params = args.trim().split(" "); + try { + val currency = if (params.size == 2) params[1] else "USD" + val price = marketPrice(params[0], currency) + send(sender, PublicMessage("${price.base}: ${price.amount} [${price.currency}]")) + } catch (e: CryptoException) { + if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) + send(e.message) + } catch (e: Exception) { + if (logger.isErrorEnabled) logger.error(debugMessage, e) + send("An error has occurred while retrieving the cryptocurrency market price.") + } + } else { + helpResponse(sender, isPrivate) + } + } + } + + companion object { + // Crypto command + private const val CRYPTO_CMD = "crypto" + } + + init { + commands.add(CRYPTO_CMD) + help.add("To retrieve a cryptocurrency's market price:") + help.add(Utils.helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) + help.add("For example:") + help.add(Utils.helpFormat("%c $CRYPTO_CMD BTC")) + help.add(Utils.helpFormat("%c $CRYPTO_CMD ETH EUR")) + help.add(Utils.helpFormat("%c $CRYPTO_CMD ETH2 GPB")) + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt similarity index 56% rename from src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 2394bb5..bd07f1c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,5 +1,5 @@ /* - * BitcoinTest.kt + * CryptoPricesTest.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -31,25 +31,43 @@ */ package net.thauvin.erik.mobibot.modules +import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice +import net.thauvin.erik.crypto.CryptoException import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Bitcoin.Companion.marketPrice import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test /** - * The `BitcoinTest` class. + * The `CryptoPricesTest` class. */ -class BitcoinTest : LocalProperties() { +class CryptoPricesTest { @Test @Throws(ModuleException::class) fun testMarketPrice() { - var messages = marketPrice("USD") - assertThat(messages).`as`("not empty").isNotEmpty - assertThat(messages[0].msg).`as`("bitcoin, BTC, $").startsWith("Bitcoin").contains("BTC").contains("$") - //assertThat(messages[1].msg).`as`("15m").contains("15m") + var price = marketPrice("BTC", "USD") + assertThat(price.base).`as`("is BTC").isEqualTo("BTC") + assertThat(price.currency).`as`("is USD").isEqualTo("USD") + assertThat(price.amount).`as`("BTC > 0").isGreaterThan(0.00) - messages = marketPrice("GBP") - assertThat(messages[0].msg).`as`("£").contains("£").contains("GBP") - //assertThat(messages[1].msg).`as`("GBP 15m").contains("15m") + price = marketPrice("ETH", "EUR") + assertThat(price.base).`as`("is ETH").isEqualTo("ETH") + assertThat(price.currency).`as`("is EUR").isEqualTo("EUR") + assertThat(price.amount).`as`("ETH > 0").isGreaterThan(0.00) + + price = marketPrice("ETH2", "GBP") + assertThat(price.base).`as`("is ETH2").isEqualTo("ETH2") + assertThat(price.currency).`as`("is GBP").isEqualTo("GBP") + assertThat(price.amount).`as`("ETH2 > 0").isGreaterThan(0.00) + + assertThatThrownBy { marketPrice("FOO", "USD") } + .`as`("FOO") + .isInstanceOf(CryptoException::class.java) + .hasMessageContaining("Invalid base currency") + + assertThatThrownBy { marketPrice("FOO", "BAR") } + .`as`("FOO-BAR") + .isInstanceOf(CryptoException::class.java) + .hasMessageContaining("Invalid currency (BAR)") } } diff --git a/version.properties b/version.properties index 80c6644..f827da8 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 04 23:36:08 PDT 2021 -version.buildmeta=632 +#Sat May 08 02:52:53 PDT 2021 +version.buildmeta=688 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+632 +version.semver=0.8.0-beta+688 From ec049c748588170f9cea1c1b537913b291cb39c8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 May 2021 15:57:02 -0700 Subject: [PATCH 497/842] Updated to JDK >= 11 on CIs and switched to Gradle 7.0.1 --- .circleci/config.yml | 8 ++++---- .gitlab-ci.yml | 2 +- .travis.yml | 2 +- build.gradle | 4 +++- gradle/wrapper/gradle-wrapper.properties | 2 +- version.properties | 6 +++--- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 41e99be..33e114d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,11 +30,11 @@ defaults_gradle: &defaults_gradle path: build/reports/ jobs: - build_gradle_jdk15: + build_gradle_jdk16: <<: *defaults docker: - - image: cimg/openjdk:15.0.2 + - image: cimg/openjdk:16.0.0 <<: *defaults_gradle @@ -42,7 +42,7 @@ jobs: <<: *defaults docker: - - image: cimg/openjdk:11.0.7 + - image: cimg/openjdk:11.0.10 <<: *defaults_gradle @@ -51,5 +51,5 @@ workflows: gradle: jobs: - build_gradle_jdk11 - - build_gradle_jdk15 + - build_gradle_jdk16 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e06d44..4b01f79 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: openjdk:11-jdk-slim +image: gradle:7-jdk11 variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" diff --git a/.travis.yml b/.travis.yml index 83aefb4..84c808c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ addons: jdk: - openjdk11 - - openjdk14 + - openjdk15 before_install: - chmod +x gradlew diff --git a/build.gradle b/build.gradle index d55ba00..d5efd48 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,6 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'org.json:json:20210307' implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' @@ -73,6 +72,9 @@ dependencies { } test { + testLogging { + exceptionFormat "full" + } useTestNG() { options.suites('src/test/resources/testng.xml') } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f371643..e5338d3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/version.properties b/version.properties index f827da8..4d6d016 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat May 08 02:52:53 PDT 2021 -version.buildmeta=688 +#Mon May 10 15:50:39 PDT 2021 +version.buildmeta=696 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+688 +version.semver=0.8.0-beta+696 From eff93791d1fab49fdb9d70f301a514376241890e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 May 2021 17:20:57 -0700 Subject: [PATCH 498/842] Reverted to JDK 15 on CircleCI. --- .circleci/config.yml | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 33e114d..19da0e1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,11 +30,11 @@ defaults_gradle: &defaults_gradle path: build/reports/ jobs: - build_gradle_jdk16: + build_gradle_jdk15: <<: *defaults docker: - - image: cimg/openjdk:16.0.0 + - image: cimg/openjdk:15.0.2 <<: *defaults_gradle @@ -51,5 +51,5 @@ workflows: gradle: jobs: - build_gradle_jdk11 - - build_gradle_jdk16 + - build_gradle_jdk15 diff --git a/version.properties b/version.properties index 4d6d016..1aeba4f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon May 10 15:50:39 PDT 2021 -version.buildmeta=696 +#Mon May 10 17:07:39 PDT 2021 +version.buildmeta=698 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+696 +version.semver=0.8.0-beta+698 From 56b8114b93cae03ccc6f31921a744806ed689f00 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 10 May 2021 23:03:51 -0700 Subject: [PATCH 499/842] Cleanup. --- .../kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt | 6 +++--- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index d8ec28c..d6bb8f4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -48,14 +48,14 @@ class PinboardUtilsTest : LocalProperties() { val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) PinboardUtils.addPin(pinboard, ircServer, entry) - Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "add") + Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "addPin") entry.link = "https://www.foo.com/" PinboardUtils.updatePin(pinboard, ircServer, url, entry) - Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "update") + Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "updatePin") PinboardUtils.deletePin(pinboard, entry) - Assert.assertFalse(validatePin(apiToken, url = entry.link), "delete") + Assert.assertFalse(validatePin(apiToken, url = entry.link), "deletePin") } private fun validatePin(apiToken: String, ircServer: String = "", url: String): Boolean { diff --git a/version.properties b/version.properties index 1aeba4f..10ef04f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon May 10 17:07:39 PDT 2021 -version.buildmeta=698 +#Mon May 10 22:46:41 PDT 2021 +version.buildmeta=713 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+698 +version.semver=0.8.0-beta+713 From 22e29d5ce36b292d3ff942747347d53176a13d46 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 May 2021 00:15:28 -0700 Subject: [PATCH 500/842] Converted ModuleExceptionTest to Kotlin. --- .idea/codeStyles/Project.xml | 10 +- .idea/jarRepositories.xml | 5 + .idea/libraries-with-intellij-classes.xml | 65 +++++++++ .idea/misc.xml | 3 +- .idea/mobibot.iml | 12 -- .idea/modules.xml | 10 -- .idea/modules/mobibot.main.iml | 100 -------------- .idea/modules/mobibot.test.iml | 125 ------------------ config/detekt/baseline.xml | 1 - .../mobibot/modules/ModuleExceptionTest.java | 80 ----------- .../mobibot/modules/ModuleExceptionTest.kt | 75 +++++++++++ version.properties | 6 +- 12 files changed, 159 insertions(+), 333 deletions(-) create mode 100644 .idea/libraries-with-intellij-classes.xml delete mode 100644 .idea/mobibot.iml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/modules/mobibot.main.iml delete mode 100644 .idea/modules/mobibot.test.iml delete mode 100644 src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 10683d5..6407ef6 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -344,10 +344,18 @@ </arrangement> </codeStyleSettings> <codeStyleSettings language="kotlin"> - <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" /> <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> <option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" /> + <option name="CALL_PARAMETERS_WRAP" value="5" /> + <option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> + <option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> + <option name="METHOD_PARAMETERS_WRAP" value="5" /> + <option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> + <option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> + <option name="EXTENDS_LIST_WRAP" value="1" /> + <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> + <option name="ASSIGNMENT_WRAP" value="1" /> <indentOptions> <option name="CONTINUATION_INDENT_SIZE" value="4" /> </indentOptions> diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 29dc1da..a3c8d4d 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -36,5 +36,10 @@ <option name="name" value="MavenLocal" /> <option name="url" value="file:$MAVEN_REPOSITORY$/" /> </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" /> + </remote-repository> </component> </project> \ No newline at end of file diff --git a/.idea/libraries-with-intellij-classes.xml b/.idea/libraries-with-intellij-classes.xml new file mode 100644 index 0000000..9fa3156 --- /dev/null +++ b/.idea/libraries-with-intellij-classes.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="libraries-with-intellij-classes"> + <option name="intellijApiContainingLibraries"> + <list> + <LibraryCoordinatesState> + <option name="artifactId" value="ideaIU" /> + <option name="groupId" value="com.jetbrains.intellij.idea" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="ideaIU" /> + <option name="groupId" value="com.jetbrains" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="ideaIC" /> + <option name="groupId" value="com.jetbrains.intellij.idea" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="ideaIC" /> + <option name="groupId" value="com.jetbrains" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="pycharmPY" /> + <option name="groupId" value="com.jetbrains.intellij.pycharm" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="pycharmPY" /> + <option name="groupId" value="com.jetbrains" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="pycharmPC" /> + <option name="groupId" value="com.jetbrains.intellij.pycharm" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="pycharmPC" /> + <option name="groupId" value="com.jetbrains" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="clion" /> + <option name="groupId" value="com.jetbrains.intellij.clion" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="clion" /> + <option name="groupId" value="com.jetbrains" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="riderRD" /> + <option name="groupId" value="com.jetbrains.intellij.rider" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="riderRD" /> + <option name="groupId" value="com.jetbrains" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="goland" /> + <option name="groupId" value="com.jetbrains.intellij.goland" /> + </LibraryCoordinatesState> + <LibraryCoordinatesState> + <option name="artifactId" value="goland" /> + <option name="groupId" value="com.jetbrains" /> + </LibraryCoordinatesState> + </list> + </option> + </component> +</project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1fa7978..fb38c4a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,10 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> + <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="FrameworkDetectionExcludesConfiguration"> <file type="web" url="file://$PROJECT_DIR$" /> </component> <component name="JavaScriptSettings"> <option name="languageLevel" value="ES6" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="14" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="15" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml deleted file mode 100644 index 255f0c1..0000000 --- a/.idea/mobibot.iml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+359" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <excludeFolder url="file://$MODULE_DIR$/.gradle" /> - <excludeFolder url="file://$MODULE_DIR$/build" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - </component> -</module> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 2150ab5..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="ProjectModuleManager"> - <modules> - <module fileurl="file://$PROJECT_DIR$/.idea/mobibot.iml" filepath="$PROJECT_DIR$/.idea/mobibot.iml" /> - <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.main.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.main.iml" /> - <module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.test.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.test.iml" /> - </modules> - </component> -</project> \ No newline at end of file diff --git a/.idea/modules/mobibot.main.iml b/.idea/modules/mobibot.main.iml deleted file mode 100644 index c12d92e..0000000 --- a/.idea/modules/mobibot.main.iml +++ /dev/null @@ -1,100 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+359" type="JAVA_MODULE" version="4"> - <component name="FacetManager"> - <facet type="kotlin-language" name="Kotlin"> - <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> - <compilerSettings> - <option name="additionalArguments" value="-Xallow-no-source-files" /> - </compilerSettings> - <compilerArguments> - <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" /> - <option name="noStdlib" value="true" /> - <option name="noReflect" value="true" /> - <option name="moduleName" value="mobibot" /> - <option name="jvmTarget" value="11" /> - <option name="languageVersion" value="1.4" /> - <option name="apiVersion" value="1.4" /> - <option name="pluginOptions"> - <array /> - </option> - <option name="pluginClasspaths"> - <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.4.20/fc1d26586910b32d676480c75acd3e922e5e81fa/kotlin-compiler-embeddable-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.4.20/a051291fb01bf2397759625626fec670cd57b3f0/kotlin-daemon-embeddable-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.4.20/411fc46e908bfa9c034f52b0d31b2e1f61f06127/kotlin-reflect-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.20/2e2bf29688a76cec111df56bc5e358c5bbc5057/kotlin-scripting-common-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.20/83704fbbe39946cc2ac6d0f07f41947abeb8dc20/kotlin-scripting-jvm-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> - <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> - <option value="$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar" /> - </array> - </option> - <option name="errors"> - <ArgumentParseErrors /> - </option> - </compilerArguments> - </configuration> - </facet> - </component> - <component name="NewModuleRootManager"> - <output url="file://$MODULE_DIR$/../../build/classes/java/main" /> - <exclude-output /> - <content url="file://$MODULE_DIR$/../../build/generated/source/kapt/main"> - <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kapt/main" isTestSource="false" generated="true" /> - </content> - <content url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/main"> - <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/main" isTestSource="false" generated="true" /> - </content> - <content url="file://$MODULE_DIR$/../../src/main"> - <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module-library"> - <library name="Gradle: kaptGeneratedClasses"> - <CLASSES> - <root url="file://$MODULE_DIR$/../../build/tmp/kapt3/classes/main" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - </component> -</module> \ No newline at end of file diff --git a/.idea/modules/mobibot.test.iml b/.idea/modules/mobibot.test.iml deleted file mode 100644 index 9e64bd2..0000000 --- a/.idea/modules/mobibot.test.iml +++ /dev/null @@ -1,125 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot:test" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+359" type="JAVA_MODULE" version="4"> - <component name="FacetManager"> - <facet type="kotlin-language" name="Kotlin"> - <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false"> - <compilerSettings> - <option name="additionalArguments" value="-Xallow-no-source-files" /> - </compilerSettings> - <compilerArguments> - <option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/test" /> - <option name="classpath" value="$MODULE_DIR$/../../build/classes/java/main:/media/erik/Projects/java/mobibot/build/classes/kotlin/main:/media/erik/Projects/java/mobibot/build/tmp/kapt3/classes/main:/home/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.18.1/aaa02680dd92a568a4278bb40aa4a6305f632ec0/assertj-core-3.18.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.testng/testng/7.3.0/a5069c3dfba58d23702f96c3d9f5081f5ce7136f/testng-7.3.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.inject/guice/4.2.2/fa13659f9128f4c011c8e1d06f137083b4876377/guice-4.2.2-no_aop.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/25.1-android/bdaab946ca5ad20253502d873ba0c3313d141036/guava-25.1-android.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.78/a3927de9bd6f351429bcf763712c9890629d8f51/jcommander-1.78.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.10.3/88becdeb77cdd2457757b7268e1a10666c03d382/ant-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.21/18775fdda48574784f40b47bf478ab0593f92e4d/snakeyaml-1.21.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.10.3/9dd5189e7f561ca19833b4e3672720b9bc5cb2fe/ant-launcher-1.10.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/media/erik/Projects/maven/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/aopalliance/aopalliance/1.0/235ba8b489512805ac13a8f9ea77a1ca5ebe3e8/aopalliance-1.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-compat-qual/2.0.0/fc89b03860d11d6213d0154a62bcd1c2f69b9efa/checker-compat-qual-2.0.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.1.3/39b109f2cd352b2d71b52a3b5a1a9850e1dc304b/error_prone_annotations-2.1.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar" /> - <option name="noStdlib" value="true" /> - <option name="noReflect" value="true" /> - <option name="moduleName" value="mobibot" /> - <option name="jvmTarget" value="11" /> - <option name="friendPaths"> - <array> - <option value="$MODULE_DIR$/../../build/classes/java/main" /> - <option value="$MODULE_DIR$/../../build/classes/kotlin/main" /> - <option value="$MODULE_DIR$/../../build/tmp/kapt3/classes/main" /> - <option value="$MODULE_DIR$/../../build/libs/mobibot.jar" /> - </array> - </option> - <option name="languageVersion" value="1.4" /> - <option name="apiVersion" value="1.4" /> - <option name="pluginOptions"> - <array /> - </option> - <option name="pluginClasspaths"> - <array> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.4.20/fc1d26586910b32d676480c75acd3e922e5e81fa/kotlin-compiler-embeddable-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.4.20/a051291fb01bf2397759625626fec670cd57b3f0/kotlin-daemon-embeddable-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.4.20/411fc46e908bfa9c034f52b0d31b2e1f61f06127/kotlin-reflect-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.20/2e2bf29688a76cec111df56bc5e358c5bbc5057/kotlin-scripting-common-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.20/83704fbbe39946cc2ac6d0f07f41947abeb8dc20/kotlin-scripting-jvm-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar" /> - <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" /> - <option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> - <option value="$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar" /> - </array> - </option> - <option name="errors"> - <ArgumentParseErrors /> - </option> - </compilerArguments> - </configuration> - </facet> - </component> - <component name="NewModuleRootManager"> - <output-test url="file://$MODULE_DIR$/../../build/classes/java/test" /> - <exclude-output /> - <content url="file://$MODULE_DIR$/../../build/generated/source/kapt/test"> - <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kapt/test" isTestSource="true" generated="true" /> - </content> - <content url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/test"> - <sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/test" isTestSource="true" generated="true" /> - </content> - <content url="file://$MODULE_DIR$/../../src/test"> - <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/test/kotlin" isTestSource="true" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="library" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" /> - <orderEntry type="library" name="Gradle: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.yaml:snakeyaml:1.21" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" /> - <orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" /> - <orderEntry type="library" name="Gradle: com.google.j2objc:j2objc-annotations:1.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: org.testng:testng:7.3.0" level="project" /> - <orderEntry type="library" name="Gradle: org.assertj:assertj-core:3.18.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: com.google.inject:guice:no_aop:4.2.2" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: com.beust:jcommander:1.78" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.ant:ant:1.10.3" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.google.errorprone:error_prone_annotations:2.1.3" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: com.google.guava:guava:25.1-android" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.ant:ant-launcher:1.10.3" level="project" /> - <orderEntry type="library" name="Gradle: org.checkerframework:checker-compat-qual:2.0.0" level="project" /> - <orderEntry type="library" name="Gradle: junit:junit:4.12" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module-library"> - <library name="Gradle: kaptGeneratedClasses"> - <CLASSES> - <root url="file://$MODULE_DIR$/../../build/tmp/kapt3/classes/test" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> - <orderEntry type="module" module-name="mobibot.main" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" /> - </component> - <component name="TestModuleProperties" production-module="mobibot.main" /> -</module> \ No newline at end of file diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 5dab69a..81addac 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -39,7 +39,6 @@ <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> - <ID>ThrowsCount:CryptoPrices.kt$CryptoPrices.Companion$ @JvmStatic @Throws(ModuleException::class) fun marketPrice(base: String, currency: String): Price</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java deleted file mode 100644 index b7feb89..0000000 --- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ModuleExceptionTest.java - * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.lang.reflect.Method; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The <code>ModuleExceptionTest</code> class. - */ -public class ModuleExceptionTest { - static final String debugMessage = "debugMessage"; - static final String message = "message"; - - @DataProvider(name = "dp") - Object[][] createData(final Method m) { - return new Object[][]{ new Object[]{ new ModuleException(debugMessage, - message, - new IOException("URL http://foobar.com")) }, - new Object[]{ new ModuleException(debugMessage, - message, - new IOException("URL http://foobar.com?")) }, - new Object[]{ new ModuleException(debugMessage, message) } }; - } - - @Test(dataProvider = "dp") - final void testGetDebugMessage(final ModuleException e) { - assertThat(e.getDebugMessage()).as("get debug message").isEqualTo(debugMessage); - } - - @Test(dataProvider = "dp") - final void testGetMessage(final ModuleException e) { - assertThat(e.getMessage()).as("get message").isEqualTo(message); - } - - @Test - final void testGetSanitizedMessage() { - final String apiKey = "1234567890"; - final ModuleException e = new ModuleException(debugMessage, - message, - new IOException( - "URL http://foo.com?apiKey=" + apiKey + "&userID=me")); - assertThat(e.getSanitizedMessage(apiKey)).as("sanitized url").contains("xxxxxxxxxx").doesNotContain(apiKey); - } -} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt new file mode 100644 index 0000000..e49da97 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -0,0 +1,75 @@ +/* + * ModuleExceptionTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import org.assertj.core.api.Assertions +import org.testng.annotations.DataProvider +import org.testng.annotations.Test +import java.io.IOException +import java.lang.reflect.Method + +/** + * The `ModuleExceptionTest` class. + */ +class ModuleExceptionTest { + companion object { + const val debugMessage = "debugMessage" + const val message = "message" + } + + @DataProvider(name = "dp") + fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> { + return arrayOf( + arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), + arrayOf(ModuleException(debugMessage,message,IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(debugMessage, message)) + ) + } + + @Test(dataProvider = "dp") + fun testGetDebugMessage(e: ModuleException) { + Assertions.assertThat(e.debugMessage).describedAs("get debug message").isEqualTo(debugMessage) + } + + @Test(dataProvider = "dp") + fun testGetMessage(e: ModuleException) { + Assertions.assertThat(e.message).describedAs("get message").isEqualTo(message) + } + + @Test + fun testGetSanitizedMessage() { + val apiKey = "1234567890" + val e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) + Assertions.assertThat(e.getSanitizedMessage(apiKey)).describedAs("sanitized url") + .contains("xxxxxxxxxx").doesNotContain(apiKey) + } +} diff --git a/version.properties b/version.properties index 10ef04f..e7931a0 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon May 10 22:46:41 PDT 2021 -version.buildmeta=713 +#Tue May 11 00:11:43 PDT 2021 +version.buildmeta=720 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+713 +version.semver=0.8.0-beta+720 From faf40d3048047028d83c536ae41864a5a2bac218 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 May 2021 00:51:35 -0700 Subject: [PATCH 501/842] Cleaned up tests. --- .../net/thauvin/erik/mobibot/UtilsTest.kt | 53 ++++++++++--------- .../mobibot/commands/tell/TellMessageTest.kt | 20 +++---- .../erik/mobibot/entries/EntryLinkTest.kt | 20 +++---- .../thauvin/erik/mobibot/modules/CalcTest.kt | 11 ++-- .../erik/mobibot/modules/CryptoPricesTest.kt | 33 ++++++------ .../mobibot/modules/CurrencyConverterTest.kt | 11 ++-- .../thauvin/erik/mobibot/modules/DiceTest.kt | 6 +-- .../erik/mobibot/modules/GoogleSearchTest.kt | 14 ++--- .../thauvin/erik/mobibot/modules/JokeTest.kt | 4 +- .../erik/mobibot/modules/LookupTest.kt | 4 +- .../mobibot/modules/ModuleExceptionTest.kt | 2 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 4 +- .../mobibot/modules/RockPaperScissorsTest.kt | 18 ++++--- .../erik/mobibot/modules/StockQuoteTest.kt | 12 ++--- .../erik/mobibot/modules/TwitterTest.kt | 2 +- .../erik/mobibot/modules/Weather2Test.kt | 14 ++--- .../erik/mobibot/modules/WordTimeTest.kt | 6 +-- version.properties | 6 +-- 18 files changed, 123 insertions(+), 117 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 8a33943..a57d591 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -74,25 +74,26 @@ class UtilsTest { @Test fun testBold() { - assertThat(bold(1)).`as`("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - assertThat(bold(ascii)).`as`("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) } @Test fun testCapitalize() { - assertThat(capitalize("test")).`as`("capitalize(test)").isEqualTo("Test") - assertThat(capitalize("Test")).`as`("capitalize(Test)").isEqualTo("Test") - assertThat(capitalize(null)).`as`("captitalize(null)").isNull() - assertThat(capitalize("")).`as`("capitalize()").isEqualTo("") + assertThat(capitalize("test")).describedAs("capitalize(test)").isEqualTo("Test") + assertThat(capitalize("Test")).describedAs("capitalize(Test)").isEqualTo("Test") + assertThat(capitalize(null)).describedAs("captitalize(null)").isNull() + assertThat(capitalize("")).describedAs("capitalize()").isEqualTo("") } @Test fun testColorize() { - assertThat(colorize(ascii, Colors.REVERSE)).`as`("colorize(reverse)").isEqualTo( + assertThat(colorize(ascii, Colors.REVERSE)).describedAs("colorize(reverse)").isEqualTo( Colors.REVERSE + ascii + Colors.REVERSE ) - assertThat(colorize(ascii, Colors.RED)).`as`("colorize(red)").isEqualTo(Colors.RED + ascii + Colors.NORMAL) - assertThat(colorize(null, Colors.RED)).`as`("colorize(null)").isEqualTo(Colors.NORMAL) + assertThat(colorize(ascii, Colors.RED)).describedAs("colorize(red)") + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo(Colors.NORMAL) } @Test @@ -102,15 +103,15 @@ class UtilsTest { @Test fun testEnsureDir() { - assertThat(ensureDir("dir", false)).`as`("ensureDir(dir, false)").isEqualTo("dir" + File.separatorChar) - assertThat(ensureDir("https://erik.thauvin.net", true)).`as`("ensureDir(erik.thauvin.net, true)") + assertThat(ensureDir("dir", false)).describedAs("ensureDir(dir, false)").isEqualTo("dir" + File.separatorChar) + assertThat(ensureDir("https://erik.thauvin.net", true)).describedAs("ensureDir(erik.thauvin.net, true)") .isEqualTo("https://erik.thauvin.net/") } @Test fun testGetIntProperty() { - assertThat(getIntProperty("10", 1)).`as`("getIntProperty(10, 1)").isEqualTo(10) - assertThat(getIntProperty("a", 1)).`as`("getIntProperty(a, 1)").isEqualTo(1) + assertThat(getIntProperty("10", 1)).describedAs("getIntProperty(10, 1)").isEqualTo(10) + assertThat(getIntProperty("a", 1)).describedAs("getIntProperty(a, 1)").isEqualTo(1) } @Test @@ -120,25 +121,25 @@ class UtilsTest { @Test fun testIsoLocalDate() { - assertThat(isoLocalDate(cal.time)).`as`("isoLocalDate(date)").isEqualTo("1952-02-17") - assertThat(isoLocalDate(localDateTime)).`as`("isoLocalDate(localDate)").isEqualTo("1952-02-17") + assertThat(isoLocalDate(cal.time)).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17") + assertThat(isoLocalDate(localDateTime)).describedAs("isoLocalDate(localDate)").isEqualTo("1952-02-17") } @Test fun testObfuscate() { - assertThat(obfuscate(ascii).length).`as`("obfuscate is right length").isEqualTo(ascii.length) - assertThat(obfuscate(ascii)).`as`("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) - assertThat(obfuscate(" ")).`as`("obfuscate(blank)").isEqualTo(" ") + assertThat(obfuscate(ascii).length).describedAs("obfuscate is right length").isEqualTo(ascii.length) + assertThat(obfuscate(ascii)).describedAs("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) + assertThat(obfuscate(" ")).describedAs("obfuscate(blank)").isEqualTo(" ") } @Test fun testPlural() { val week = "week" val weeks = "weeks" - assertThat(plural(-1, week, weeks)).`as`("plural(-1)").isEqualTo(week) - assertThat(plural(0, week, weeks)).`as`("plural(0)").isEqualTo(week) - assertThat(plural(1, week, weeks)).`as`("plural(1)").isEqualTo(week) - assertThat(plural(2, week, weeks)).`as`("plural(2)").isEqualTo(weeks) + assertThat(plural(-1, week, weeks)).describedAs("plural(-1)").isEqualTo(week) + assertThat(plural(0, week, weeks)).describedAs("plural(0)").isEqualTo(week) + assertThat(plural(1, week, weeks)).describedAs("plural(1)").isEqualTo(week) + assertThat(plural(2, week, weeks)).describedAs("plural(2)").isEqualTo(weeks) } @Test @@ -166,14 +167,14 @@ class UtilsTest { @Test @Throws(IOException::class) fun testUrlReader() { - assertThat(urlReader(URL("https://postman-echo.com/status/200"))).`as`("urlReader()").isEqualTo( - "{\"status\":200}" + assertThat(urlReader(URL("https://postman-echo.com/status/200"))).describedAs("urlReader()") + .isEqualTo("{\"status\":200}" ) } @Test fun testUtcDateTime() { - assertThat(utcDateTime(cal.time)).`as`("utcDateTime(date)").isEqualTo("1952-02-17 12:30") - assertThat(utcDateTime(localDateTime)).`as`("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") + assertThat(utcDateTime(cal.time)).describedAs("utcDateTime(date)").isEqualTo("1952-02-17 12:30") + assertThat(utcDateTime(localDateTime)).describedAs("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index f0f0054..4fdd2db 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -51,17 +51,17 @@ class TellMessageTest { val recipient = "recipient" val sender = "sender" val tellMessage = TellMessage(sender, recipient, message) - assertThat(tellMessage.sender).`as`(sender).isEqualTo(sender) - assertThat(tellMessage.recipient).`as`(recipient).isEqualTo(recipient) - assertThat(tellMessage.message).`as`(message).isEqualTo(message) - assertThat(isValidDate(tellMessage.queued)).`as`("queued is valid date/time").isTrue - assertThat(tellMessage.isMatch(sender)).`as`("match sender").isTrue - assertThat(tellMessage.isMatch(recipient)).`as`("match recipient").isTrue - assertThat(tellMessage.isMatch("foo")).`as`("foo is no match").isFalse + assertThat(tellMessage.sender).describedAs(sender).isEqualTo(sender) + assertThat(tellMessage.recipient).describedAs(recipient).isEqualTo(recipient) + assertThat(tellMessage.message).describedAs(message).isEqualTo(message) + assertThat(isValidDate(tellMessage.queued)).describedAs("queued is valid date/time").isTrue + assertThat(tellMessage.isMatch(sender)).describedAs("match sender").isTrue + assertThat(tellMessage.isMatch(recipient)).describedAs("match recipient").isTrue + assertThat(tellMessage.isMatch("foo")).describedAs("foo is no match").isFalse tellMessage.isReceived = true - assertThat(tellMessage.isReceived).`as`("is received").isTrue - assertThat(isValidDate(tellMessage.receptionDate)).`as`("received is valid date/time").isTrue + assertThat(tellMessage.isReceived).describedAs("is received").isTrue + assertThat(isValidDate(tellMessage.receptionDate)).describedAs("received is valid date/time").isTrue tellMessage.isNotified = true - assertThat(tellMessage.isNotified).`as`("is notified").isTrue + assertThat(tellMessage.isNotified).describedAs("is notified").isTrue } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index dcb9378..b6a83dc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -56,37 +56,37 @@ class EntryLinkTest { entryLink.addComment("c$i", "u$i") i++ } - assertThat(entryLink.comments.size).`as`("getComments().size() == 5").isEqualTo(i) + assertThat(entryLink.comments.size).describedAs("getComments().size() == 5").isEqualTo(i) i = 0 for (comment in entryLink.comments) { - assertThat(comment.comment).`as`("getComment($i)").isEqualTo("c$i") - assertThat(comment.nick).`as`("getNick($i)").isEqualTo("u$i") + assertThat(comment.comment).describedAs("getComment($i)").isEqualTo("c$i") + assertThat(comment.nick).describedAs("getNick($i)").isEqualTo("u$i") i++ } val r = SecureRandom() while (entryLink.comments.size > 0) { entryLink.deleteComment(r.nextInt(entryLink.comments.size)) } - assertThat(entryLink.comments.isNotEmpty()).`as`("hasComments()").isFalse + assertThat(entryLink.comments.isNotEmpty()).describedAs("hasComments()").isFalse entryLink.addComment("nothing", "nobody") entryLink.setComment(0, "something", "somebody") - assertThat(entryLink.getComment(0).nick).`as`("getNick(somebody)").isEqualTo("somebody") - assertThat(entryLink.getComment(0).comment).`as`("getComment(something)").isEqualTo("something") + assertThat(entryLink.getComment(0).nick).describedAs("getNick(somebody)").isEqualTo("somebody") + assertThat(entryLink.getComment(0).comment).describedAs("getComment(something)").isEqualTo("something") } @Test fun testTags() { val tags: List<SyndCategory> = entryLink.tags for ((i, tag) in tags.withIndex()) { - assertThat(tag.name).`as`("tag.getName($i)").isEqualTo("tag" + (i + 1)) + assertThat(tag.name).describedAs("tag.getName($i)").isEqualTo("tag" + (i + 1)) } - assertThat(entryLink.tags.size).`as`("getTags().size() is 5").isEqualTo(5) - assertThat(entryLink.tags.isNotEmpty()).`as`("hasTags() is true").isTrue + assertThat(entryLink.tags.size).describedAs("getTags().size() is 5").isEqualTo(5) + assertThat(entryLink.tags.isNotEmpty()).describedAs("hasTags() is true").isTrue entryLink.setTags("-tag5") entryLink.setTags("+mobitopia") entryLink.setTags("tag4") entryLink.setTags("-mobitopia") - assertThat(entryLink.pinboardTags).`as`("getPinboardTags()") + assertThat(entryLink.pinboardTags).describedAs("getPinboardTags()") .isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index a397f5a..4064672 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -44,10 +44,11 @@ import org.testng.annotations.Test class CalcTest { @Test fun testCalculate() { - assertThat(calculate("1 + 1")).`as`("calculate(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)) - assertThat(calculate("1 -3")).`as`("calculate(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)) - assertThat(calculate("pi+π+e+φ")).`as`("calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) - assertThatThrownBy { calculate("one + one") }.`as`("calculate(one+one)") - .isInstanceOf(UnknownFunctionOrVariableException::class.java) + assertThat(calculate("1 + 1")).describedAs("calculate(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)) + assertThat(calculate("1 -3")).describedAs("calculate(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)) + assertThat(calculate("pi+π+e+φ")).describedAs("calculate(pi+π+e+φ)") + .isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) + assertThatThrownBy { calculate("one + one") }.describedAs("calculate(one+one)") + .isInstanceOf(UnknownFunctionOrVariableException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index bd07f1c..baed49c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -31,9 +31,8 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import net.thauvin.erik.crypto.CryptoException -import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test @@ -46,28 +45,28 @@ class CryptoPricesTest { @Throws(ModuleException::class) fun testMarketPrice() { var price = marketPrice("BTC", "USD") - assertThat(price.base).`as`("is BTC").isEqualTo("BTC") - assertThat(price.currency).`as`("is USD").isEqualTo("USD") - assertThat(price.amount).`as`("BTC > 0").isGreaterThan(0.00) + assertThat(price.base).describedAs("is BTC").isEqualTo("BTC") + assertThat(price.currency).describedAs("is USD").isEqualTo("USD") + assertThat(price.amount).describedAs("BTC > 0").isGreaterThan(0.00) price = marketPrice("ETH", "EUR") - assertThat(price.base).`as`("is ETH").isEqualTo("ETH") - assertThat(price.currency).`as`("is EUR").isEqualTo("EUR") - assertThat(price.amount).`as`("ETH > 0").isGreaterThan(0.00) + assertThat(price.base).describedAs("is ETH").isEqualTo("ETH") + assertThat(price.currency).describedAs("is EUR").isEqualTo("EUR") + assertThat(price.amount).describedAs("ETH > 0").isGreaterThan(0.00) price = marketPrice("ETH2", "GBP") - assertThat(price.base).`as`("is ETH2").isEqualTo("ETH2") - assertThat(price.currency).`as`("is GBP").isEqualTo("GBP") - assertThat(price.amount).`as`("ETH2 > 0").isGreaterThan(0.00) + assertThat(price.base).describedAs("is ETH2").isEqualTo("ETH2") + assertThat(price.currency).describedAs("is GBP").isEqualTo("GBP") + assertThat(price.amount).describedAs("ETH2 > 0").isGreaterThan(0.00) assertThatThrownBy { marketPrice("FOO", "USD") } - .`as`("FOO") - .isInstanceOf(CryptoException::class.java) - .hasMessageContaining("Invalid base currency") + .describedAs("FOO") + .isInstanceOf(CryptoException::class.java) + .hasMessageContaining("Invalid base currency") assertThatThrownBy { marketPrice("FOO", "BAR") } - .`as`("FOO-BAR") - .isInstanceOf(CryptoException::class.java) - .hasMessageContaining("Invalid currency (BAR)") + .describedAs("FOO-BAR") + .isInstanceOf(CryptoException::class.java) + .hasMessageContaining("Invalid currency (BAR)") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 0fbfedd..5d509d3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -51,10 +51,11 @@ class CurrencyConverterTest { @Test fun testConvertCurrency() { assertThat(convertCurrency("100 USD to EUR").msg) - .`as`("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR") - assertThat(convertCurrency("100 USD to USD").msg).`as`("100 USD to USD").contains("You're kidding, right?") - assertThat(convertCurrency("100 USD").msg).`as`("100 USD").contains("Invalid query.") - assertThat(currencyRates().size).`as`("currencyRates().size() == 33").isEqualTo(33) - assertThat(currencyRates()).`as`("currencyRates().get(EUR)").contains(" EUR: 1") + .describedAs("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR") + assertThat(convertCurrency("100 USD to USD").msg).describedAs("100 USD to USD") + .contains("You're kidding, right?") + assertThat(convertCurrency("100 USD").msg).describedAs("100 USD").contains("Invalid query.") + assertThat(currencyRates().size).describedAs("currencyRates().size() == 33").isEqualTo(33) + assertThat(currencyRates()).describedAs("currencyRates().get(EUR)").contains(" EUR: 1") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index cea3e35..681dc5b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -39,8 +39,8 @@ import org.testng.annotations.Test class DiceTest { @Test fun testWinLoseOrTie() { - assertThat(Dice.winLoseOrTie(6, 6)).`as`("6 vs. 6").isEqualTo(Dice.Result.TIE) - assertThat(Dice.winLoseOrTie(6, 5)).`as`("6 vs. 5").isEqualTo(Dice.Result.WIN) - assertThat(Dice.winLoseOrTie(5, 6)).`as`("5 vs. 6").isEqualTo(Dice.Result.LOSE) + assertThat(Dice.winLoseOrTie(6, 6)).describedAs("6 vs. 6").isEqualTo(Dice.Result.TIE) + assertThat(Dice.winLoseOrTie(6, 5)).describedAs("6 vs. 5").isEqualTo(Dice.Result.WIN) + assertThat(Dice.winLoseOrTie(5, 6)).describedAs("5 vs. 6").isEqualTo(Dice.Result.LOSE) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 4d8abd5..fd34802 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -48,19 +48,19 @@ class GoogleSearchTest : LocalProperties() { val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) try { var messages = searchGoogle("mobitopia", apiKey, cseKey) - assertThat(messages).`as`("mobitopia results not empty").isNotEmpty - assertThat(messages[0].msg).`as`("found freenode").contains("freenode") + assertThat(messages).describedAs("mobitopia results not empty").isNotEmpty + assertThat(messages[0].msg).describedAs("found freenode").contains("freenode") messages = searchGoogle("aapl", apiKey, cseKey) - assertThat(messages).`as`("aapl results not empty").isNotEmpty - assertThat(messages[0].msg).`as`("found apple").containsIgnoringCase("apple") + assertThat(messages).describedAs("aapl results not empty").isNotEmpty + assertThat(messages[0].msg).describedAs("found apple").containsIgnoringCase("apple") assertThatThrownBy { searchGoogle("test", "", "apiKey") } - .`as`("no API key") + .describedAs("no API key") .isInstanceOf(ModuleException::class.java).hasNoCause() assertThatThrownBy { searchGoogle("test", "apiKey", "") } - .`as`("no CSE API key") + .describedAs("no CSE API key") .isInstanceOf(ModuleException::class.java).hasNoCause() assertThatThrownBy { searchGoogle("", "apikey", "apiKey") } - .`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause() + .describedAs("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank() && cseKey.isNotBlank()) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 36156e2..073d089 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -42,7 +42,7 @@ class JokeTest { @Test @Throws(ModuleException::class) fun testRamdomJoke() { - assertThat(randomJoke().msg.isNotEmpty()).`as`("randomJoke() > 0").isTrue - assertThat(randomJoke().msg).`as`("randomJoke()").containsIgnoringCase("chuck") + assertThat(randomJoke().msg.isNotEmpty()).describedAs("randomJoke() > 0").isTrue + assertThat(randomJoke().msg).describedAs("randomJoke()").containsIgnoringCase("chuck") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 3b94181..9fbf64c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -44,7 +44,7 @@ class LookupTest { @Throws(Exception::class) fun testLookup() { val result = nslookup("apple.com") - assertThat(result).`as`("lookup(apple.com)").contains("17.253.144.10") + assertThat(result).describedAs("lookup(apple.com)").contains("17.253.144.10") } @Test @@ -52,6 +52,6 @@ class LookupTest { fun testWhois() { val result = whois("17.178.96.59", Lookup.WHOIS_HOST) assertThat(result.stream().anyMatch { m: String -> m.contains("Apple Inc.") }) - .`as`("whois(17.178.96.59/Apple Inc.").isTrue + .describedAs("whois(17.178.96.59/Apple Inc.").isTrue } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index e49da97..c2d61e3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -50,7 +50,7 @@ class ModuleExceptionTest { fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> { return arrayOf( arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(debugMessage,message,IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), arrayOf(ModuleException(debugMessage, message)) ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index b849192..1eba3b7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -41,13 +41,13 @@ import org.testng.annotations.Test class PingTest { @Test fun testPingsArray() { - assertThat(Ping.PINGS).`as`("Pings array is not empty.").isNotEmpty + assertThat(Ping.PINGS).describedAs("Pings array is not empty.").isNotEmpty } @Test fun testRandomPing() { for (i in 0..9) { - assertThat(randomPing()).`as`("Random ping $i").isIn(Ping.PINGS) + assertThat(randomPing()).describedAs("Random ping $i").isIn(Ping.PINGS) } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 34cf7bb..3f5352f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -38,13 +38,17 @@ import org.testng.annotations.Test class RockPaperScissorsTest { @Test fun testWinLoseOrDraw() { - assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper")).`as`("scissors vs. paper").isEqualTo("win") - assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock")).`as`("paper vs. rock").isEqualTo("win") - assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors")).`as`("rock vs. scissors").isEqualTo("win") - assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors")).`as`("paper vs. scissors").isEqualTo("lose") - assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper")).`as`("rock vs. paper").isEqualTo("lose") - assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock")).`as`("scissors vs. rock").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper")).describedAs("scissors vs. paper") + .isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock")).describedAs("paper vs. rock").isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors")).describedAs("rock vs. scissors") + .isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors")).describedAs("paper vs. scissors") + .isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper")).describedAs("rock vs. paper").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock")).describedAs("scissors vs. rock") + .isEqualTo("lose") assertThat(RockPaperScissors.winLoseOrDraw("scissors", "scissors")) - .`as`("scissors vs. scissors").isEqualTo("draw") + .describedAs("scissors vs. scissors").isEqualTo("draw") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index c12b33e..60875cf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -47,17 +47,17 @@ class StockQuoteTest : LocalProperties() { val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP) try { val messages = getQuote("apple inc", apiKey) - assertThat(messages).`as`("response not empty").isNotEmpty - assertThat(messages[0].msg).`as`("same stock symbol").startsWith("Symbol: AAPL") - assertThat(messages[1].msg).`as`("price label").startsWith(" Price: ") + assertThat(messages).describedAs("response not empty").isNotEmpty + assertThat(messages[0].msg).describedAs("same stock symbol").startsWith("Symbol: AAPL") + assertThat(messages[1].msg).describedAs("price label").startsWith(" Price: ") try { getQuote("blahfoo", apiKey) } catch (e: ModuleException) { - assertThat(e.message).`as`("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL) + assertThat(e.message).describedAs("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL) } - assertThatThrownBy { getQuote("test", "") }.`as`("no API key") + assertThatThrownBy { getQuote("test", "") }.describedAs("no API key") .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThatThrownBy { getQuote("", "apikey") }.`as`("no symbol") + assertThatThrownBy { getQuote("", "apikey") }.describedAs("no symbol") .isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 10a9d79..ce5c36d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -66,6 +66,6 @@ class TwitterTest : LocalProperties() { msg, true ).msg - ).`as`("twitterPost($msg)").isEqualTo(msg) + ).describedAs("twitterPost($msg)").isEqualTo(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 0d9570c..23d9b16 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -46,16 +46,16 @@ class Weather2Test : LocalProperties() { @Throws(ModuleException::class) fun testWeather() { var messages = getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP)) - assertThat(messages[0].msg).`as`("is Everett").contains("Everett").contains("US") - assertThat(messages[messages.size - 1].msg).`as`("is City Search").endsWith("98204%2CUS") + assertThat(messages[0].msg).describedAs("is Everett").contains("Everett").contains("US") + assertThat(messages[messages.size - 1].msg).describedAs("is City Search").endsWith("98204%2CUS") messages = getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP)) - assertThat(messages[0].msg).`as`("is UK").contains("London").contains("UK") - assertThat(messages[messages.size - 1].msg).`as`("is City Code").endsWith("4517009") + assertThat(messages[0].msg).describedAs("is UK").contains("London").contains("UK") + assertThat(messages[messages.size - 1].msg).describedAs("is City Code").endsWith("4517009") assertThatThrownBy { getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)) } - .`as`("Montpellier not found").hasCauseInstanceOf(APIException::class.java) + .describedAs("Montpellier not found").hasCauseInstanceOf(APIException::class.java) assertThatThrownBy { getWeather("test", "") } - .`as`("no API key").isInstanceOf(ModuleException::class.java).hasNoCause() + .describedAs("no API key").isInstanceOf(ModuleException::class.java).hasNoCause() messages = getWeather("", "apikey") - assertThat(messages[0].isError).`as`("no query").isTrue + assertThat(messages[0].isError).describedAs("no query").isTrue } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 4243383..d0c3e00 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -42,8 +42,8 @@ import org.testng.annotations.Test class WordTimeTest { @Test fun testTime() { - assertThat(time("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles")) - assertThat(time("BLAH").isError).`as`("BLAH").isTrue - assertThat(time("BEATS").msg).`as`("BEATS").contains("@") + assertThat(time("PST").msg).describedAs("PST").endsWith(Utils.bold("Los Angeles")) + assertThat(time("BLAH").isError).describedAs("BLAH").isTrue + assertThat(time("BEATS").msg).describedAs("BEATS").contains("@") } } diff --git a/version.properties b/version.properties index e7931a0..0d16bd8 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 11 00:11:43 PDT 2021 -version.buildmeta=720 +#Tue May 11 00:47:17 PDT 2021 +version.buildmeta=722 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+720 +version.semver=0.8.0-beta+722 From 273ac60c7db70bfcc590562f0c67b2b450d25145 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 May 2021 01:47:41 -0700 Subject: [PATCH 502/842] Fixed CryptoPrices tests. --- .../erik/mobibot/modules/CryptoPrices.kt | 37 +++++++------------ .../erik/mobibot/modules/CryptoPricesTest.kt | 23 ++---------- version.properties | 6 +-- 3 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 20c42b8..a1313a0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -31,36 +31,17 @@ */ package net.thauvin.erik.mobibot.modules -import okhttp3.OkHttpClient -import okhttp3.Request -import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import net.thauvin.erik.crypto.CryptoException +import net.thauvin.erik.crypto.CryptoPrice +import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage import net.thauvin.erik.mobibot.msg.PublicMessage -import org.json.JSONException -import org.json.JSONObject -import java.io.IOException -import java.net.URL -import java.text.DecimalFormat -import java.time.format.DateTimeFormatter -import java.time.Instant -import java.time.temporal.ChronoUnit -import java.time.ZoneId -import java.time.ZoneOffset - -data class Price(val base: String, val currency: String, val amount: Double) /** * The Cryptocurrency Prices module. */ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { - val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneOffset.UTC) - val decimalFormat = DecimalFormat("0.00") - /** * Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price). */ @@ -68,10 +49,8 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { val debugMessage = "crypto($cmd $args)" with(bot) { if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { - val params = args.trim().split(" "); try { - val currency = if (params.size == 2) params[1] else "USD" - val price = marketPrice(params[0], currency) + val price = currentPrice(args.split(' ')) send(sender, PublicMessage("${price.base}: ${price.amount} [${price.currency}]")) } catch (e: CryptoException) { if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) @@ -89,6 +68,16 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { companion object { // Crypto command private const val CRYPTO_CMD = "crypto" + + /** + * Get current market price. + */ + fun currentPrice(args: List<String>): CryptoPrice { + return if (args.size == 2) + marketPrice(args[0], args[1]) + else + marketPrice(args[0]) + } } init { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index baed49c..f046b59 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -31,10 +31,8 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.crypto.CryptoException -import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice +import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test /** @@ -44,29 +42,14 @@ class CryptoPricesTest { @Test @Throws(ModuleException::class) fun testMarketPrice() { - var price = marketPrice("BTC", "USD") + var price = currentPrice(listOf("BTC")) assertThat(price.base).describedAs("is BTC").isEqualTo("BTC") assertThat(price.currency).describedAs("is USD").isEqualTo("USD") assertThat(price.amount).describedAs("BTC > 0").isGreaterThan(0.00) - price = marketPrice("ETH", "EUR") + price = currentPrice(listOf("ETH", "EUR")) assertThat(price.base).describedAs("is ETH").isEqualTo("ETH") assertThat(price.currency).describedAs("is EUR").isEqualTo("EUR") assertThat(price.amount).describedAs("ETH > 0").isGreaterThan(0.00) - - price = marketPrice("ETH2", "GBP") - assertThat(price.base).describedAs("is ETH2").isEqualTo("ETH2") - assertThat(price.currency).describedAs("is GBP").isEqualTo("GBP") - assertThat(price.amount).describedAs("ETH2 > 0").isGreaterThan(0.00) - - assertThatThrownBy { marketPrice("FOO", "USD") } - .describedAs("FOO") - .isInstanceOf(CryptoException::class.java) - .hasMessageContaining("Invalid base currency") - - assertThatThrownBy { marketPrice("FOO", "BAR") } - .describedAs("FOO-BAR") - .isInstanceOf(CryptoException::class.java) - .hasMessageContaining("Invalid currency (BAR)") } } diff --git a/version.properties b/version.properties index 0d16bd8..be3f7f2 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 11 00:47:17 PDT 2021 -version.buildmeta=722 +#Tue May 11 01:30:42 PDT 2021 +version.buildmeta=726 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+722 +version.semver=0.8.0-beta+726 From a7988001983b09f58ff0e07e8c1e9ce3f03b2794 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 May 2021 12:42:46 -0700 Subject: [PATCH 503/842] Cleaned up usage of the Utils class. --- .../thauvin/erik/mobibot/commands/ChannelFeed.kt | 7 ++----- .../net/thauvin/erik/mobibot/commands/Cycle.kt | 4 ++-- .../net/thauvin/erik/mobibot/commands/Me.kt | 7 ++----- .../net/thauvin/erik/mobibot/commands/Modules.kt | 7 ++----- .../net/thauvin/erik/mobibot/commands/Msg.kt | 4 ++-- .../net/thauvin/erik/mobibot/commands/Nick.kt | 7 ++----- .../net/thauvin/erik/mobibot/commands/Say.kt | 7 ++----- .../net/thauvin/erik/mobibot/commands/Users.kt | 7 ++----- .../erik/mobibot/commands/links/Comment.kt | 13 +++++++------ .../erik/mobibot/commands/links/Posting.kt | 15 ++++++++------- .../thauvin/erik/mobibot/commands/links/Tags.kt | 4 ++-- .../thauvin/erik/mobibot/commands/links/View.kt | 7 ++++--- .../erik/mobibot/modules/AbstractModule.kt | 4 ++-- .../net/thauvin/erik/mobibot/modules/Calc.kt | 7 ++++--- .../thauvin/erik/mobibot/modules/CryptoPrices.kt | 10 +++++----- .../net/thauvin/erik/mobibot/modules/Dice.kt | 13 +++++++------ .../net/thauvin/erik/mobibot/modules/Lookup.kt | 4 ++-- .../erik/mobibot/modules/ModuleException.kt | 4 ++-- .../net/thauvin/erik/mobibot/modules/Ping.kt | 4 ++-- .../net/thauvin/erik/mobibot/modules/Twitter.kt | 4 ++-- .../net/thauvin/erik/mobibot/modules/WorldTime.kt | 13 +++++++------ version.properties | 6 +++--- 22 files changed, 73 insertions(+), 85 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 637aec5..9c50932 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -34,14 +34,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.FeedReader import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { override val name = channel - override val help = listOf( - "To list the last 5 posts from the channel's weblog feed:", - Utils.helpFormat("%c $channel") - ) + override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel")) override val isOp = false override val isPublic = true override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 32c06e5..88de89b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -33,13 +33,13 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class Cycle(bot: Mobibot) : AbstractCommand(bot) { @Suppress("MagicNumber") private val wait = 10 override val name = "cycle" - override val help = listOf("To have the bot leave the channel and come back:", Utils.helpFormat("%c $name")) + override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name")) override val isOp = true override val isPublic = false override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index c668fe0..4b996f1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -33,14 +33,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class Me(bot: Mobibot) : AbstractCommand(bot) { override val name = "me" - override val help = listOf( - "To have the bot perform an action:", - Utils.helpFormat("%c $name <action>") - ) + override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>")) override val isOp = true override val isPublic = false override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 50ddc93..2bd43c9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -33,14 +33,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class Modules(bot: Mobibot) : AbstractCommand(bot) { override val name = "modules" - override val help = listOf( - "To view a list of enabled modules:", - Utils.helpFormat("%c $name") - ) + override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name")) override val isOp = true override val isPublic = false override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 4e7aa4d..5e9b5ae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -33,13 +33,13 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class Msg(bot: Mobibot) : AbstractCommand(bot) { override val name = "msg" override val help = listOf( "To have the bot send a private message to someone:", - Utils.helpFormat("%c $name <nick> <text>") + helpFormat("%c $name <nick> <text>") ) override val isOp = true override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index fa1ff2c..b4495b3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -33,14 +33,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class Nick(bot: Mobibot) : AbstractCommand(bot) { override val name = "nick" - override val help = listOf( - "To change the bot's nickname:", - Utils.helpFormat("%c $name <nick>") - ) + override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <nick>")) override val isOp = true override val isPublic = true override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index af08502..198ea8c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -33,14 +33,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class Say(bot: Mobibot) : AbstractCommand(bot) { override val name = "say" - override val help = listOf( - "To have the bot say something on the channel:", - Utils.helpFormat("%c $name <text>") - ) + override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name <text>")) override val isOp = true override val isPublic = false override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 1d1ec6e..4db1781 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -33,14 +33,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat class Users(bot: Mobibot) : AbstractCommand(bot) { override val name = "users" - override val help = listOf( - "To list the users present on the channel:", - Utils.helpFormat("%c $name") - ) + override val help = listOf("To list the users present on the channel:", helpFormat("%c $name")) override val isOp = false override val isPublic = true override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 9a8685a..ebe7067 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -34,7 +34,8 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink @@ -43,12 +44,12 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { override val name = COMMAND override val help = listOf( "To add a comment:", - Utils.helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${bold(Constants.LINK_CMD)}1.1", "To edit a comment, use its label: ", - Utils.helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), + helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), "To delete a comment, use its label and a minus sign: ", - Utils.helpFormat("${Constants.LINK_CMD}1.1:-") + helpFormat("${Constants.LINK_CMD}1.1:-") ) override val isOp = false override val isPublic = true @@ -99,7 +100,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { bot.send(sender, "To change a comment's author:", isPrivate) bot.send( sender, - Utils.helpFormat("${Constants.LINK_CMD}1.1:?<nick>"), + helpFormat("${Constants.LINK_CMD}1.1:?<nick>"), isPrivate ) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index ef993cb..77c9b43 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -34,7 +34,8 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries import net.thauvin.erik.mobibot.entries.EntriesUtils @@ -44,15 +45,15 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { override val name = "posting" override val help = listOf( "Post a URL, by saying it on a line on its own:", - Utils.helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1", + helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + "I will reply with a label, for example: ${bold(Constants.LINK_CMD)}1", "To add a title, use its label and a pipe:", - Utils.helpFormat("${Constants.LINK_CMD}1:|This is the title"), + helpFormat("${Constants.LINK_CMD}1:|This is the title"), "To add a comment:", - Utils.helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${bold(Constants.LINK_CMD)}1.1", "To edit a comment, see: ", - Utils.helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOp = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1481166..554d6c8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink @@ -43,7 +43,7 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { override val name = COMMAND override val help = listOf( "To categorize or tag a URL, use its label and a T:", - Utils.helpFormat("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") + helpFormat("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") ) override val isOp = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 0627d93..45da0f5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -33,7 +33,8 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries import net.thauvin.erik.mobibot.entries.EntriesUtils @@ -45,7 +46,7 @@ class View(bot: Mobibot) : AbstractCommand(bot) { override val name = VIEW_CMD override val help = listOf( "To list or search the current URL posts:", - Utils.helpFormat("%c $name [<start>] [<query>]") + helpFormat("%c $name [<start>] [<query>]") ) override val isOp = false override val isPublic = true @@ -109,7 +110,7 @@ class View(bot: Mobibot) : AbstractCommand(bot) { i++ if (sent == maxEntries && i < max) { bot.send( - sender, "To view more, try: " + Utils.bold("${bot.nick}: $name ${i + 1} $lcArgs"), false + sender, "To view more, try: " + bold("${bot.nick}: $name ${i + 1} $lcArgs"), false ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 3448fa8..3c46fb4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax /** * The `Module` abstract class. @@ -76,7 +76,7 @@ abstract class AbstractModule(val bot: Mobibot) { */ open fun helpResponse(sender: String, isPrivate: Boolean): Boolean { for (h in help) { - bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) + bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) } return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 9704461..a197391 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -34,7 +34,8 @@ package net.thauvin.erik.mobibot.modules import net.objecthunter.exp4j.ExpressionBuilder import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat import java.text.DecimalFormat /** @@ -74,13 +75,13 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { fun calculate(query: String): String { val decimalFormat = DecimalFormat("#.##") val calc = ExpressionBuilder(query).build() - return query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate())) + return query.replace(" ", "") + " = " + bold(decimalFormat.format(calc.evaluate())) } } init { commands.add(CALC_CMD) help.add("To solve a mathematical calculation:") - help.add(Utils.helpFormat("%c $CALC_CMD <calculation>")) + help.add(helpFormat("%c $CALC_CMD <calculation>")) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index a1313a0..4cd57c2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -35,7 +35,7 @@ import net.thauvin.erik.crypto.CryptoException import net.thauvin.erik.crypto.CryptoPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.msg.PublicMessage /** @@ -83,10 +83,10 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(CRYPTO_CMD) help.add("To retrieve a cryptocurrency's market price:") - help.add(Utils.helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) + help.add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) help.add("For example:") - help.add(Utils.helpFormat("%c $CRYPTO_CMD BTC")) - help.add(Utils.helpFormat("%c $CRYPTO_CMD ETH EUR")) - help.add(Utils.helpFormat("%c $CRYPTO_CMD ETH2 GPB")) + help.add(helpFormat("%c $CRYPTO_CMD BTC")) + help.add(helpFormat("%c $CRYPTO_CMD ETH EUR")) + help.add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 53c39ad..8d1afc3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -32,7 +32,8 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat import kotlin.random.Random /** @@ -52,13 +53,13 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { with(bot) { send( channel, - "$sender rolled two dice: ${Utils.bold(playerRoll.first)} and ${Utils.bold(playerRoll.second)}" - + " for a total of ${Utils.bold(playerTotal)}", + "$sender rolled two dice: ${bold(playerRoll.first)} and ${bold(playerRoll.second)}" + + " for a total of ${bold(playerTotal)}", isPrivate ) action( - "rolled two dice: ${Utils.bold(roll.first)} and ${Utils.bold(roll.second)}" + - " for a total of ${Utils.bold(total)}" + "rolled two dice: ${bold(roll.first)} and ${bold(roll.second)}" + + " for a total of ${bold(total)}" ) when (winLoseOrTie(total, playerTotal)) { Result.WIN -> action("wins.") @@ -99,6 +100,6 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { init { commands.add(DICE_CMD) help.add("To roll the dice:") - help.add(Utils.helpFormat("%c $DICE_CMD")) + help.add(helpFormat("%c $DICE_CMD")) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 6ead8e7..8c50d09 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat import org.apache.commons.net.whois.WhoisClient import java.io.IOException import java.net.InetAddress @@ -164,6 +164,6 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { init { commands.add(LOOKUP_CMD) help.add("To perform a DNS lookup query:") - help.add(Utils.helpFormat("%c $LOOKUP_CMD <ip address or hostname>")) + help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>")) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 3e77d96..c970aca 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.obfuscate import org.apache.commons.lang3.StringUtils /** @@ -68,7 +68,7 @@ class ModuleException : Exception { * Return the sanitized message (e.g. remove API keys, etc.) */ fun getSanitizedMessage(vararg sanitize: String): String { - val obfuscate = sanitize.map { Utils.obfuscate(it) }.toTypedArray() + val obfuscate = sanitize.map { obfuscate(it) }.toTypedArray() return when { cause != null -> { cause.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index ecfdf47..0a97d7b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat import kotlin.random.Random /** @@ -85,6 +85,6 @@ class Ping(bot: Mobibot) : AbstractModule(bot) { init { commands.add(PING_CMD) help.add("To ping the bot:") - help.add(Utils.helpFormat("%c $PING_CMD")) + help.add(helpFormat("%c $PING_CMD")) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 7fdefd4..eb08dd5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.TwitterTimer -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.links.LinksMgr import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.msg.Message @@ -235,7 +235,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(TWITTER_CMD) help.add("To post to Twitter:") - help.add(Utils.helpFormat("%c $TWITTER_CMD <message>")) + help.add(helpFormat("%c $TWITTER_CMD <message>")) properties[AUTOPOST_PROP] = "false" initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 0b21462..0d74165 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -32,7 +32,8 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage @@ -76,16 +77,16 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).uppercase()] val response: String = if (tz != null) { if (BEATS_KEYWORD == tz) { - "The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD) + "The current Internet Time is: " + bold(internetTime() + ' ' + BEATS_KEYWORD) } else { (ZonedDateTime.now() .withZoneSameInstant(ZoneId.of(tz)) .format( DateTimeFormatter.ofPattern( - "'The time is ${Utils.bold("'HH:mm'")} on ${Utils.bold("'EEEE, d MMMM yyyy'")} in '" + "'The time is ${bold("'HH:mm'")} on ${bold("'EEEE, d MMMM yyyy'")} in '" ) ) - + Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' ')) + + bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' ')) ) } } else { @@ -214,9 +215,9 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { init { help.add("To display a country's current date/time:") - help.add(Utils.helpFormat("%c $TIME_CMD") + " [<country code>]") + help.add(helpFormat("%c $TIME_CMD") + " [<country code>]") help.add("For a listing of the supported countries:") - help.add(Utils.helpFormat("%c $TIME_CMD")) + help.add(helpFormat("%c $TIME_CMD")) commands.add(TIME_CMD) } } diff --git a/version.properties b/version.properties index be3f7f2..596efc1 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 11 01:30:42 PDT 2021 -version.buildmeta=726 +#Tue May 11 12:39:39 PDT 2021 +version.buildmeta=727 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+726 +version.semver=0.8.0-beta+727 From ad3a9907a3eb7609736c860dd4838e6b2ca64203 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 May 2021 13:30:58 -0700 Subject: [PATCH 504/842] Converted versions command to Kotlin. --- .../erik/mobibot/commands/Versions.java | 95 ------------------- .../thauvin/erik/mobibot/commands/Versions.kt | 65 +++++++++++++ version.properties | 6 +- 3 files changed, 68 insertions(+), 98 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Versions.java create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java b/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java deleted file mode 100644 index a86493f..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Versions.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Versions.java - * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands; - -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.ReleaseInfo; -import net.thauvin.erik.mobibot.Utils; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class Versions extends AbstractCommand { - private final List<String> allVersions = - List.of("Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')', - "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") - + " (" + System.getProperty("os.arch") + ')', - "Runtime: " + System.getProperty("java.runtime.name") - + ' ' + System.getProperty("java.runtime.version")); - - public Versions(@NotNull final Mobibot bot) { - super(bot); - } - - @NotNull - @Override - public String getName() { - return "versions"; - } - - - @NotNull - @Override - public List<String> getHelp() { - return List.of("To view the versions data (bot, platform, java, etc.):", Utils.helpFormat("%c " + getName())); - } - - @Override - public boolean isOp() { - return true; - } - - @Override - public boolean isPublic() { - return false; - } - - @Override - public boolean isVisible() { - return true; - } - - @Override - public void commandResponse(@NotNull final String sender, - @NotNull final String login, - @NotNull final String args, - final boolean isOp, - final boolean isPrivate) { - if (isOp) { - getBot().sendList(sender, allVersions, 1, isPrivate); - } else { - getBot().helpDefault(sender, false, isPrivate); - } - - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt new file mode 100644 index 0000000..57cd79b --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -0,0 +1,65 @@ +/* + * Versions.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.ReleaseInfo +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isoLocalDate + +class Versions(bot: Mobibot) : AbstractCommand(bot) { + private val allVersions = listOf( + "Version: ${ReleaseInfo.VERSION} (" + isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + + " (" + System.getProperty("os.arch") + ')', + "Runtime: " + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version") + ) + override val name = "versions" + override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) + override val isOp = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse( + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + bot.sendList(sender, allVersions, 1, isPrivate) + } else { + bot.helpDefault(sender, false, isPrivate) + } + } +} diff --git a/version.properties b/version.properties index 596efc1..aeb3fb7 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 11 12:39:39 PDT 2021 -version.buildmeta=727 +#Tue May 11 13:27:24 PDT 2021 +version.buildmeta=728 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+727 +version.semver=0.8.0-beta+728 From b98879bd7d1cf08ea5ae4964da2539fd5effd362 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 May 2021 17:51:24 -0700 Subject: [PATCH 505/842] Converted info command to Kotlin. --- .../thauvin/erik/mobibot/commands/Info.java | 109 ------------------ .../net/thauvin/erik/mobibot/commands/Info.kt | 80 +++++++++++++ 2 files changed, 80 insertions(+), 109 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/commands/Info.java create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java deleted file mode 100644 index 8597c8c..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Info.java - * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands; - -import net.thauvin.erik.mobibot.Mobibot; -import net.thauvin.erik.mobibot.ReleaseInfo; -import net.thauvin.erik.mobibot.Utils; -import net.thauvin.erik.mobibot.commands.links.LinksMgr; -import org.jetbrains.annotations.NotNull; - -import java.lang.management.ManagementFactory; -import java.util.List; - -public class Info extends AbstractCommand { - private final List<String> allVersions = List.of( - Utils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION - + " (" + Utils.green(ReleaseInfo.WEBSITE) + ')', - "Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')'); - - public Info(final Mobibot bot) { - super(bot); - } - - @NotNull - @Override - public String getName() { - return "info"; - } - - @NotNull - @Override - public List<String> getHelp() { - return List.of("To view information about the bot:", Utils.helpFormat("%c " + getName())); - } - - @Override - public boolean isOp() { - return false; - } - - @Override - public boolean isPublic() { - return true; - } - - @Override - public boolean isVisible() { - return true; - } - - @Override - public void commandResponse(@NotNull final String sender, - @NotNull final String login, - @NotNull final String args, - final boolean isOp, - final boolean isPrivate) { - getBot().sendList(sender, allVersions, 1, isPrivate); - - final StringBuilder info = new StringBuilder(29); - - info.append("Uptime: ") - .append(Utils.uptime(ManagementFactory.getRuntimeMXBean().getUptime())) - .append(" [Entries: ") - .append(LinksMgr.entries.size()); - - if (isOp) { - if (getBot().getTell().isEnabled()) { - info.append(", Messages: ").append(getBot().getTell().size()); - } - if (getBot().getTwitter().isAutoPost()) { - info.append(", Twitter: ").append(getBot().getTwitter().entriesCount()); - } - } - - info.append(", Recap: ").append(Recap.recaps.size()).append(']'); - - getBot().send(sender, info.toString(), isPrivate); - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt new file mode 100644 index 0000000..d3df42e --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -0,0 +1,80 @@ +/* + * Info.java + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.ReleaseInfo +import net.thauvin.erik.mobibot.Utils.capitalize +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.uptime +import net.thauvin.erik.mobibot.commands.links.LinksMgr +import java.lang.management.ManagementFactory + +class Info(bot: Mobibot?) : AbstractCommand(bot!!) { + private val allVersions = listOf( + capitalize(ReleaseInfo.PROJECT) + " ${ReleaseInfo.VERSION} (" + green(ReleaseInfo.WEBSITE) + ')', + "Written by ${ReleaseInfo.AUTHOR} (" + green(ReleaseInfo.AUTHOR_URL) + ')' + ) + override val name = "info" + override val help = listOf("To view information about the bot:", helpFormat("%c $name")) + override val isOp = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse( + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + with(bot) { + sendList(sender, allVersions, 1, isPrivate) + val info = StringBuilder() + info.append("Uptime: ") + .append(uptime(ManagementFactory.getRuntimeMXBean().uptime)) + .append(" [Entries: ") + .append(LinksMgr.entries.size) + if (isOp) { + if (tell.isEnabled()) { + info.append(", Messages: ").append(tell.size()) + } + if (twitter.isAutoPost) { + info.append(", Twitter: ").append(twitter.entriesCount()) + } + } + info.append(", Recap: ").append(Recap.recaps.size).append(']') + send(sender, info.toString(), isPrivate) + } + } +} From 0d3dc268ca99ecf839f3be410e27447f9c5e7baf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 11 May 2021 17:51:52 -0700 Subject: [PATCH 506/842] Fixed Google search test, again. --- .../net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index fd34802..f2fec0b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -49,7 +49,7 @@ class GoogleSearchTest : LocalProperties() { try { var messages = searchGoogle("mobitopia", apiKey, cseKey) assertThat(messages).describedAs("mobitopia results not empty").isNotEmpty - assertThat(messages[0].msg).describedAs("found freenode").contains("freenode") + assertThat(messages[0].msg).describedAs("found mobibtopia").contains("mobitopia") messages = searchGoogle("aapl", apiKey, cseKey) assertThat(messages).describedAs("aapl results not empty").isNotEmpty assertThat(messages[0].msg).describedAs("found apple").containsIgnoringCase("apple") diff --git a/version.properties b/version.properties index aeb3fb7..bec7ede 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 11 13:27:24 PDT 2021 -version.buildmeta=728 +#Tue May 11 17:49:06 PDT 2021 +version.buildmeta=730 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+728 +version.semver=0.8.0-beta+730 From 694cc119edbb458423fb905df2d712e956af5dd6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 12 May 2021 19:03:14 -0700 Subject: [PATCH 507/842] Removed spotbugs. --- build.gradle | 28 ++----------- config/spotbugs/excludeFilter.xml | 40 ------------------- .../thauvin/erik/mobibot/TwitterOAuth.java | 4 +- .../net/thauvin/erik/mobibot/modules/War.java | 15 ++++--- version.properties | 6 +-- 5 files changed, 14 insertions(+), 79 deletions(-) delete mode 100644 config/spotbugs/excludeFilter.xml diff --git a/build.gradle b/build.gradle index d5efd48..5fe6323 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,6 @@ plugins { id 'application' id 'checkstyle' id 'com.github.ben-manes.versions' version '0.38.0' - id 'com.github.spotbugs' version '4.7.1' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.17.0-RC1' id 'jacoco' @@ -23,7 +22,6 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" ext.versions = [ log4j : '2.14.1', pmd : '6.34.0', - spotbugs: '4.2.3' ] repositories { @@ -61,19 +59,14 @@ dependencies { implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' - compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" - testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs" - testImplementation 'org.assertj:assertj-core:3.19.0' testImplementation 'org.testng:testng:7.4.0' - - spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0' - spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7' } test { testLogging { exceptionFormat "full" + events /* "passed", */ "skipped", "failed" } useTestNG() { options.suites('src/test/resources/testng.xml') @@ -106,22 +99,7 @@ compileJava { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { - jvmTarget = '11' - } -} - -spotbugs { - toolVersion.set("$versions.spotbugs") - excludeFilter.set(file("$projectDir/config/spotbugs/excludeFilter.xml")) - tasks.spotbugsMain { - reports.create("html") { - enabled = true - } - } - tasks.spotbugsTest { - reports.create("html") { - enabled = true - } + jvmTarget = java.targetCompatibility.toString() } } @@ -213,7 +191,7 @@ task copyToDeployLib(type: Copy) { } task deploy(dependsOn: ['clean', 'build', 'jar']) { - description = 'Copies all needed files to the ${deployDir} directory.' + description = "Copies all needed files to the ${deployDir} directory." group = 'Publishing' outputs.dir deployDir inputs.files copyToDeploy diff --git a/config/spotbugs/excludeFilter.xml b/config/spotbugs/excludeFilter.xml deleted file mode 100644 index 21512f0..0000000 --- a/config/spotbugs/excludeFilter.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<FindBugsFilter - xmlns="https://github.com/spotbugs/filter/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/4.0.0/spotbugs/etc/findbugsfilter.xsd"> - <Match> - <Or> - <Package name="net.thauvin.erik.mobibot.*"/> - <Package name="net.thauvin.erik.mobibot.commands.tell.*"/> - <Package name="net.thauvin.erik.mobibot.entries.*"/> - </Or> - <Or> - <Bug pattern="PATH_TRAVERSAL_IN"/> - <Bug pattern="PATH_TRAVERSAL_OUT"/> - - </Or> - <Confidence value="2"/> - </Match> - <Match> - <Or> - <Class name="net.thauvin.erik.mobibot.Mobibot"/> - <Class name="net.thauvin.erik.mobibot.Pinboard"/> - <Class name="net.thauvin.erik.mobibot.FeedReader"/> - <Class name="net.thauvin.erik.mobibot.commands.tell.Tell"/> - <Class name="net.thauvin.erik.mobibot.commands.Versions"/> - <Package name="net.thauvin.erik.mobibot.modules.*"/> - <Package name="net.thauvin.erik.mobibot.entries.*"/> - </Or> - <Bug pattern="FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY"/> - </Match> - <Match> - <Class name="net.thauvin.erik.mobibot.Mobibot"/> - <Method name="main"/> - <Bug pattern="PATH_TRAVERSAL_OUT"/> - <Confidence value="1"/> - </Match> - <Match> - <Source name="~.*\.kt"/> - </Match> -</FindBugsFilter> diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java index 8396fce..2690710 100644 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import twitter4j.TwitterException; import twitter4j.TwitterFactory; import twitter4j.auth.AccessToken; @@ -69,8 +68,7 @@ public final class TwitterOAuth { * @throws TwitterException If an error occurs. * @throws IOException If an IO error occurs. */ - @SuppressFBWarnings({ "DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE" }) - @SuppressWarnings({ "PMD.AvoidPrintStackTrace", "PMD.SystemPrintln" }) + @SuppressWarnings({ "PMD.SystemPrintln" }) public static void main(final String[] args) throws TwitterException, IOException { if (args.length == 2) { final twitter4j.Twitter twitter = new TwitterFactory().getInstance(); diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index af9e1d4..8c18b9f 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import org.jetbrains.annotations.NotNull; @@ -56,6 +55,8 @@ public final class War extends AbstractModule { new String[]{ "Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2" }; // Suits for the deck of card private static final String[] WAR_SUITS = new String[]{ "Hearts", "Spades", "Diamonds", "Clubs" }; + // Random + private static final SecureRandom RANDOM = new SecureRandom(); /** * The default constructor. @@ -72,24 +73,22 @@ public final class War extends AbstractModule { /** * {@inheritDoc} */ - @SuppressFBWarnings("DMI_RANDOM_USED_ONLY_ONCE") @Override public void commandResponse(@NotNull final String sender, @NotNull final String cmd, @NotNull final String args, final boolean isPrivate) { - final SecureRandom r = new SecureRandom(); - int i; int y; while (true) { - i = r.nextInt(WAR_DECK.length); - y = r.nextInt(WAR_DECK.length); + i = RANDOM.nextInt(WAR_DECK.length); + y = RANDOM.nextInt(WAR_DECK.length); getBot().send(sender + " drew the " + bold(WAR_DECK[i]) + " of " - + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); - getBot().action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)])); + + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); + getBot().action("drew the " + bold(WAR_DECK[y]) + " of " + + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); if (i != y) { break; diff --git a/version.properties b/version.properties index bec7ede..7802055 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 11 17:49:06 PDT 2021 -version.buildmeta=730 +#Wed May 12 19:01:23 PDT 2021 +version.buildmeta=751 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+730 +version.semver=0.8.0-beta+751 From 11a75a7179bc05fb763959bb7ef25579a6e630f8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 13 May 2021 18:38:09 -0700 Subject: [PATCH 508/842] Moved TwitterOAuth to Kotlin. --- .../thauvin/erik/mobibot/TwitterOAuth.java | 111 ----------------- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 113 ++++++++++++++++++ 2 files changed, 113 insertions(+), 111 deletions(-) delete mode 100644 src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java deleted file mode 100644 index 2690710..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * TwitterOAuth.java - * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot; - -import twitter4j.TwitterException; -import twitter4j.TwitterFactory; -import twitter4j.auth.AccessToken; -import twitter4j.auth.RequestToken; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -/** - * The <code>TwitterOAuth</code> class. - * <p> - * Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> to register your bot. - * </p> - * Then execute: - * <p> - * <code> - * java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> - * </code> - * </p> - * and follow the prompts/instructions. - * - * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @author <a href="http://twitter4j.org/en/code-examples.html#oauth" target="_blank">Twitter4J</a> - * @created Sep 13, 2010 - * @since 1.0 - */ -@SuppressWarnings("PMD.UseUtilityClass") -public final class TwitterOAuth { - /** - * Twitter OAuth Client Registration. - * - * @param args The consumerKey and consumerSecret should be passed as arguments. - * @throws TwitterException If an error occurs. - * @throws IOException If an IO error occurs. - */ - @SuppressWarnings({ "PMD.SystemPrintln" }) - public static void main(final String[] args) throws TwitterException, IOException { - if (args.length == 2) { - final twitter4j.Twitter twitter = new TwitterFactory().getInstance(); - twitter.setOAuthConsumer(args[0], args[1]); - final RequestToken requestToken = twitter.getOAuthRequestToken(); - AccessToken accessToken = null; - try (final BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { - while (null == accessToken) { - System.out.println("Open the following URL and grant access to your account:"); - System.out.println(requestToken.getAuthorizationURL()); - System.out.print("Enter the PIN (if available) or just hit enter.[PIN]:"); - final String pin = br.readLine(); - try { - if (pin != null && pin.length() > 0) { - accessToken = twitter.getOAuthAccessToken(requestToken, pin); - } else { - accessToken = twitter.getOAuthAccessToken(); - } - - System.out.println( - "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey=" - + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token=" - + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken - .getTokenSecret()); - } catch (TwitterException te) { - if (401 == te.getStatusCode()) { - System.out.println("Unable to get the access token."); - } else { - te.printStackTrace(); - } - } - } - } - } else { - System.out.println("Usage: " + TwitterOAuth.class.getName() + " <consumerKey> <consumerSecret>"); - } - - System.exit(0); - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt new file mode 100644 index 0000000..31bde39 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt @@ -0,0 +1,113 @@ +/* + * TwitterOAuth.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import twitter4j.TwitterException +import twitter4j.TwitterFactory +import twitter4j.auth.AccessToken +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import kotlin.system.exitProcess + +/** + * The `TwitterOAuth` class. + * + * Go to [https://developer.twitter.com/en/apps](https://developer.twitter.com/en/apps) to register your bot. + * + * Then execute: + * + * `java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret>` + * + * and follow the prompts/instructions. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net) + * @author [Twitter4J Example](https://twitter4j.org/en/code-examples.html#oauth) + */ +object TwitterOAuth { + /** + * Twitter OAuth Client Registration. + * + * @param args The consumerKey and consumerSecret should be passed as arguments. + */ + @Throws(TwitterException::class, IOException::class) + @JvmStatic + fun main(args: Array<String>) { + if (args.size == 2) { + with(TwitterFactory.getSingleton()) { + setOAuthConsumer(args[0], args[1]) + val requestToken = oAuthRequestToken + var accessToken: AccessToken? = null + BufferedReader(InputStreamReader(System.`in`)).use { br -> + while (null == accessToken) { + print( + """ + Open the following URL and grant access to your account: + + ${requestToken.authorizationURL} + + Enter the PIN (if available) or just hit enter. [PIN]: """.trimIndent() + ) + val pin = br.readLine() + try { + accessToken = if (!pin.isNullOrEmpty()) { + getOAuthAccessToken(requestToken, pin) + } else { + oAuthAccessToken + } + println( + """ + Please add the following to the bot's property file: + + twitter-consumerKey=${args[0]} + twitter-consumerSecret=${args[1]} + twitter-token=${accessToken?.token} + twitter-tokenSecret=${accessToken?.tokenSecret} + """.trimIndent() + ) + } catch (te: TwitterException) { + @Suppress("MagicNumber") + if (401 == te.statusCode) { + println("Unable to get the access token.") + } else { + te.printStackTrace() + } + } + } + } + } + } else { + println("Usage: ${TwitterOAuth::class.java.name} <consumerKey> <consumerSecret>") + } + exitProcess(0) + } +} From 790bd3159e9e9d3c88f94fecef9d4e1b4be3a6dc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 13 May 2021 21:37:16 -0700 Subject: [PATCH 509/842] Cleanup. --- .idea/codeStyles/Project.xml | 3 + .idea/codeStyles/codeStyleConfig.xml | 1 - build.gradle | 6 -- config/detekt/baseline.xml | 1 + properties/mobibot.properties | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 4 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 6 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 10 ++-- .../net/thauvin/erik/mobibot/PinboardUtils.kt | 3 +- .../net/thauvin/erik/mobibot/TwitterTimer.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 6 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/entries/EntriesMgr.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 4 +- .../erik/mobibot/modules/StockQuote.kt | 59 ++++++++++--------- .../thauvin/erik/mobibot/modules/Twitter.kt | 1 - .../thauvin/erik/mobibot/modules/Weather2.kt | 3 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 6 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- version.properties | 6 +- 23 files changed, 67 insertions(+), 69 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 6407ef6..321b376 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -14,6 +14,9 @@ <option name="JD_INDENT_ON_CONTINUATION" value="true" /> </JavaCodeStyleSettings> <JetCodeStyleSettings> + <option name="PACKAGES_TO_USE_STAR_IMPORTS"> + <value /> + </option> <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 23f4bb5..79ee123 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,6 +1,5 @@ <component name="ProjectCodeStyleConfiguration"> <state> <option name="USE_PER_PROJECT_SETTINGS" value="true" /> - <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> </state> </component> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5fe6323..871deaa 100644 --- a/build.gradle +++ b/build.gradle @@ -116,12 +116,6 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } -javadoc { - options.tags = ['created'] - options.author = true - options.links('http://www.jibble.org/javadocs/pircbot/', 'http://docs.oracle.com/javase/8/docs/api/') -} - jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 81addac..07ee2ba 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -34,6 +34,7 @@ <ID>NestedBlockDepth:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID> <ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?, logger: Logger)</ID> + <ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @Throws(TwitterException::class, IOException::class) @JvmStatic fun main(args: Array<String>)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 4043eeb..8089d08 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -27,7 +27,7 @@ tell-max-size=50 #pinboard-api-token=user\:TOKEN # -# Configure app at: https://apps.twitter.com/ +# Configure app at: https://developer.twitter.com/en/apps # and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret> # #twitter-consumerKey= diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 8c18b9f..7795e33 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -48,6 +48,8 @@ import static net.thauvin.erik.mobibot.Utils.bold; * @since 1.0 */ public final class War extends AbstractModule { + // Random + private static final SecureRandom RANDOM = new SecureRandom(); // War command private static final String WAR_CMD = "war"; // Deck of card @@ -55,8 +57,6 @@ public final class War extends AbstractModule { new String[]{ "Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2" }; // Suits for the deck of card private static final String[] WAR_SUITS = new String[]{ "Hearts", "Spades", "Diamonds", "Clubs" }; - // Random - private static final SecureRandom RANDOM = new SecureRandom(); /** * The default constructor. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index c2151a0..5496fd9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.modules.AbstractModule -import java.util.* +import java.util.Properties /** * Modules and Commands addons. @@ -100,8 +100,8 @@ class Addons { val mods = if (isPrivate) modules.filter { it.isPrivateMsgEnabled } else modules for (module in mods) { if (module.commands.contains(cmd)) { - module.commandResponse(sender, cmd, args, isPrivate) - return true + module.commandResponse(sender, cmd, args, isPrivate) + return true } } return false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 317cc35..d1b33b3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot -import java.util.* +import java.util.Locale /** * The `Constants`. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 1efd54b..90997bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -64,8 +64,8 @@ import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.commands.tell.Tell import net.thauvin.erik.mobibot.entries.EntriesMgr import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.modules.CryptoPrices import net.thauvin.erik.mobibot.modules.Calc +import net.thauvin.erik.mobibot.modules.CryptoPrices import net.thauvin.erik.mobibot.modules.CurrencyConverter import net.thauvin.erik.mobibot.modules.Dice import net.thauvin.erik.mobibot.modules.GoogleSearch @@ -99,7 +99,8 @@ import java.io.IOException import java.io.PrintStream import java.nio.file.Files import java.nio.file.Paths -import java.util.* +import java.util.Properties +import java.util.Timer import java.util.logging.ConsoleHandler import java.util.regex.Pattern import kotlin.system.exitProcess @@ -358,7 +359,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert helpResponse(sender, args, true) } else if (isOp && Constants.KILL_CMD == cmd) { // kill twitter.notification("$name killed by $sender on $channel") - sendRawLine("QUIT : Poof!") + sendRawLine("QUIT :Poof!") exitProcess(0) } else if (isOp && Constants.DIE_CMD == cmd) { // die send("$sender has just signed my death sentence.") @@ -492,8 +493,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert * Returns the bot's version. */ override fun onVersion(sourceNick: String, sourceLogin: String, sourceHostname: String, target: String) { - sendRawLine("NOTICE " + sourceNick + " :\u0001VERSION " + ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION - + "\u0001") + sendRawLine("NOTICE $sourceNick :\u0001VERSION ${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}\u0001") } companion object { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt index 29460ed..8c388f9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.entries.EntryLink @@ -40,7 +39,7 @@ import net.thauvin.erik.pinboard.PinboardPoster import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter -import java.util.* +import java.util.Date /** * Handles posts to pinboard.in. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt index 0b8ad2a..6b3a4b3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot -import java.util.* +import java.util.TimerTask class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() { override fun run() { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 2304bc0..82ce058 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -44,7 +44,7 @@ import java.nio.charset.StandardCharsets import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter -import java.util.* +import java.util.Date import java.util.concurrent.TimeUnit import java.util.stream.Collectors @@ -82,13 +82,13 @@ object Utils { val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) return StringUtils.replaceEach(text, searchFlags, replace) } - + /** * Capitalize a string. */ @JvmStatic fun capitalize(s: String?): String? = s?.replaceFirstChar { it.uppercase() } - + /** * Colorize a string. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 80b7f79..9052110 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -327,7 +327,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { const val MAX_DAYS_PROP = "tell-max-days" /** - * Max size proeprty. + * Max size property. */ const val MAX_SIZE_PROP = "tell-max-size" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 00fe777..50a8e6a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -47,7 +47,7 @@ import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths -import java.util.* +import java.util.Calendar /** * Manages the feed entries. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 08d10e7..09f22a7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -36,7 +36,8 @@ import com.rometools.rome.feed.synd.SyndCategoryImpl import net.thauvin.erik.mobibot.commands.links.LinksMgr import org.apache.commons.lang3.StringUtils import java.io.Serializable -import java.util.* +import java.util.Calendar +import java.util.Date /** * The class used to store link entries. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index a197391..11602e6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -52,10 +52,10 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { try { bot.send(calculate(args)) } catch (e: IllegalArgumentException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Failed to calcualte: $args", e) + if (bot.logger.isWarnEnabled) bot.logger.warn("Failed to calculate: $args", e) bot.send("No idea. This is the kind of math I don't get.") } catch (e: UnknownFunctionOrVariableException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to calcualte: $args", e) + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to calculate: $args", e) bot.send("No idea. I must've some form of Dyscalculia.") } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index f8e1a8f..60c5f0f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -79,7 +79,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { const val INVALID_SYMBOL = "Invalid symbol." // Alpha Advantage URL - private const val ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=" + private const val ALPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=" // Quote command private const val STOCK_CMD = "stock" @@ -122,7 +122,8 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Utils.capitalize(STOCK_CMD)} is disabled. The API key is missing.") + "${Utils.capitalize(STOCK_CMD)} is disabled. The API key is missing." + ) } return if (symbol.isNotBlank()) { val debugMessage = "getQuote($symbol)" @@ -132,10 +133,10 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { with(messages) { // Search for symbol/keywords response = Utils.urlReader( - URL( - "${ALAPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) - + "&apikey=" + Utils.encodeUrl(apiKey) - ) + URL( + "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + + "&apikey=" + Utils.encodeUrl(apiKey) + ) ) var json = getJsonResponse(response, debugMessage) val symbols = json.getJSONArray("bestMatches") @@ -146,11 +147,11 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { // Get quote for symbol response = Utils.urlReader( - URL( - "${ALAPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" - + Utils.encodeUrl(symbolInfo.getString("1. symbol")) - + "&apikey=" + Utils.encodeUrl(apiKey) - ) + URL( + "${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" + + Utils.encodeUrl(symbolInfo.getString("1. symbol")) + + "&apikey=" + Utils.encodeUrl(apiKey) + ) ) json = getJsonResponse(response, debugMessage) val quote = json.getJSONObject("Global Quote") @@ -159,36 +160,36 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { return messages } add( - PublicMessage( - "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) - + " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']' - ) + PublicMessage( + "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + + " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']' + ) ) add(PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))) add( - PublicMessage( - " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")) - ) + PublicMessage( + " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")) + ) ) add(NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))) add(NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))) add(NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))) add( - NoticeMessage( - " Volume: " + Utils.unescapeXml(quote.getString("06. volume")) - ) + NoticeMessage( + " Volume: " + Utils.unescapeXml(quote.getString("06. volume")) + ) ) add( - NoticeMessage( - " Latest: " - + Utils.unescapeXml(quote.getString("07. latest trading day")) - ) + NoticeMessage( + " Latest: " + + Utils.unescapeXml(quote.getString("07. latest trading day")) + ) ) add( - NoticeMessage( - " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" - + Utils.unescapeXml(quote.getString("10. change percent")) + ']' - ) + NoticeMessage( + " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + + Utils.unescapeXml(quote.getString("10. change percent")) + ']' + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index eb08dd5..09441a4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -42,7 +42,6 @@ import net.thauvin.erik.mobibot.msg.NoticeMessage import twitter4j.TwitterException import twitter4j.TwitterFactory import twitter4j.conf.ConfigurationBuilder -import java.util.* /** * The Twitter module. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 8c01cff..ae49759 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -102,7 +102,8 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Utils.capitalize(WEATHER_CMD)} is disabled. The API key is missing.") + "${Utils.capitalize(WEATHER_CMD)} is disabled. The API key is missing." + ) } val owm = OWM(apiKey) val messages = mutableListOf<Message>() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 0d74165..78c91ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -41,8 +41,8 @@ import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField -import java.util.* -import kotlin.collections.ArrayList +import java.util.Collections +import java.util.Locale /** * The WorldTime module. @@ -146,7 +146,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["MX"] = "America/Mexico_City" countries["NL"] = "Europe/Amsterdam" countries["NO"] = "Europe/Oslo" - countries["NP"] = "Asia/Katmandu" + countries["NP"] = "Asia/Kathmandu" countries["NZ"] = "Pacific/Auckland" countries["PDT"] = "America/Los_Angeles" countries["PH"] = "Asia/Manila" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 0c7095e..70434a4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -35,7 +35,7 @@ import org.testng.annotations.BeforeSuite import java.io.IOException import java.nio.file.Files import java.nio.file.Paths -import java.util.* +import java.util.Properties /** * Access to `local.properties`. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index a57d591..8b75b72 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -56,7 +56,7 @@ import java.io.File import java.io.IOException import java.net.URL import java.time.LocalDateTime -import java.util.* +import java.util.Calendar /** * The `Utils Test` class. diff --git a/version.properties b/version.properties index 7802055..3846e61 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed May 12 19:01:23 PDT 2021 -version.buildmeta=751 +#Thu May 13 21:34:31 PDT 2021 +version.buildmeta=771 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+751 +version.semver=0.8.0-beta+771 From 2e1c6e77324bdb8b00501258fae0865e473ad289 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 14 May 2021 20:57:25 -0700 Subject: [PATCH 510/842] Utils functions improvements. --- .../net/thauvin/erik/mobibot/Mobibot.kt | 10 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 139 ++++++++---------- .../erik/mobibot/commands/AbstractCommand.kt | 4 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 18 ++- .../net/thauvin/erik/mobibot/commands/Info.kt | 6 +- .../thauvin/erik/mobibot/commands/Recap.kt | 7 +- .../thauvin/erik/mobibot/commands/Versions.kt | 4 +- .../erik/mobibot/commands/links/LinksMgr.kt | 18 ++- .../erik/mobibot/commands/tell/Tell.kt | 35 ++--- .../erik/mobibot/entries/EntriesMgr.kt | 4 +- .../erik/mobibot/modules/CurrencyConverter.kt | 17 ++- .../erik/mobibot/modules/GoogleSearch.kt | 18 ++- .../net/thauvin/erik/mobibot/modules/Joke.kt | 10 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 13 +- .../erik/mobibot/modules/StockQuote.kt | 58 ++++---- .../thauvin/erik/mobibot/modules/Weather2.kt | 17 ++- .../thauvin/erik/mobibot/modules/WorldTime.kt | 4 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 44 +++--- .../thauvin/erik/mobibot/modules/CalcTest.kt | 8 +- .../erik/mobibot/modules/WordTimeTest.kt | 4 +- version.properties | 6 +- 22 files changed, 221 insertions(+), 225 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 90997bc..2bb5d56 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -36,11 +36,11 @@ import net.thauvin.erik.mobibot.PinboardUtils.deletePin import net.thauvin.erik.mobibot.PinboardUtils.updatePin import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.colorize -import net.thauvin.erik.mobibot.Utils.ensureDir import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isoLocalDate import net.thauvin.erik.mobibot.Utils.today +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.Utils.toDir import net.thauvin.erik.mobibot.commands.AddLog import net.thauvin.erik.mobibot.commands.ChannelFeed import net.thauvin.erik.mobibot.commands.Cycle @@ -546,7 +546,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } commandLine.hasOption(Constants.VERSION_ARG[0]) -> { // Output the version - println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${isoLocalDate(ReleaseInfo.BUILDDATE)})") + println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})") println(ReleaseInfo.WEBSITE) } else -> { @@ -567,7 +567,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) val channel = p.getProperty("channel") - val logsDir = ensureDir(p.getProperty("logs", "."), false) + val logsDir = p.getProperty("logs", ".").toDir() // Redirect stdout and stderr if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { @@ -637,7 +637,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert // Set the URLs weblogUrl = p.getProperty("weblog", "") - backlogsUrl = ensureDir(p.getProperty("backlogs", weblogUrl), true) + backlogsUrl = p.getProperty("backlogs", weblogUrl).toDir(true) // Load the current entries and backlogs, if any try { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 82ce058..c5b4919 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -87,7 +87,7 @@ object Utils { * Capitalize a string. */ @JvmStatic - fun capitalize(s: String?): String? = s?.replaceFirstChar { it.uppercase() } + fun String.capitalise(): String = this.replaceFirstChar { it.uppercase() } /** * Colorize a string. @@ -115,30 +115,6 @@ object Utils { @JvmStatic fun encodeUrl(s: String): String = URLEncoder.encode(s, StandardCharsets.UTF_8) - /** - * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. - */ - @JvmStatic - fun ensureDir(location: String, isUrl: Boolean): String { - return if (location.isNotEmpty()) { - if (isUrl) { - if (location[location.length - 1] == '/') { - location - } else { - "$location/" - } - } else { - if (location[location.length - 1] == File.separatorChar) { - location - } else { - location + File.separatorChar - } - } - } else { - location - } - } - /** * Returns a property as an int. */ @@ -166,46 +142,27 @@ object Utils { return (if (isIndent) " " else "").plus(if (isBold) bold(help) else help) } - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun isoLocalDate(date: Date): String { - return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) - } - - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun isoLocalDate(date: LocalDateTime): String = date.format(DateTimeFormatter.ISO_LOCAL_DATE) - /** * Obfuscates the given string. */ @JvmStatic - fun obfuscate(s: String): String { - return if (s.isNotBlank()) { - StringUtils.repeat('x', s.length) - } else s + fun String.obfuscate(): String { + return if (this.isNotBlank()) { + StringUtils.repeat('x', this.length) + } else this } /** * Returns the plural form of a word, if count > 1. */ - @JvmStatic - fun plural(count: Int, word: String, plural: String): String = plural(count.toLong(), word, plural) - + fun String.plural(count: Int, plural: String): String = this.plural(count.toLong(), plural) + /** * Returns the plural form of a word, if count > 1. */ @JvmStatic - fun plural(count: Long, word: String, plural: String): String { - return if (count > 1) { - plural - } else { - word - } + fun String.plural(count: Long, plural: String): String { + return if (count > 1) plural else this } /** @@ -224,7 +181,55 @@ object Utils { * Returns today's date. */ @JvmStatic - fun today(): String = isoLocalDate(LocalDateTime.now()) + fun today(): String = LocalDateTime.now().toIsoLocalDate() + + /** + * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. + */ + @JvmStatic + fun String.toDir(isUrl: Boolean = false) : String { + return if (isUrl) { + if (this.last() == '/') { + this + } else { + "$this/" + } + } else { + if (this.last() == File.separatorChar) { + this + } else { + this + File.separatorChar + } + } + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun Date.toIsoLocalDate(): String { + return LocalDateTime.ofInstant(this.toInstant(), ZoneId.systemDefault()).toIsoLocalDate() + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun LocalDateTime.toIsoLocalDate(): String = this.format(DateTimeFormatter.ISO_LOCAL_DATE) + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun Date.toUtcDateTime(): String { + return LocalDateTime.ofInstant(this.toInstant(), ZoneId.systemDefault()).toUtcDateTime() + } + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun LocalDateTime.toUtcDateTime(): String = this.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) /** * Converts XML/XHTML entities to plain text. @@ -254,21 +259,21 @@ object Utils { ) with(info) { if (years > 0) { - append(years).append(plural(years, " year ", " years ")) + append(years).append(" year ".plural(years, " years ")) } if (months > 0) { - append(weeks).append(plural(months, " month ", " months ")) + append(weeks).append(" month ".plural(months, " months ")) } if (weeks > 0) { - append(weeks).append(plural(weeks, " week ", " weeks ")) + append(weeks).append(" week ".plural(weeks, " weeks ")) } if (days > 0) { - append(days).append(plural(days, " day ", " days ")) + append(days).append(" day ".plural(days, " days ")) } if (hours > 0) { - append(hours).append(plural(hours, " hour ", " hours ")) + append(hours).append(" hour ".plural(hours, " hours ")) } - append(minutes).append(plural(minutes, " minute", " minutes")) + append(minutes).append(" minute ".plural(minutes, " minutes")) return toString() } } @@ -282,20 +287,4 @@ object Utils { BufferedReader(InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) .use { reader -> return reader.lines().collect(Collectors.joining(System.lineSeparator())) } } - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun utcDateTime(date: Date): String { - return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())) - } - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun utcDateTime(date: LocalDateTime?): String { - return date?.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) ?: "" - } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 2c02589..7675fe5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import java.util.concurrent.ConcurrentHashMap abstract class AbstractCommand(val bot: Mobibot) { @@ -56,7 +56,7 @@ abstract class AbstractCommand(val bot: Mobibot) { open fun helpResponse(command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { if (!this.isOp || this.isOp == isOp) { for (h in help) { - bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) + bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) } return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index cb635f2..ce4fd32 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -33,7 +33,9 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.links.LinksMgr class Ignore(bot: Mobibot) : AbstractCommand(bot) { @@ -46,17 +48,17 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { override val name = IGNORE_CMD override val help = listOf( "To ignore a link posted to the channel:", - Utils.helpFormat("https://www.foo.bar %n"), + helpFormat("https://www.foo.bar %n"), "To check your ignore status:", - Utils.helpFormat("%c $name"), + helpFormat("%c $name"), "To toggle your ignore status:", - Utils.helpFormat("%c $name $me") + helpFormat("%c $name $me") ) private val helpOp = listOf( "To ignore a link posted to the channel:", - Utils.helpFormat("https://www.foo.bar " + Utils.bold("%n"), false), + helpFormat("https://www.foo.bar " + bold("%n"), false), "To add/remove nicks from the ignored list:", - Utils.helpFormat("%c $name <nick> [<nick> ...]") + helpFormat("%c $name <nick> [<nick> ...]") ) override val isOp = false @@ -98,7 +100,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { ): Boolean { return if (isOp) { for (h in helpOp) { - bot.send(sender, Utils.buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) + bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) } true } else { @@ -143,7 +145,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { @Suppress("MagicNumber") bot.sendList(sender, ignored.sorted(), 8, isPrivate, isIndent = true) } else { - bot.send(sender, "No one is currently ${Utils.bold("ignored")}.", isPrivate) + bot.send(sender, "No one is currently ${bold("ignored")}.", isPrivate) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index d3df42e..7d2fc84 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.ReleaseInfo -import net.thauvin.erik.mobibot.Utils.capitalize +import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.uptime @@ -42,8 +42,8 @@ import java.lang.management.ManagementFactory class Info(bot: Mobibot?) : AbstractCommand(bot!!) { private val allVersions = listOf( - capitalize(ReleaseInfo.PROJECT) + " ${ReleaseInfo.VERSION} (" + green(ReleaseInfo.WEBSITE) + ')', - "Written by ${ReleaseInfo.AUTHOR} (" + green(ReleaseInfo.AUTHOR_URL) + ')' + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${green(ReleaseInfo.WEBSITE)})", + "Written by ${ReleaseInfo.AUTHOR} (${green(ReleaseInfo.AUTHOR_URL)})" ) override val name = "info" override val help = listOf("To view information about the bot:", helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index cbcf5d0..775b9ce 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -33,7 +33,8 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.toUtcDateTime +import net.thauvin.erik.mobibot.Utils.helpFormat import java.time.Clock import java.time.LocalDateTime @@ -41,7 +42,7 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { override val name = "recap" override val help = listOf( "To list the last 10 public channel messages:", - Utils.helpFormat("%c $name") + helpFormat("%c $name") ) override val isOp = false override val isPublic = true @@ -57,7 +58,7 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { @JvmStatic fun storeRecap(sender: String, message: String, isAction: Boolean) { recaps.add( - Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC())) + LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + " - $sender" + (if (isAction) " " else ": ") + message ) @Suppress("MagicNumber") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 57cd79b..c20d77e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -34,11 +34,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.ReleaseInfo import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isoLocalDate +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate class Versions(bot: Mobibot) : AbstractCommand(bot) { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (" + isoLocalDate(ReleaseInfo.BUILDDATE) + ')', + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + " (" + System.getProperty("os.arch") + ')', "Runtime: " + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 2ba9ae5..ce0b797 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -34,7 +34,9 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.entries.EntriesMgr @@ -72,7 +74,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { val history = mutableListOf<String>() @JvmStatic - var startDate: String = Utils.today() + var startDate: String = today() private set /** @@ -88,9 +90,9 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { @JvmStatic fun startup(current: String, backlogs: String, channel: String) { startDate = EntriesMgr.loadEntries(current, channel, entries) - if (Utils.today() != startDate) { + if (today() != startDate) { entries.clear() - startDate = Utils.today() + startDate = today() } EntriesMgr.loadBacklogs(backlogs, history) } @@ -144,7 +146,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { bot.send(sender, "Please specify a title, by typing:", isPrivate) bot.send( sender, - Utils.helpFormat("${EntriesUtils.buildLinkCmd(index)}:|This is the title"), + helpFormat("${EntriesUtils.buildLinkCmd(index)}:|This is the title"), isPrivate ) } @@ -183,7 +185,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { for (i in entries.indices) { if (link == entries[i].link) { val entry: EntryLink = entries[i] - bot.send(sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry), isPrivate) + bot.send(sender, bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry), isPrivate) return true } } @@ -201,10 +203,10 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { } private fun saveDayBackup(bot: Mobibot): Boolean { - if (Utils.today() != startDate) { + if (today() != startDate) { saveEntries(bot, true) entries.clear() - startDate = Utils.today() + startDate = today() return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 9052110..9404287 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -38,7 +38,7 @@ import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.reverseColor -import net.thauvin.erik.mobibot.Utils.utcDateTime +import net.thauvin.erik.mobibot.Utils.toUtcDateTime import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.View @@ -117,21 +117,16 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { */ override val name = "tell" - override val help: List<String> - get() = listOf( + override val help = listOf( "To send a message to someone when they join the channel:", helpFormat("%c $name <nick> <message>"), "To view queued and sent messages:", helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for " + bold(maxDays) - + plural(maxDays, " day.", " days.") + "Messages are kept for ${bold(maxDays)}" + " day.".plural(maxDays, " days.") ) - override val isOp: Boolean - get() = false - override val isPublic: Boolean - get() = isEnabled() - override val isVisible: Boolean - get() = isEnabled() + override val isOp: Boolean = false + override val isPublic: Boolean = isEnabled() + override val isVisible: Boolean = isEnabled() override fun commandResponse( sender: String, @@ -232,8 +227,8 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { && !message.isNotified) { bot.send( nickname, - "Your message ${reverseColor("[ID " + message.id + ']')} was sent to " + - "${bold(message.recipient)} on ${utcDateTime(message.receptionDate)}", + "Your message ${reverseColor("[ID " + message.id + ']')} was sent to " + + "${bold(message.recipient)} on ${message.receptionDate.toUtcDateTime()}", true ) message.isNotified = true @@ -248,9 +243,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { * * @return The size. */ - fun size(): Int { - return messages.size - } + fun size(): Int = messages.size // View all messages. private fun viewAll(sender: String, isPrivate: Boolean) { @@ -281,7 +274,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { bot.send( sender, bold(message.sender) + ARROW + bold(message.recipient) - + " [" + utcDateTime(message.receptionDate) + ", ID: " + + " [${message.receptionDate.toUtcDateTime()}, ID: " + bold(message.id) + ", DELIVERED]", isPrivate ) @@ -289,7 +282,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { bot.send( sender, bold(message.sender) + ARROW + bold(message.recipient) - + " [" + utcDateTime(message.queued) + ", ID: " + + " [${message.queued.toUtcDateTime()}, ID: " + bold(message.id) + ", QUEUED]", isPrivate ) @@ -312,11 +305,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { ), isPrivate ) - bot.send( - sender, - "Messages are kept for ${bold(maxDays)}${plural(maxDays, " day.", " days.")}", - isPrivate - ) + bot.send(sender, help.last(), isPrivate) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 50a8e6a..ca74eae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -40,7 +40,7 @@ import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.SyndFeedOutput import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.isoLocalDate +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import java.io.IOException import java.io.InputStreamReader import java.io.OutputStreamWriter @@ -146,7 +146,7 @@ object EntriesMgr { Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8 ).use { reader -> val feed = input.build(reader) - today = isoLocalDate(feed.publishedDate) + today = feed.publishedDate.toIsoLocalDate() val items = feed.entries var entry: EntryLink for (i in items.indices.reversed()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index acf3669..fc5decf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -33,7 +33,10 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage @@ -56,7 +59,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { isPrivate: Boolean ) { synchronized(this) { - if (pubDate != Utils.today()) { + if (pubDate != today()) { EXCHANGE_RATES.clear() } } @@ -85,7 +88,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { helpResponse(sender, isPrivate) } } else if (args.contains(CURRENCY_RATES_KEYWORD)) { - send(sender, "The currency rates for ${Utils.bold(pubDate)} are:", isPrivate) + send(sender, "The currency rates for ${bold(pubDate)} are:", isPrivate) @Suppress("MagicNumber") sendList(sender, currencyRates(), 3, isPrivate, isIndent = true) } else { @@ -109,16 +112,16 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { send(sender, "To convert from one currency to another:", isPrivate) send( sender, - Utils.helpFormat( - Utils.buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled) + helpFormat( + buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled) ), isPrivate ) send(sender, "For a listing of current rates:", isPrivate) send( sender, - Utils.helpFormat( - Utils.buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) + helpFormat( + buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) ), isPrivate ) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index eeff387..80dc091 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -32,7 +32,11 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.urlReader +import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage import org.jibble.pircbot.Colors @@ -86,21 +90,21 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message> { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { - throw ModuleException("${Utils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.") + throw ModuleException("${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing.") } return if (query.isNotBlank()) { val results = mutableListOf<Message>() try { val url = URL( "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - "&q=${Utils.encodeUrl(query)}&filter=1&num=5&alt=json" + "&q=${encodeUrl(query)}&filter=1&num=5&alt=json" ) - val json = JSONObject(Utils.urlReader(url)) + val json = JSONObject(urlReader(url)) val ja = json.getJSONArray("items") for (i in 0 until ja.length()) { val j = ja.getJSONObject(i) - results.add(NoticeMessage(Utils.unescapeXml(j.getString("title")))) - results.add(NoticeMessage(Utils.helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) + results.add(NoticeMessage(unescapeXml(j.getString("title")))) + results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) } } catch (e: IOException) { throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e) @@ -117,7 +121,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(GOOGLE_CMD) help.add("To search Google:") - help.add(Utils.helpFormat("%c $GOOGLE_CMD <query>")) + help.add(helpFormat("%c $GOOGLE_CMD <query>")) initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 8ac1786..f2624ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -32,7 +32,9 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.cyan +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import org.json.JSONException @@ -58,7 +60,7 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { try { - bot.send(Utils.cyan(randomJoke().msg)) + bot.send(cyan(randomJoke().msg)) } catch (e: ModuleException) { if (bot.logger.isWarnEnabled) bot.logger.warn(e.debugMessage, e) bot.send(sender, e.message, isPrivate) @@ -81,7 +83,7 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { fun randomJoke(): Message { return try { val url = URL(JOKE_URL) - val json = JSONObject(Utils.urlReader(url)) + val json = JSONObject(urlReader(url)) PublicMessage( json.getJSONObject("value")["joke"].toString().replace("\\'", "'") .replace("\\\"", "\"") @@ -97,6 +99,6 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(JOKE_CMD) help.add("To retrieve a random joke:") - help.add(Utils.helpFormat("%c $JOKE_CMD")) + help.add(helpFormat("%c $JOKE_CMD")) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index c970aca..48a0462 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -68,7 +68,7 @@ class ModuleException : Exception { * Return the sanitized message (e.g. remove API keys, etc.) */ fun getSanitizedMessage(vararg sanitize: String): String { - val obfuscate = sanitize.map { obfuscate(it) }.toTypedArray() + val obfuscate = sanitize.map { it.obfuscate() }.toTypedArray() return when { cause != null -> { cause.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index cb8a825..6f7528b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -33,7 +33,10 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.red import kotlin.random.Random @@ -51,7 +54,7 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { with(help) { add("To play Rock Paper Scissors:") add( - Utils.helpFormat( + helpFormat( "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + " | ${Hands.SCISSORS.name.lowercase()}" ) @@ -99,15 +102,15 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { with(bot) { when { hand == botHand -> { - send("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)}") + send("${green(hand.name)} vs. ${green(botHand.name)}") action("tied.") } hand.beats(botHand) -> { - send("${Utils.green(hand.name)} ${Utils.bold(hand.action)} ${Utils.red(botHand.name)}") + send("${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)}") action("lost.") } else -> { - send("${Utils.green(botHand.name)} ${Utils.bold(botHand.action)} ${Utils.red(hand.name)}") + send("${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)}") action("wins.") } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 60c5f0f..46423c3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -32,7 +32,11 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage @@ -91,7 +95,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { try { val info = json.getString("Information") if (info.isNotEmpty()) { - throw ModuleException(debugMessage, Utils.unescapeXml(info)) + throw ModuleException(debugMessage, unescapeXml(info)) } } catch (ignore: JSONException) { // Do nothing @@ -99,11 +103,11 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { try { var error = json.getString("Note") if (error.isNotEmpty()) { - throw ModuleException(debugMessage, Utils.unescapeXml(error)) + throw ModuleException(debugMessage, unescapeXml(error)) } error = json.getString("Error Message") if (error.isNotEmpty()) { - throw ModuleException(debugMessage, Utils.unescapeXml(error)) + throw ModuleException(debugMessage, unescapeXml(error)) } } catch (ignore: JSONException) { // Do nothing @@ -122,7 +126,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Utils.capitalize(STOCK_CMD)} is disabled. The API key is missing." + "${STOCK_CMD.capitalise()} is disabled. The API key is missing." ) } return if (symbol.isNotBlank()) { @@ -132,10 +136,10 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { try { with(messages) { // Search for symbol/keywords - response = Utils.urlReader( + response = urlReader( URL( - "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) - + "&apikey=" + Utils.encodeUrl(apiKey) + "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + encodeUrl(symbol) + + "&apikey=" + encodeUrl(apiKey) ) ) var json = getJsonResponse(response, debugMessage) @@ -146,11 +150,11 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { val symbolInfo = symbols.getJSONObject(0) // Get quote for symbol - response = Utils.urlReader( + response = urlReader( URL( "${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" - + Utils.encodeUrl(symbolInfo.getString("1. symbol")) - + "&apikey=" + Utils.encodeUrl(apiKey) + + encodeUrl(symbolInfo.getString("1. symbol")) + + "&apikey=" + encodeUrl(apiKey) ) ) json = getJsonResponse(response, debugMessage) @@ -161,34 +165,28 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { } add( PublicMessage( - "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) - + " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']' + "Symbol: " + unescapeXml(quote.getString("01. symbol")) + + " [" + unescapeXml(symbolInfo.getString("2. name")) + ']' ) ) - add(PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price")))) + add(PublicMessage(" Price: " + unescapeXml(quote.getString("05. price")))) add( PublicMessage( - " Previous: " + Utils.unescapeXml(quote.getString("08. previous close")) + " Previous: " + unescapeXml(quote.getString("08. previous close")) ) ) - add(NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open")))) - add(NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high")))) - add(NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low")))) - add( - NoticeMessage( - " Volume: " + Utils.unescapeXml(quote.getString("06. volume")) + add(NoticeMessage(" Open: " + unescapeXml(quote.getString("02. open")))) + add(NoticeMessage(" High: " + unescapeXml(quote.getString("03. high")))) + add(NoticeMessage(" Low: " + unescapeXml(quote.getString("04. low")))) + add(NoticeMessage(" Volume: " + unescapeXml(quote.getString("06. volume")))) + add(NoticeMessage( + " Latest: " + unescapeXml(quote.getString("07. latest trading day")) ) ) add( NoticeMessage( - " Latest: " - + Utils.unescapeXml(quote.getString("07. latest trading day")) - ) - ) - add( - NoticeMessage( - " Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" - + Utils.unescapeXml(quote.getString("10. change percent")) + ']' + " Change: " + unescapeXml(quote.getString("09. change")) + " [" + + unescapeXml(quote.getString("10. change percent")) + ']' ) ) } @@ -208,7 +206,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(STOCK_CMD) help.add("To retrieve a stock quote:") - help.add(Utils.helpFormat("%c $STOCK_CMD <symbol|keywords>")) + help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>")) initProperties(ALPHAVANTAGE_API_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index ae49759..77631cd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -36,7 +36,10 @@ import net.aksingh.owmjapis.core.OWM import net.aksingh.owmjapis.core.OWM.Country import net.aksingh.owmjapis.model.CurrentWeather import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage @@ -102,7 +105,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Utils.capitalize(WEATHER_CMD)} is disabled. The API key is missing." + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." ) } val owm = OWM(apiKey) @@ -151,7 +154,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { for (w in list) { if (w != null) { condition.append(' ') - .append(Utils.capitalize(w.getDescription())) + .append(w.getDescription().capitalise()) .append('.') } } @@ -167,7 +170,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { messages.add( NoticeMessage( "https://openweathermap.org/find?q=" - + Utils.encodeUrl("$city,${country.uppercase()}"), + + encodeUrl("$city,${country.uppercase()}"), Colors.GREEN ) ) @@ -197,10 +200,10 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(WEATHER_CMD) help.add("To display weather information:") - help.add(Utils.helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) + help.add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) help.add("For example:") - help.add(Utils.helpFormat("%c $WEATHER_CMD paris, fr")) - help.add("The default ISO 3166 country code is ${Utils.bold("US")}. Zip codes supported in most countries.") + help.add(helpFormat("%c $WEATHER_CMD paris, fr")) + help.add("The default ISO 3166 country code is ${bold("US")}. Zip codes supported in most countries.") initProperties(OWM_API_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 78c91ec..7c769e9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -77,7 +77,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).uppercase()] val response: String = if (tz != null) { if (BEATS_KEYWORD == tz) { - "The current Internet Time is: " + bold(internetTime() + ' ' + BEATS_KEYWORD) + "The current Internet Time is: ${bold(internetTime())} $BEATS_KEYWORD" } else { (ZonedDateTime.now() .withZoneSameInstant(ZoneId.of(tz)) @@ -215,7 +215,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { init { help.add("To display a country's current date/time:") - help.add(helpFormat("%c $TIME_CMD") + " [<country code>]") + help.add(helpFormat("%c $TIME_CMD [<country code>]")) help.add("For a listing of the supported countries:") help.add(helpFormat("%c $TIME_CMD")) commands.add(TIME_CMD) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 8b75b72..a28aee5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -35,18 +35,18 @@ import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.capitalize import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.cyan -import net.thauvin.erik.mobibot.Utils.ensureDir import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.green -import net.thauvin.erik.mobibot.Utils.isoLocalDate import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.toDir +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.Utils.toUtcDateTime import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.Utils.urlReader -import net.thauvin.erik.mobibot.Utils.utcDateTime import org.apache.commons.lang3.StringUtils import org.assertj.core.api.Assertions.assertThat import org.jibble.pircbot.Colors @@ -101,13 +101,6 @@ class UtilsTest { assertThat(cyan(ascii)).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) } - @Test - fun testEnsureDir() { - assertThat(ensureDir("dir", false)).describedAs("ensureDir(dir, false)").isEqualTo("dir" + File.separatorChar) - assertThat(ensureDir("https://erik.thauvin.net", true)).describedAs("ensureDir(erik.thauvin.net, true)") - .isEqualTo("https://erik.thauvin.net/") - } - @Test fun testGetIntProperty() { assertThat(getIntProperty("10", 1)).describedAs("getIntProperty(10, 1)").isEqualTo(10) @@ -121,25 +114,25 @@ class UtilsTest { @Test fun testIsoLocalDate() { - assertThat(isoLocalDate(cal.time)).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17") - assertThat(isoLocalDate(localDateTime)).describedAs("isoLocalDate(localDate)").isEqualTo("1952-02-17") + assertThat(cal.time.toIsoLocalDate()).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17") + assertThat(localDateTime.toIsoLocalDate()).describedAs("isoLocalDate(localDate)").isEqualTo("1952-02-17") } @Test fun testObfuscate() { - assertThat(obfuscate(ascii).length).describedAs("obfuscate is right length").isEqualTo(ascii.length) - assertThat(obfuscate(ascii)).describedAs("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) - assertThat(obfuscate(" ")).describedAs("obfuscate(blank)").isEqualTo(" ") + assertThat(ascii.obfuscate().length).describedAs("obfuscate is right length").isEqualTo(ascii.length) + assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) + assertThat(" ".obfuscate()).describedAs("obfuscate(blank)").isEqualTo(" ") } @Test fun testPlural() { val week = "week" val weeks = "weeks" - assertThat(plural(-1, week, weeks)).describedAs("plural(-1)").isEqualTo(week) - assertThat(plural(0, week, weeks)).describedAs("plural(0)").isEqualTo(week) - assertThat(plural(1, week, weeks)).describedAs("plural(1)").isEqualTo(week) - assertThat(plural(2, week, weeks)).describedAs("plural(2)").isEqualTo(weeks) + assertThat(week.plural(-1, weeks)).describedAs("plural(-1)").isEqualTo(week) + assertThat(week.plural(0, weeks)).describedAs("plural(0)").isEqualTo(week) + assertThat(week.plural(1, weeks)).describedAs("plural(1)").isEqualTo(week) + assertThat(week.plural(2, weeks)).describedAs("plural(2)").isEqualTo(weeks) } @Test @@ -149,7 +142,14 @@ class UtilsTest { @Test fun testToday() { - assertThat(today()).isEqualTo(isoLocalDate(LocalDateTime.now())) + assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate()) + } + + @Test + fun testToDir() { + assertThat("dir".toDir(false)).describedAs("toDir(dir, false)").isEqualTo("dir" + File.separatorChar) + assertThat("https://erik.thauvin.net".toDir(true)).describedAs("toDir(erik.thauvin.net, true)") + .isEqualTo("https://erik.thauvin.net/") } @Test @@ -174,7 +174,7 @@ class UtilsTest { @Test fun testUtcDateTime() { - assertThat(utcDateTime(cal.time)).describedAs("utcDateTime(date)").isEqualTo("1952-02-17 12:30") - assertThat(utcDateTime(localDateTime)).describedAs("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") + assertThat(cal.time.toUtcDateTime()).describedAs("utcDateTime(date)").isEqualTo("1952-02-17 12:30") + assertThat(localDateTime.toUtcDateTime()).describedAs("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 4064672..8a78beb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -44,10 +44,10 @@ import org.testng.annotations.Test class CalcTest { @Test fun testCalculate() { - assertThat(calculate("1 + 1")).describedAs("calculate(1+1)").isEqualTo("1+1 = %s", Utils.bold(2)) - assertThat(calculate("1 -3")).describedAs("calculate(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2)) + assertThat(calculate("1 + 1")).describedAs("calculate(1+1)").isEqualTo("1+1 = %s", bold(2)) + assertThat(calculate("1 -3")).describedAs("calculate(1 -3)").isEqualTo("1-3 = %s", bold(-2)) assertThat(calculate("pi+π+e+φ")).describedAs("calculate(pi+π+e+φ)") - .isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62")) + .isEqualTo("pi+π+e+φ = %s", bold("10.62")) assertThatThrownBy { calculate("one + one") }.describedAs("calculate(one+one)") .isInstanceOf(UnknownFunctionOrVariableException::class.java) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index d0c3e00..73a2007 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test @@ -42,7 +42,7 @@ import org.testng.annotations.Test class WordTimeTest { @Test fun testTime() { - assertThat(time("PST").msg).describedAs("PST").endsWith(Utils.bold("Los Angeles")) + assertThat(time("PST").msg).describedAs("PST").endsWith(bold("Los Angeles")) assertThat(time("BLAH").isError).describedAs("BLAH").isTrue assertThat(time("BEATS").msg).describedAs("BEATS").contains("@") } diff --git a/version.properties b/version.properties index 3846e61..88ee6f9 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu May 13 21:34:31 PDT 2021 -version.buildmeta=771 +#Fri May 14 20:13:13 PDT 2021 +version.buildmeta=782 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+771 +version.semver=0.8.0-beta+782 From 8098a4855d0701c7e236c3edb0748f1b2fb8cbc7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 14 May 2021 21:46:23 -0700 Subject: [PATCH 511/842] Cleanup. --- .../net/thauvin/erik/mobibot/modules/War.java | 1 - .../kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 4 ++-- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 6 +++--- .../net/thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../thauvin/erik/mobibot/commands/tell/Tell.kt | 17 +++++++++-------- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../thauvin/erik/mobibot/modules/StockQuote.kt | 3 ++- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 16 ++++++++-------- version.properties | 6 +++--- 10 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 7795e33..ddd56a2 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -44,7 +44,6 @@ import static net.thauvin.erik.mobibot.Utils.bold; * The War module. * * @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a> - * @created 2014-04-28 * @since 1.0 */ public final class War extends AbstractModule { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 2bb5d56..af81bc8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -38,9 +38,9 @@ import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.today -import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.toDir +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AddLog import net.thauvin.erik.mobibot.commands.ChannelFeed import net.thauvin.erik.mobibot.commands.Cycle diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index c5b4919..3a07ac3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -156,7 +156,7 @@ object Utils { * Returns the plural form of a word, if count > 1. */ fun String.plural(count: Int, plural: String): String = this.plural(count.toLong(), plural) - + /** * Returns the plural form of a word, if count > 1. */ @@ -187,7 +187,7 @@ object Utils { * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. */ @JvmStatic - fun String.toDir(isUrl: Boolean = false) : String { + fun String.toDir(isUrl: Boolean = false): String { return if (isUrl) { if (this.last() == '/') { this @@ -229,7 +229,7 @@ object Utils { * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. */ @JvmStatic - fun LocalDateTime.toUtcDateTime(): String = this.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + fun LocalDateTime.toUtcDateTime(): String = this.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) /** * Converts XML/XHTML entities to plain text. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 775b9ce..2f21dc6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -33,8 +33,8 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.toUtcDateTime import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.toUtcDateTime import java.time.Clock import java.time.LocalDateTime diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 9404287..72fd262 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -117,13 +117,13 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { */ override val name = "tell" - override val help = listOf( - "To send a message to someone when they join the channel:", - helpFormat("%c $name <nick> <message>"), - "To view queued and sent messages:", - helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${bold(maxDays)}" + " day.".plural(maxDays, " days.") - ) + override val help = listOf( + "To send a message to someone when they join the channel:", + helpFormat("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpFormat("%c $name ${View.VIEW_CMD}"), + "Messages are kept for ${bold(maxDays)}" + " day.".plural(maxDays, " days.") + ) override val isOp: Boolean = false override val isPublic: Boolean = isEnabled() override val isVisible: Boolean = isEnabled() @@ -224,7 +224,8 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { save() } } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified) { + && !message.isNotified + ) { bot.send( nickname, "Your message ${reverseColor("[ID " + message.id + ']')} was sent to " diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 80dc091..8184881 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -35,8 +35,8 @@ import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage import org.jibble.pircbot.Colors diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 46423c3..4b4bec1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -179,7 +179,8 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { add(NoticeMessage(" High: " + unescapeXml(quote.getString("03. high")))) add(NoticeMessage(" Low: " + unescapeXml(quote.getString("04. low")))) add(NoticeMessage(" Volume: " + unescapeXml(quote.getString("06. volume")))) - add(NoticeMessage( + add( + NoticeMessage( " Latest: " + unescapeXml(quote.getString("07. latest trading day")) ) ) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 77631cd..238952b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -36,8 +36,8 @@ import net.aksingh.owmjapis.core.OWM import net.aksingh.owmjapis.core.OWM.Country import net.aksingh.owmjapis.model.CurrentWeather import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.msg.ErrorMessage diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index a28aee5..0a77475 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.capitalize +import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.getIntProperty @@ -79,11 +79,10 @@ class UtilsTest { } @Test - fun testCapitalize() { - assertThat(capitalize("test")).describedAs("capitalize(test)").isEqualTo("Test") - assertThat(capitalize("Test")).describedAs("capitalize(Test)").isEqualTo("Test") - assertThat(capitalize(null)).describedAs("captitalize(null)").isNull() - assertThat(capitalize("")).describedAs("capitalize()").isEqualTo("") + fun testCapitalise() { + assertThat("test".capitalise()).describedAs("capitalize(test)").isEqualTo("Test") + assertThat("Test".capitalise()).describedAs("capitalize(Test)").isEqualTo("Test") + assertThat("".capitalise()).describedAs("capitalize()").isEqualTo("") } @Test @@ -168,8 +167,9 @@ class UtilsTest { @Throws(IOException::class) fun testUrlReader() { assertThat(urlReader(URL("https://postman-echo.com/status/200"))).describedAs("urlReader()") - .isEqualTo("{\"status\":200}" - ) + .isEqualTo( + "{\"status\":200}" + ) } @Test diff --git a/version.properties b/version.properties index 88ee6f9..5c5888f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri May 14 20:13:13 PDT 2021 -version.buildmeta=782 +#Fri May 14 21:44:23 PDT 2021 +version.buildmeta=787 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+782 +version.semver=0.8.0-beta+787 From 2de9dc66bd906fae0765dea970ffd04f5c33a0ee Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 15 May 2021 01:26:31 -0700 Subject: [PATCH 512/842] Added more Utils extensions. --- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 21 +++++++++++++------ .../erik/mobibot/commands/tell/Tell.kt | 6 +++--- .../net/thauvin/erik/mobibot/UtilsTest.kt | 15 +++++++++++-- version.properties | 6 +++--- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index af81bc8..c6390f0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -616,7 +616,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert name = nickname ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) - ircPort = getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT) + ircPort = p.getIntProperty("port", Constants.DEFAULT_PORT) this.channel = channel logsDir = logsDirPath diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 3a07ac3..f55ff61 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -45,6 +45,7 @@ import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter import java.util.Date +import java.util.Properties import java.util.concurrent.TimeUnit import java.util.stream.Collectors @@ -119,12 +120,8 @@ object Utils { * Returns a property as an int. */ @JvmStatic - fun getIntProperty(property: String?, def: Int): Int { - return try { - property?.toInt() ?: def - } catch (nfe: NumberFormatException) { - def - } + fun Properties.getIntProperty(key: String, defaultValue: Int): Int { + return this.getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue } /** @@ -203,6 +200,18 @@ object Utils { } } + /** + * Converts a string to an int. + */ + @JvmStatic + fun String.toIntOrDefault(defaultValue: Int): Int { + return try { + this.toInt() + } catch (e: NumberFormatException) { + defaultValue + } + } + /** * Returns the specified date as an ISO local date string. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 72fd262..cd59eac 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.commands.tell import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.buildCmdSyntax -import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.toIntOrDefault import net.thauvin.erik.mobibot.Utils.toUtcDateTime import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.View @@ -162,9 +162,9 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { override fun setProperty(key: String, value: String) { super.setProperty(key, value) if (MAX_DAYS_PROP == key) { - maxDays = getIntProperty(value, maxDays) + maxDays = value.toIntOrDefault(maxDays) } else if (MAX_SIZE_PROP == key) { - maxSize = getIntProperty(value, maxSize) + maxSize = value.toIntOrDefault(maxSize) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 0a77475..2dd8dd5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -41,6 +41,7 @@ import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.reverseColor import net.thauvin.erik.mobibot.Utils.toDir +import net.thauvin.erik.mobibot.Utils.toIntOrDefault import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.toUtcDateTime import net.thauvin.erik.mobibot.Utils.today @@ -57,6 +58,7 @@ import java.io.IOException import java.net.URL import java.time.LocalDateTime import java.util.Calendar +import java.util.Properties /** * The `Utils Test` class. @@ -102,8 +104,11 @@ class UtilsTest { @Test fun testGetIntProperty() { - assertThat(getIntProperty("10", 1)).describedAs("getIntProperty(10, 1)").isEqualTo(10) - assertThat(getIntProperty("a", 1)).describedAs("getIntProperty(a, 1)").isEqualTo(1) + val p = Properties() + p["one"] = "1" + p["two"] = "foo" + assertThat(p.getIntProperty("one", 1)).describedAs("getIntProperty(one)").isEqualTo(1) + assertThat(p.getIntProperty("two", 2)).describedAs("getIntProperty(two)").isEqualTo(2) } @Test @@ -151,6 +156,12 @@ class UtilsTest { .isEqualTo("https://erik.thauvin.net/") } + @Test + fun testToIntOrDefault() { + assertThat("10".toIntOrDefault(1)).describedAs("toIntOrDefault(10, 1)").isEqualTo(10) + assertThat("a".toIntOrDefault(2)).describedAs("toIntOrDefault(a, 2)").isEqualTo(2) + } + @Test fun testUnescapeXml() { assertThat(unescapeXml("<a name="test & ''">")).isEqualTo( diff --git a/version.properties b/version.properties index 5c5888f..a15c017 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri May 14 21:44:23 PDT 2021 -version.buildmeta=787 +#Sat May 15 01:24:55 PDT 2021 +version.buildmeta=791 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+787 +version.semver=0.8.0-beta+791 From 81cee7b00f7264f11cd20078c5392ce3df400ef5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 15 May 2021 12:01:00 -0700 Subject: [PATCH 513/842] Truncate UTC timestamp to seconds. --- .../net/thauvin/erik/mobibot/PinboardUtils.kt | 29 ++++++++++--------- .../thauvin/erik/mobibot/PinboardUtilsTest.kt | 17 ++++++++--- version.properties | 6 ++-- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt index 8c388f9..c4d3a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -39,6 +39,7 @@ import net.thauvin.erik.pinboard.PinboardPoster import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit import java.util.Date /** @@ -50,16 +51,15 @@ object PinboardUtils { */ @JvmStatic fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking { - val add = async { + async { poster.addPin( entry.link, entry.title, postedBy(entry, ircServer), entry.pinboardTags, - formatDate(entry.date) + entry.date.toTimestamp() ) - } - add.await() + }.await() } /** @@ -67,10 +67,9 @@ object PinboardUtils { */ @JvmStatic fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking { - val delete = async { + async { poster.deletePin(entry.link) - } - delete.await() + }.await() } /** @@ -78,7 +77,7 @@ object PinboardUtils { */ @JvmStatic fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { - val update = async { + async { with(entry) { if (oldUrl != link) { poster.deletePin(oldUrl) @@ -87,7 +86,7 @@ object PinboardUtils { title, postedBy(entry, ircServer), pinboardTags, - formatDate(date) + date.toTimestamp() ) } else { poster.addPin( @@ -95,21 +94,23 @@ object PinboardUtils { title, postedBy(entry, ircServer), pinboardTags, - formatDate(date), + date.toTimestamp(), replace = true, shared = true ) } } - } - update.await() + }.await() } /** * Format a date to a UTC timestamp. */ - private fun formatDate(date: Date): String { - return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT) + @JvmStatic + fun Date.toTimestamp(): String { + return ZonedDateTime.ofInstant( + this.toInstant().truncatedTo(ChronoUnit.SECONDS), + ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT) } /** diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index d6bb8f4..dd698e4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -33,9 +33,12 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.PinboardUtils.toTimestamp import net.thauvin.erik.pinboard.PinboardPoster -import org.testng.Assert +import org.testng.Assert.assertFalse +import org.testng.Assert.assertTrue import org.testng.annotations.Test +import java.util.Date import java.net.URL class PinboardUtilsTest : LocalProperties() { @@ -48,14 +51,20 @@ class PinboardUtilsTest : LocalProperties() { val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) PinboardUtils.addPin(pinboard, ircServer, entry) - Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "addPin") + assertTrue(validatePin(apiToken, ircServer, entry.link), "addPin") entry.link = "https://www.foo.com/" PinboardUtils.updatePin(pinboard, ircServer, url, entry) - Assert.assertTrue(validatePin(apiToken, ircServer, entry.link), "updatePin") + assertTrue(validatePin(apiToken, ircServer, entry.link), "updatePin") PinboardUtils.deletePin(pinboard, entry) - Assert.assertFalse(validatePin(apiToken, url = entry.link), "deletePin") + assertFalse(validatePin(apiToken, url = entry.link), "deletePin") + } + + @Test + fun toTimestampTest() { + val d = Date() + assertTrue(d.toTimestamp().matches("[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z".toRegex())) } private fun validatePin(apiToken: String, ircServer: String = "", url: String): Boolean { diff --git a/version.properties b/version.properties index a15c017..b7bbbbd 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat May 15 01:24:55 PDT 2021 -version.buildmeta=791 +#Sat May 15 12:48:51 PDT 2021 +version.buildmeta=803 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+791 +version.semver=0.8.0-beta+803 From 8681dea66f47f2d9c0a52d411c4a9ab09b0c857e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 16 May 2021 07:58:06 -0700 Subject: [PATCH 514/842] Updated to Gradle 7.0.1 --- build.gradle | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 871deaa..7bff7c0 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'checkstyle' id 'com.github.ben-manes.versions' version '0.38.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.17.0-RC1' + id 'io.gitlab.arturbosch.detekt' version '1.17.0' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -38,7 +38,7 @@ dependencies { compileOnly 'pircbot:pircbot:1.5.0:sources' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" @@ -112,7 +112,7 @@ pmd { } detekt { - toolVersion = "main-SNAPSHOT" + //toolVersion = "main-SNAPSHOT" baseline = file("${projectDir}/config/detekt/baseline.xml") } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e5338d3..0f80bbf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 082b430ee2a64305b74cdfdd7bdab09ae413094e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 16 May 2021 08:17:16 -0700 Subject: [PATCH 515/842] Cleanup. --- .idea/checkstyle-idea.xml | 16 - .idea/codeStyles/Project.xml | 344 ------------------ .idea/inspectionProfiles/Project_Default.xml | 36 -- .../inspectionProfiles/profiles_settings.xml | 7 - .idea/jarRepositories.xml | 21 +- .idea/misc.xml | 6 - .idea/modules/mobibot.iml | 56 --- .idea/vcs.xml | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 8 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 4 +- .../net/thauvin/erik/mobibot/PinboardUtils.kt | 30 +- .../thauvin/erik/mobibot/commands/Recap.kt | 4 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/entries/EntriesMgr.kt | 7 +- .../erik/mobibot/entries/EntryComment.kt | 4 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 6 +- .../erik/mobibot/modules/CurrencyConverter.kt | 11 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 4 +- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 4 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 19 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 10 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 17 +- .../thauvin/erik/mobibot/PinboardUtilsTest.kt | 6 +- 25 files changed, 68 insertions(+), 560 deletions(-) delete mode 100644 .idea/checkstyle-idea.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/modules/mobibot.iml diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml deleted file mode 100644 index 21a0f73..0000000 --- a/.idea/checkstyle-idea.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CheckStyle-IDEA"> - <option name="configuration"> - <map> - <entry key="checkstyle-version" value="8.29" /> - <entry key="copy-libs" value="true" /> - <entry key="location-0" value="BUNDLED:(bundled):Sun Checks" /> - <entry key="location-1" value="BUNDLED:(bundled):Google Checks" /> - <entry key="scan-before-checkin" value="false" /> - <entry key="scanscope" value="JavaOnly" /> - <entry key="suppress-errors" value="false" /> - </map> - </option> - </component> -</project> \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 321b376..e310056 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,18 +1,5 @@ <component name="ProjectCodeStyleConfiguration"> <code_scheme name="Project" version="173"> - <JavaCodeStyleSettings> - <option name="ANNOTATION_PARAMETER_WRAP" value="2" /> - <option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" /> - <option name="ALIGN_MULTILINE_TEXT_BLOCKS" value="true" /> - <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> - <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> - <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND"> - <value> - <package name="java.awt" withSubpackages="false" static="false" /> - </value> - </option> - <option name="JD_INDENT_ON_CONTINUATION" value="true" /> - </JavaCodeStyleSettings> <JetCodeStyleSettings> <option name="PACKAGES_TO_USE_STAR_IMPORTS"> <value /> @@ -21,335 +8,7 @@ <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </JetCodeStyleSettings> - <codeStyleSettings language="JAVA"> - <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" /> - <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" /> - <option name="LINE_COMMENT_ADD_SPACE" value="true" /> - <option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" /> - <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> - <option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" /> - <option name="ALIGN_MULTILINE_ASSIGNMENT" value="true" /> - <option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" /> - <option name="ALIGN_MULTILINE_THROWS_LIST" value="true" /> - <option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" /> - <option name="ALIGN_MULTILINE_METHOD_BRACKETS" value="true" /> - <option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" /> - <option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" /> - <option name="CALL_PARAMETERS_WRAP" value="5" /> - <option name="METHOD_PARAMETERS_WRAP" value="5" /> - <option name="RESOURCE_LIST_WRAP" value="1" /> - <option name="EXTENDS_LIST_WRAP" value="1" /> - <option name="THROWS_LIST_WRAP" value="1" /> - <option name="EXTENDS_KEYWORD_WRAP" value="1" /> - <option name="THROWS_KEYWORD_WRAP" value="1" /> - <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> - <option name="BINARY_OPERATION_WRAP" value="1" /> - <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" /> - <option name="TERNARY_OPERATION_WRAP" value="1" /> - <option name="FOR_STATEMENT_WRAP" value="1" /> - <option name="ARRAY_INITIALIZER_WRAP" value="1" /> - <option name="ASSIGNMENT_WRAP" value="1" /> - <option name="ASSERT_STATEMENT_WRAP" value="1" /> - <option name="IF_BRACE_FORCE" value="3" /> - <option name="DOWHILE_BRACE_FORCE" value="1" /> - <option name="WHILE_BRACE_FORCE" value="1" /> - <option name="FOR_BRACE_FORCE" value="1" /> - <option name="WRAP_LONG_LINES" value="true" /> - <option name="VARIABLE_ANNOTATION_WRAP" value="2" /> - <option name="ENUM_CONSTANTS_WRAP" value="1" /> - <indentOptions> - <option name="USE_RELATIVE_INDENTS" value="true" /> - </indentOptions> - <arrangement> - <groups> - <group> - <type>OVERRIDDEN_METHODS</type> - <order>BY_NAME</order> - </group> - </groups> - <rules> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PUBLIC>true</PUBLIC> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PROTECTED>true</PROTECTED> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PRIVATE>true</PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PUBLIC>true</PUBLIC> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PROTECTED>true</PROTECTED> - <STATIC>true</STATIC> - <visibility /> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>true</PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> - <STATIC>true</STATIC> - </AND> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PUBLIC>true</PUBLIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PROTECTED>true</PROTECTED> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PRIVATE>true</PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PUBLIC>true</PUBLIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PROTECTED>true</PROTECTED> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>true</PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <FIELD>true</FIELD> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> - </match> - </rule> - </section> - <section> - <rule> - <match> - <CONSTRUCTOR>true</CONSTRUCTOR> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <METHOD>true</METHOD> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <METHOD>true</METHOD> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <ENUM>true</ENUM> - </match> - </rule> - </section> - <section> - <rule> - <match> - <INTERFACE>true</INTERFACE> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <CLASS>true</CLASS> - <STATIC>true</STATIC> - </AND> - </match> - </rule> - </section> - <section> - <rule> - <match> - <CLASS>true</CLASS> - </match> - </rule> - </section> - </rules> - </arrangement> - </codeStyleSettings> <codeStyleSettings language="kotlin"> - <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" /> - <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> - <option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" /> <option name="CALL_PARAMETERS_WRAP" value="5" /> <option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> <option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> @@ -359,9 +18,6 @@ <option name="EXTENDS_LIST_WRAP" value="1" /> <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> <option name="ASSIGNMENT_WRAP" value="1" /> - <indentOptions> - <option name="CONTINUATION_INDENT_SIZE" value="4" /> - </indentOptions> </codeStyleSettings> </code_scheme> </component> \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 838047c..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,36 +0,0 @@ -<component name="InspectionProjectProfileManager"> - <profile version="1.0"> - <option name="myName" value="Project Default" /> - <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="TOP_LEVEL_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="INNER_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="METHOD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> - </value> - </option> - <option name="FIELD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="IGNORE_DEPRECATED" value="false" /> - <option name="IGNORE_JAVADOC_PERIOD" value="true" /> - <option name="IGNORE_DUPLICATED_THROWS" value="false" /> - <option name="IGNORE_POINT_TO_ITSELF" value="false" /> - <option name="myAdditionalJavadocTags" value="created" /> - </inspection_tool> - </profile> -</component> \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index dc2dcae..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,7 +0,0 @@ -<component name="InspectionProjectProfileManager"> - <settings> - <option name="PROJECT_PROFILE" value="Erik's Default" /> - <option name="USE_PROJECT_PROFILE" value="false" /> - <version value="1.0" /> - </settings> -</component> \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index a3c8d4d..39b5ad2 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -11,30 +11,15 @@ <option name="name" value="JBoss Community repository" /> <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> </remote-repository> - <remote-repository> - <option name="id" value="MavenRepo" /> - <option name="name" value="MavenRepo" /> - <option name="url" value="https://repo.maven.apache.org/maven2/" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:/$MAVEN_REPOSITORY$/" /> - </remote-repository> - <remote-repository> - <option name="id" value="BintrayJCenter" /> - <option name="name" value="BintrayJCenter" /> - <option name="url" value="https://jcenter.bintray.com/" /> - </remote-repository> <remote-repository> <option name="id" value="MavenLocal" /> <option name="name" value="MavenLocal" /> <option name="url" value="file:$MAVEN_REPOSITORY$" /> </remote-repository> <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$/" /> + <option name="id" value="MavenRepo" /> + <option name="name" value="MavenRepo" /> + <option name="url" value="https://repo.maven.apache.org/maven2/" /> </remote-repository> <remote-repository> <option name="id" value="maven" /> diff --git a/.idea/misc.xml b/.idea/misc.xml index fb38c4a..0474152 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,11 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="FrameworkDetectionExcludesConfiguration"> - <file type="web" url="file://$PROJECT_DIR$" /> - </component> - <component name="JavaScriptSettings"> - <option name="languageLevel" value="ES6" /> - </component> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="15" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/modules/mobibot.iml b/.idea/modules/mobibot.iml deleted file mode 100644 index 9de0d06..0000000 --- a/.idea/modules/mobibot.iml +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+547" type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager"> - <output url="file://$MODULE_DIR$/../../out/production/classes" /> - <output-test url="file://$MODULE_DIR$/../../out/test/classes" /> - <exclude-output /> - <content url="file://$MODULE_DIR$/../.."> - <sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" generated="true" /> - <excludeFolder url="file://$MODULE_DIR$/../../.gradle" /> - <excludeFolder url="file://$MODULE_DIR$/../../build" /> - <excludeFolder url="file://$MODULE_DIR$/../../kobaltBuild" /> - <excludeFolder url="file://$MODULE_DIR$/../../out" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.12.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.12.1" level="project" /> - <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" level="project" /> - <orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" /> - <orderEntry type="library" name="Gradle: commons-net:commons-net:3.6" level="project" /> - <orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" /> - <orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.2.0" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" /> - <orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" /> - <orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.1" level="project" /> - <orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" /> - <orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta4" level="project" /> - <orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" /> - <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> - <orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.2.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.50" level="project" /> - <orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" /> - <orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50" level="project" /> - <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" /> - <orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:7.0.0" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.13.2" level="project" /> - <orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" /> - </component> -</module> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="VcsDirectoryMappings"> - <mapping directory="$PROJECT_DIR$" vcs="Git" /> + <mapping directory="" vcs="Git" /> </component> </project> \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index ddd56a2..32cf9e6 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -53,9 +53,9 @@ public final class War extends AbstractModule { private static final String WAR_CMD = "war"; // Deck of card private static final String[] WAR_DECK = - new String[]{ "Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2" }; + new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; // Suits for the deck of card - private static final String[] WAR_SUITS = new String[]{ "Hearts", "Spades", "Diamonds", "Clubs" }; + private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; /** * The default constructor. @@ -85,9 +85,9 @@ public final class War extends AbstractModule { y = RANDOM.nextInt(WAR_DECK.length); getBot().send(sender + " drew the " + bold(WAR_DECK[i]) + " of " - + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); + + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); getBot().action("drew the " + bold(WAR_DECK[y]) + " of " - + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); + + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); if (i != y) { break; diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 4e20d32..465f603 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -33,6 +33,8 @@ package net.thauvin.erik.mobibot import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.XmlReader +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpFormat import java.net.MalformedURLException import java.net.URL @@ -67,7 +69,7 @@ class FeedReader( var i = 0 while (i < items.size && i < maxItems) { send(sender, items[i].title, false) - send(sender, Utils.helpFormat(Utils.green(items[i].link), false), false) + send(sender, helpFormat(green(items[i].link), false), false) i++ } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt index c4d3a5f..019d152 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -32,8 +32,9 @@ package net.thauvin.erik.mobibot -import kotlinx.coroutines.async +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.pinboard.PinboardPoster import java.time.ZoneId @@ -51,15 +52,15 @@ object PinboardUtils { */ @JvmStatic fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking { - async { + withContext(Dispatchers.Default) { poster.addPin( entry.link, entry.title, - postedBy(entry, ircServer), + entry.postedBy(ircServer), entry.pinboardTags, entry.date.toTimestamp() ) - }.await() + } } /** @@ -67,9 +68,9 @@ object PinboardUtils { */ @JvmStatic fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking { - async { + withContext(Dispatchers.Default) { poster.deletePin(entry.link) - }.await() + } } /** @@ -77,14 +78,14 @@ object PinboardUtils { */ @JvmStatic fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { - async { + withContext(Dispatchers.Default) { with(entry) { if (oldUrl != link) { poster.deletePin(oldUrl) poster.addPin( link, title, - postedBy(entry, ircServer), + entry.postedBy(ircServer), pinboardTags, date.toTimestamp() ) @@ -92,7 +93,7 @@ object PinboardUtils { poster.addPin( link, title, - postedBy(entry, ircServer), + entry.postedBy(ircServer), pinboardTags, date.toTimestamp(), replace = true, @@ -100,7 +101,7 @@ object PinboardUtils { ) } } - }.await() + } } /** @@ -109,15 +110,16 @@ object PinboardUtils { @JvmStatic fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( - this.toInstant().truncatedTo(ChronoUnit.SECONDS), - ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT) + this.toInstant().truncatedTo(ChronoUnit.SECONDS), + ZoneId.systemDefault() + ).format(DateTimeFormatter.ISO_INSTANT) } /** * Returns the pinboard.in extended attribution line. */ - private fun postedBy(entry: EntryLink, ircServer: String): String { - return "Posted by ${entry.nick} on ${entry.channel} ( $ircServer )" + private fun EntryLink.postedBy(ircServer: String): String { + return "Posted by ${this.nick} on ${this.channel} ( $ircServer )" } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 2f21dc6..5912ff4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -59,11 +59,11 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { fun storeRecap(sender: String, message: String, isAction: Boolean) { recaps.add( LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() - + " - $sender" + (if (isAction) " " else ": ") + message + + " - $sender" + (if (isAction) " " else ": ") + message ) @Suppress("MagicNumber") if (recaps.size > 10) { - recaps.removeAt(0) + recaps.removeFirst() } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index c20d77e..72acc6b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -40,7 +40,7 @@ class Versions(bot: Mobibot) : AbstractCommand(bot) { private val allVersions = listOf( "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") - + " (" + System.getProperty("os.arch") + ')', + + " (" + System.getProperty("os.arch") + ')', "Runtime: " + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version") ) override val name = "versions" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index ca74eae..0b68da8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -80,7 +80,7 @@ object EntriesMgr { if (!history.contains(bot.today)) { history.add(bot.today) while (history.size > maxBacklogs) { - history.removeAt(0) + history.removeFirst() } } OutputStreamWriter( @@ -201,7 +201,6 @@ object EntriesMgr { language = "en" } val buff: StringBuilder = StringBuilder() - var comment: EntryComment for (i in entries.size - 1 downTo 0) { with(entries[i]) { buff.setLength(0) @@ -215,13 +214,11 @@ object EntriesMgr { .append("</b></a>") if (comments.size > 0) { buff.append(" <br/><br/>") - val comments = comments for (j in comments.indices) { - comment = comments[j] if (j > 0) { buff.append(" <br/>") } - buff.append(comment.nick).append(": ").append(comment.comment) + buff.append(comments[j].nick).append(": ").append(comments[j].comment) } } item = SyndEntryImpl() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index c1c4416..be442d5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -43,9 +43,7 @@ data class EntryComment(var comment: String, var nick: String) : Serializable { */ val date: LocalDateTime = LocalDateTime.now() - override fun toString(): String { - return ("EntryComment{comment='$comment', date=$date, nick='$nick'}") - } + override fun toString(): String = "EntryComment{comment='$comment', date=$date, nick='$nick'}" companion object { // Serial version UID diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 09f22a7..b87f777 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -145,8 +145,8 @@ class EntryLink : Serializable { */ fun matches(match: String?): Boolean { return (StringUtils.containsIgnoreCase(link, match) - || StringUtils.containsIgnoreCase(title, match) - || StringUtils.containsIgnoreCase(nick, match)) + || StringUtils.containsIgnoreCase(title, match) + || StringUtils.containsIgnoreCase(nick, match)) } /** @@ -203,7 +203,7 @@ class EntryLink : Serializable { */ override fun toString(): String { return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," + - "nick='$nick', tags=$tags, title='$title'}") + "nick='$nick', tags=$tags, title='$title'}") } companion object { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index fc5decf..b714e9a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -152,6 +152,9 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { // Last exchange rates table publication date private var pubDate = "" + private fun Double.formatCurrency(): String = + NumberFormat.getCurrencyInstance(Constants.LOCALE).format(this).substring(1) + /** * Converts from a currency to another. */ @@ -171,11 +174,8 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { val doubleFrom = EXCHANGE_RATES[to]!!.toDouble() val doubleTo = EXCHANGE_RATES[from]!!.toDouble() PublicMessage( - NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1) - + " ${cmds[1].uppercase()} = " - + NumberFormat.getCurrencyInstance(Constants.LOCALE) - .format(amt * doubleTo / doubleFrom).substring(1) - + " ${cmds[3].uppercase()}" + amt.formatCurrency() + " ${cmds[1].uppercase()} = " + + (amt * doubleTo / doubleFrom).formatCurrency() + " ${cmds[3].uppercase()}" ) } catch (e: NumberFormatException) { ErrorMessage("Let's try with some real numbers next time, okay?") @@ -187,6 +187,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { } else ErrorMessage("Invalid query. Let's try again.") } + @JvmStatic fun currencyRates(): List<String> { val rates = mutableListOf<String>() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 8d1afc3..46f788c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -54,12 +54,12 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { send( channel, "$sender rolled two dice: ${bold(playerRoll.first)} and ${bold(playerRoll.second)}" - + " for a total of ${bold(playerTotal)}", + + " for a total of ${bold(playerTotal)}", isPrivate ) action( "rolled two dice: ${bold(roll.first)} and ${bold(roll.second)}" + - " for a total of ${bold(total)}" + " for a total of ${bold(total)}" ) when (winLoseOrTie(total, playerTotal)) { Result.WIN -> action("wins.") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 8184881..5c1a268 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -97,7 +97,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { try { val url = URL( "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - "&q=${encodeUrl(query)}&filter=1&num=5&alt=json" + "&q=${encodeUrl(query)}&filter=1&num=5&alt=json" ) val json = JSONObject(urlReader(url)) val ja = json.getJSONArray("items") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 8c50d09..e1557d9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -58,8 +58,8 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) { } catch (ignore: UnknownHostException) { if (args.matches( ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex() + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex() ) ) { try { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 6f7528b..bfcbe23 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -56,7 +56,7 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { add( helpFormat( "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" - + " | ${Hands.SCISSORS.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" ) ) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 4b4bec1..51f49e3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -139,7 +139,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { response = urlReader( URL( "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + encodeUrl(symbol) - + "&apikey=" + encodeUrl(apiKey) + + "&apikey=" + encodeUrl(apiKey) ) ) var json = getJsonResponse(response, debugMessage) @@ -153,8 +153,8 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { response = urlReader( URL( "${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" - + encodeUrl(symbolInfo.getString("1. symbol")) - + "&apikey=" + encodeUrl(apiKey) + + encodeUrl(symbolInfo.getString("1. symbol")) + + "&apikey=" + encodeUrl(apiKey) ) ) json = getJsonResponse(response, debugMessage) @@ -166,28 +166,23 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { add( PublicMessage( "Symbol: " + unescapeXml(quote.getString("01. symbol")) - + " [" + unescapeXml(symbolInfo.getString("2. name")) + ']' + + " [" + unescapeXml(symbolInfo.getString("2. name")) + ']' ) ) add(PublicMessage(" Price: " + unescapeXml(quote.getString("05. price")))) - add( - PublicMessage( - " Previous: " + unescapeXml(quote.getString("08. previous close")) - ) - ) + add(PublicMessage(" Previous: " + unescapeXml(quote.getString("08. previous close")))) add(NoticeMessage(" Open: " + unescapeXml(quote.getString("02. open")))) add(NoticeMessage(" High: " + unescapeXml(quote.getString("03. high")))) add(NoticeMessage(" Low: " + unescapeXml(quote.getString("04. low")))) add(NoticeMessage(" Volume: " + unescapeXml(quote.getString("06. volume")))) - add( - NoticeMessage( + add(NoticeMessage( " Latest: " + unescapeXml(quote.getString("07. latest trading day")) ) ) add( NoticeMessage( " Change: " + unescapeXml(quote.getString("09. change")) + " [" - + unescapeXml(quote.getString("10. change percent")) + ']' + + unescapeXml(quote.getString("10. change percent")) + ']' ) ) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 238952b..b1f7d3e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -104,9 +104,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { - throw ModuleException( - "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." - ) + throw ModuleException("${WEATHER_CMD.capitalise()} is disabled. The API key is missing.") } val owm = OWM(apiKey) val messages = mutableListOf<Message>() @@ -127,9 +125,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { owm.currentWeatherByCityName(city, getCountry(country)) } if (cwd.hasCityName()) { - messages.add( - PublicMessage("City: ${cwd.cityName} [${country.uppercase()}]") - ) + messages.add(PublicMessage("City: ${cwd.cityName} [${country.uppercase()}]")) with(cwd.mainData) { if (this != null) { if (hasTemp()) { @@ -170,7 +166,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { messages.add( NoticeMessage( "https://openweathermap.org/find?q=" - + encodeUrl("$city,${country.uppercase()}"), + + encodeUrl("$city,${country.uppercase()}"), Colors.GREEN ) ) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 7c769e9..0837590 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -58,6 +58,10 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { // The Time command private const val TIME_CMD = "time" + // Date/Time Format + private var dtf = + DateTimeFormatter.ofPattern("'The time is ${bold("'HH:mm'")} on ${bold("'EEEE, d MMMM yyyy'")} in '") + /** * Returns the current Internet (beat) Time. */ @@ -65,7 +69,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { private fun internetTime(): String { val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 - + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() + + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() return String.format(Locale.getDefault(), "%c%03d", '@', beats) } @@ -79,15 +83,8 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { if (BEATS_KEYWORD == tz) { "The current Internet Time is: ${bold(internetTime())} $BEATS_KEYWORD" } else { - (ZonedDateTime.now() - .withZoneSameInstant(ZoneId.of(tz)) - .format( - DateTimeFormatter.ofPattern( - "'The time is ${bold("'HH:mm'")} on ${bold("'EEEE, d MMMM yyyy'")} in '" - ) - ) - + bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' ')) - ) + (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) + + bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' '))) } } else { return ErrorMessage("Unsupported country/zone. Please try again.") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index dd698e4..7fcd726 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -32,14 +32,14 @@ package net.thauvin.erik.mobibot -import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.mobibot.PinboardUtils.toTimestamp +import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.pinboard.PinboardPoster import org.testng.Assert.assertFalse import org.testng.Assert.assertTrue import org.testng.annotations.Test -import java.util.Date import java.net.URL +import java.util.Date class PinboardUtilsTest : LocalProperties() { @Test @@ -71,7 +71,7 @@ class PinboardUtilsTest : LocalProperties() { val response = Utils.urlReader( URL( "https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" - + Utils.encodeUrl(url) + + Utils.encodeUrl(url) ) ) From 484f11664e0d22872b4a93fcec3a2d07bf5b5bb3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 18 May 2021 22:12:07 -0700 Subject: [PATCH 516/842] Added FeedReader tests. --- .../net/thauvin/erik/mobibot/FeedReader.kt | 57 +++++++++------ .../thauvin/erik/mobibot/FeedReaderTest.kt | 73 +++++++++++++++++++ 2 files changed, 106 insertions(+), 24 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 465f603..9cb929b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -31,11 +31,14 @@ */ package net.thauvin.erik.mobibot +import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.XmlReader +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat -import java.net.MalformedURLException +import java.io.IOException import java.net.URL /** @@ -49,38 +52,44 @@ class FeedReader( // URL to fetch private val url: String ) : Runnable { - // Maximum number of feed items to display - @Suppress("MagicNumber") - private val maxItems = 5 - /** * Fetches the Feed's items. */ override fun run() { with(bot) { try { - val input = SyndFeedInput() - XmlReader(URL(url)).use { reader -> - val feed = input.build(reader) - val items = feed.entries - if (items.isEmpty()) { - send(sender, "There is currently nothing to view.", false) - } else { - var i = 0 - while (i < items.size && i < maxItems) { - send(sender, items[i].title, false) - send(sender, helpFormat(green(items[i].link), false), false) - i++ - } - } + readFeed(url).forEach { + send(sender, it) } - } catch (e: MalformedURLException) { - if (logger.isDebugEnabled) logger.debug("Invalid feed URL.", e) - send(sender, "The feed URL is invalid.", false) - } catch (e: Exception) { - if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed.", e) + } catch (e: FeedException) { + if (logger.isDebugEnabled) logger.debug("Unabled to parse the feed at ${url}", e) + send(sender, "An error has occured while parsing the feed: ${e.message}", false) + } catch (e: IOException) { + if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed at ${url}", e) send(sender, "An error has occurred while fetching the feed: ${e.message}", false) } } } + + companion object { + @JvmStatic + @Throws(FeedException::class, IOException::class) + fun readFeed(url: String, maxItems: Int = 5) : List<Message> { + val messages = mutableListOf<Message>() + val input = SyndFeedInput() + XmlReader(URL(url)).use { reader -> + val feed = input.build(reader) + val items = feed.entries + if (items.isEmpty()) { + messages.add(PublicMessage("There is currently nothing to view.")) + } else { + items.take(maxItems).forEach { + messages.add(PublicMessage(it.title)) + messages.add(PublicMessage(helpFormat(green(it.link), false))) + } + } + } + return messages + } + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt new file mode 100644 index 0000000..0b2185e --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -0,0 +1,73 @@ +/* + * FeedReaderTest.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import com.rometools.rome.io.FeedException +import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.testng.annotations.Test + +import java.io.FileNotFoundException +import java.net.MalformedURLException +import java.net.UnknownHostException + +/** + * The `FeedReader Test` class. + */ +class FeedReaderTest { + @Test + fun readFeedTest() { + var messages = readFeed("https://feeds.thauvin.net/ethauvin") + assertThat(messages.size).describedAs("messages = 10").isEqualTo(10) + assertThat(messages[1].msg).describedAs("feed entry url").contains("ethauvin") + + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") + assertThat(messages[0].msg).describedAs("nothing to view").contains("nothing") + + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=42", 42) + assertThat(messages.size).describedAs("messages = 84").isEqualTo(84) + assertThat(messages.last().msg).describedAs("example entry url").contains("http://example.com/test/") + + assertThatThrownBy { readFeed("blah") }.describedAs("invalid URL") + .isInstanceOf(MalformedURLException::class.java) + + assertThatThrownBy { readFeed("https://www.example.com") }.describedAs("not a feed") + .isInstanceOf(FeedException::class.java) + + assertThatThrownBy { readFeed("https://www.examples.com/foo") }.describedAs("404 not found") + .isInstanceOf(FileNotFoundException::class.java) + + assertThatThrownBy { readFeed("https://www.doesnotexists.com") }.describedAs("unknown host") + .isInstanceOf(UnknownHostException::class.java) + } +} From 6644400a232206ef67ece1e8254be8cfae25953f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 18 May 2021 22:13:31 -0700 Subject: [PATCH 517/842] Added shudown() with associated Die and Kill commands. --- config/detekt/baseline.xml | 2 - .../net/thauvin/erik/mobibot/Mobibot.kt | 23 +++++++- .../net/thauvin/erik/mobibot/commands/Die.kt | 57 +++++++++++++++++++ .../net/thauvin/erik/mobibot/commands/Kill.kt | 56 ++++++++++++++++++ 4 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 07ee2ba..0b6f182 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -24,7 +24,6 @@ <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$// Daily backup private fun dailyBackup( bot: Mobibot, history: MutableList<String> )</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> - <ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> @@ -45,7 +44,6 @@ <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>TooGenericExceptionCaught:CryptoPrices.kt$CryptoPrices$e: Exception</ID> - <ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID> <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID> <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID> <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index c6390f0..0cb7100 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -45,8 +45,10 @@ import net.thauvin.erik.mobibot.commands.AddLog import net.thauvin.erik.mobibot.commands.ChannelFeed import net.thauvin.erik.mobibot.commands.Cycle import net.thauvin.erik.mobibot.commands.Debug +import net.thauvin.erik.mobibot.commands.Die import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.Info +import net.thauvin.erik.mobibot.commands.Kill import net.thauvin.erik.mobibot.commands.Me import net.thauvin.erik.mobibot.commands.Modules import net.thauvin.erik.mobibot.commands.Msg @@ -260,7 +262,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } } - /** * Responds with the default, commands or modules help. */ @@ -468,6 +469,24 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } } + /** + * Shutdown the bot. + */ + fun shutdown(sender: String, now: Boolean = false) { + if (now) { // kill + twitter.notification("$name killed by $sender on $channel") + sendRawLine("QUIT :Poof!") + } else { + timer.cancel() + twitter.shutdown() + twitter.notification("$name stopped by $sender on $channel") + @Suppress("MagicNumber") + sleep(3) + quitServer("The Bot Is Out There!") + } + exitProcess(0) + } + /** * Sleeps for the specified number of seconds. */ @@ -654,9 +673,11 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.add(AddLog(this), p) addons.add(ChannelFeed(this, channelName), p) addons.add(Cycle(this), p) + addons.add(Die(this), p) addons.add(Debug(this), p) addons.add(Ignore(this), p) addons.add(Info(this), p) + addons.add(Kill(this), p) addons.add(Me(this), p) addons.add(Modules(this), p) addons.add(Msg(this), p) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt new file mode 100644 index 0000000..eba2dcc --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -0,0 +1,57 @@ +/* + * Die.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.helpFormat + +class Die(bot: Mobibot) : AbstractCommand(bot) { + override val name = "die" + override val help = emptyList<String>() + override val isOp = true + override val isPublic = false + override val isVisible = false + + override fun commandResponse( + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + bot.send("$sender has just signed my death sentence.") + bot.shutdown(sender) + } + } +} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt new file mode 100644 index 0000000..6055798 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt @@ -0,0 +1,56 @@ +/* + * Kill.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.helpFormat + +class Kill(bot: Mobibot) : AbstractCommand(bot) { + override val name = "kill" + override val help = emptyList<String>() + override val isOp = true + override val isPublic = false + override val isVisible = false + + override fun commandResponse( + sender: String, + login: String, + args: String, + isOp: Boolean, + isPrivate: Boolean + ) { + if (isOp) { + bot.shutdown(sender, true) + } + } +} From 48c2ad65c0391098c643ec2ddd816b15be1f476b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 18 May 2021 23:48:25 -0700 Subject: [PATCH 518/842] Cleanup. --- .../kotlin/net/thauvin/erik/mobibot/Constants.kt | 5 +++++ .../kotlin/net/thauvin/erik/mobibot/FeedReader.kt | 10 +++++----- .../net/thauvin/erik/mobibot/commands/Die.kt | 1 - .../net/thauvin/erik/mobibot/commands/Kill.kt | 1 - .../thauvin/erik/mobibot/commands/links/Tags.kt | 8 ++++---- .../net/thauvin/erik/mobibot/commands/tell/Tell.kt | 14 +++++++------- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../net/thauvin/erik/mobibot/entries/EntriesMgr.kt | 3 ++- .../thauvin/erik/mobibot/entries/EntriesUtils.kt | 2 +- .../net/thauvin/erik/mobibot/modules/StockQuote.kt | 3 ++- .../net/thauvin/erik/mobibot/FeedReaderTest.kt | 4 ++-- 11 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index d1b33b3..4b93ac1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -102,6 +102,11 @@ object Constants { */ const val PROPS_ARG = "properties" + /** + * The tag command + */ + const val TAG_CMD = "T" + /** * The timer delay in minutes. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 9cb929b..884ca78 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.XmlReader -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage import java.io.IOException import java.net.URL @@ -62,10 +62,10 @@ class FeedReader( send(sender, it) } } catch (e: FeedException) { - if (logger.isDebugEnabled) logger.debug("Unabled to parse the feed at ${url}", e) + if (logger.isDebugEnabled) logger.debug("Unabled to parse the feed at $url", e) send(sender, "An error has occured while parsing the feed: ${e.message}", false) } catch (e: IOException) { - if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed at ${url}", e) + if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed at $url", e) send(sender, "An error has occurred while fetching the feed: ${e.message}", false) } } @@ -74,7 +74,7 @@ class FeedReader( companion object { @JvmStatic @Throws(FeedException::class, IOException::class) - fun readFeed(url: String, maxItems: Int = 5) : List<Message> { + fun readFeed(url: String, maxItems: Int = 5): List<Message> { val messages = mutableListOf<Message>() val input = SyndFeedInput() XmlReader(URL(url)).use { reader -> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index eba2dcc..c9c6299 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.helpFormat class Die(bot: Mobibot) : AbstractCommand(bot) { override val name = "die" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt index 6055798..803ab58 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.helpFormat class Kill(bot: Mobibot) : AbstractCommand(bot) { override val name = "kill" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 554d6c8..e2b6d8f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -42,8 +42,8 @@ import net.thauvin.erik.mobibot.entries.EntryLink class Tags(bot: Mobibot) : AbstractCommand(bot) { override val name = COMMAND override val help = listOf( - "To categorize or tag a URL, use its label and a T:", - helpFormat("${Constants.LINK_CMD}1T:<+tag|-tag> [...]") + "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", + helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") ) override val isOp = false override val isPublic = true @@ -60,7 +60,7 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - val cmds = args.substring(1).split("T:", limit = 2) + val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2) val index = cmds[0].toInt() - 1 if (index < LinksMgr.entries.size) { @@ -86,6 +86,6 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { } override fun matches(message: String): Boolean { - return message.matches("^${Constants.LINK_CMD}[0-9]+T:.*".toRegex()) + return message.matches("^${Constants.LINK_CMD}[0-9]+${Constants.TAG_CMD}:.*".toRegex()) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index cd59eac..0792eaa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -229,7 +229,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { bot.send( nickname, "Your message ${reverseColor("[ID " + message.id + ']')} was sent to " - + "${bold(message.recipient)} on ${message.receptionDate.toUtcDateTime()}", + + "${bold(message.recipient)} on ${message.receptionDate.toUtcDateTime()}", true ) message.isNotified = true @@ -252,8 +252,8 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { for (message in messages) { bot.send( sender, bold(message.sender) + ARROW + bold(message.recipient) - + " [ID: " + message.id + ", " - + (if (message.isReceived) "DELIVERED" else "QUEUED") + ']', + + " [ID: " + message.id + ", " + + (if (message.isReceived) "DELIVERED" else "QUEUED") + ']', isPrivate ) } @@ -275,16 +275,16 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { bot.send( sender, bold(message.sender) + ARROW + bold(message.recipient) - + " [${message.receptionDate.toUtcDateTime()}, ID: " - + bold(message.id) + ", DELIVERED]", + + " [${message.receptionDate.toUtcDateTime()}, ID: " + + bold(message.id) + ", DELIVERED]", isPrivate ) } else { bot.send( sender, bold(message.sender) + ARROW + bold(message.recipient) - + " [${message.queued.toUtcDateTime()}, ID: " - + bold(message.id) + ", QUEUED]", + + " [${message.queued.toUtcDateTime()}, ID: " + + bold(message.id) + ", QUEUED]", isPrivate ) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 396b854..6a058f6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -95,7 +95,7 @@ class TellMessage internal constructor( override fun toString(): String { return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " + - "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") + "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") } companion object { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 0b68da8..6f9ffff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -248,7 +248,8 @@ object EntriesMgr { } catch (e: FeedException) { if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) } catch (e: IOException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) + if (bot.logger.isWarnEnabled) + bot.logger.warn("An IO error occurred while generating the entries feed.", e) } } else { if (bot.logger.isWarnEnabled) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 5f83aa2..c0247c3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -76,5 +76,5 @@ object EntriesUtils { * Build an entry's tags/categories for display on the channel. */ fun buildTags(entryIndex: Int, entry: EntryLink): String = - buildLinkCmd(entryIndex) + "T: " + entry.pinboardTags.replace(",", ", ") + buildLinkCmd(entryIndex) + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ") } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 51f49e3..1459914 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -175,7 +175,8 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { add(NoticeMessage(" High: " + unescapeXml(quote.getString("03. high")))) add(NoticeMessage(" Low: " + unescapeXml(quote.getString("04. low")))) add(NoticeMessage(" Volume: " + unescapeXml(quote.getString("06. volume")))) - add(NoticeMessage( + add( + NoticeMessage( " Latest: " + unescapeXml(quote.getString("07. latest trading day")) ) ) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 0b2185e..3360242 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -53,11 +53,11 @@ class FeedReaderTest { messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") assertThat(messages[0].msg).describedAs("nothing to view").contains("nothing") - + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=42", 42) assertThat(messages.size).describedAs("messages = 84").isEqualTo(84) assertThat(messages.last().msg).describedAs("example entry url").contains("http://example.com/test/") - + assertThatThrownBy { readFeed("blah") }.describedAs("invalid URL") .isInstanceOf(MalformedURLException::class.java) From 5b9d8911e190a6dccb5dc42189301bf4881be31d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 18:24:56 -0700 Subject: [PATCH 519/842] Removed fetch on TravisCI --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 84c808c..b1a896e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,6 @@ env: global: - CI_NAME="Travis CI" -install: - - git fetch --unshallow --tags - addons: sonarcloud: organization: "ethauvin-github" From 9270f5f8b46ded05d1af6da9397ed5aa911a35cb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 18:43:20 -0700 Subject: [PATCH 520/842] Removed Sonarcloud from TravisCI. --- .travis.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1a896e..19b0ad1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,19 +5,9 @@ env: global: - CI_NAME="Travis CI" -addons: - sonarcloud: - organization: "ethauvin-github" - jdk: - openjdk11 - openjdk15 before_install: - chmod +x gradlew - -after_success: - - | - if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == openjdk11 ]; then - ./gradlew sonarqube - fi From 98ed1d5042d9c6c220a3a81c645a0a0b80630953 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 19:01:47 -0700 Subject: [PATCH 521/842] Added Sonarcloud to GitHub CI. --- .github/workflows/gradle.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 2fff393..5f9e89c 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,17 +5,29 @@ on: [push, pull_request, workflow_dispatch] jobs: build: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Cache SonarCloud packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Gradle packages + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle - name: Test with Gradle - run: ./gradlew check env: CI_NAME: "GitHub CI" ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -23,8 +35,10 @@ jobs: GOOGLE_CSE_CX: ${{ secrets.GOOGLE_CSE_CX }} OWM_API_KEY: ${{ secrets.OWM_API_KEY }} PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} TWITTER_CONSUMERKEY: ${{ secrets.TWITTER_CONSUMERKEY }} TWITTER_CONSUMERSECRET: ${{ secrets.TWITTER_CONSUMERSECRET }} TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} + run: ./gradlew check sonarqube From 381887d937643f7683ae2ed8142d72e786f6e2f6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 19:13:02 -0700 Subject: [PATCH 522/842] Testing Sonarqube with GitHub CI. --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5f9e89c..b8ad30b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -41,4 +41,4 @@ jobs: TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} - run: ./gradlew check sonarqube + run: ./gradlew sonarqube --info From 30f2ef7273136a2240670e013d2a51dbabb23a36 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 19:23:38 -0700 Subject: [PATCH 523/842] Added Sonarcloud URL. --- .github/workflows/gradle.yml | 2 +- build.gradle | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b8ad30b..5f9e89c 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -41,4 +41,4 @@ jobs: TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} - run: ./gradlew sonarqube --info + run: ./gradlew check sonarqube diff --git a/build.gradle b/build.gradle index 7bff7c0..b4bed24 100644 --- a/build.gradle +++ b/build.gradle @@ -145,6 +145,7 @@ incrementBuildMeta { sonarqube { properties { property "sonar.projectKey", "ethauvin_mobibot" + property "sonar.host.url", "https://sonarcloud.io" } } From b8c3bf96159111a8446f2f68cb2dfa22a25edef5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 19:29:34 -0700 Subject: [PATCH 524/842] Added sonar.organization property. --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index b4bed24..4d4a074 100644 --- a/build.gradle +++ b/build.gradle @@ -144,6 +144,7 @@ incrementBuildMeta { sonarqube { properties { + property "sonar.organization", "ethauvin-github" property "sonar.projectKey", "ethauvin_mobibot" property "sonar.host.url", "https://sonarcloud.io" } From e4d3f3f2c4ae6e7f3207a66f070d8196fb573eea Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 19:42:55 -0700 Subject: [PATCH 525/842] Added GitHub token env. --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5f9e89c..b1bb3a7 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -31,6 +31,7 @@ jobs: env: CI_NAME: "GitHub CI" ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} GOOGLE_CSE_CX: ${{ secrets.GOOGLE_CSE_CX }} OWM_API_KEY: ${{ secrets.OWM_API_KEY }} From d2c0e12e1a88de4a1adf9de150a0668ced4e31b8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 May 2021 20:18:32 -0700 Subject: [PATCH 526/842] Added Gradle caches setup and cleanup. --- .github/workflows/gradle.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b1bb3a7..b11bffb 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -22,11 +22,14 @@ jobs: key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Gradle packages - uses: actions/cache@v1 + uses: actions/cache@v2 with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- - name: Test with Gradle env: CI_NAME: "GitHub CI" @@ -43,3 +46,7 @@ jobs: TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} run: ./gradlew check sonarqube + - name: Cleanup Gradle Cache + run: | + rm -f ~/.gradle/caches/modules-2/modules-2.lock + rm -f ~/.gradle/caches/modules-2/gc.properties From 1263160924f991430096dfcaeae3a0f8580b39b2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 20 May 2021 18:09:42 -0700 Subject: [PATCH 527/842] Switched to system-wide Gradle. --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4b01f79..c415a8c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ stages: build: stage: build - script: ./gradlew --build-cache assemble + script: gradle --build-cache assemble cache: key: "$CI_COMMIT_REF_NAME" policy: push @@ -23,7 +23,7 @@ build: test: stage: test - script: ./gradlew check + script: gradle check cache: key: "$CI_COMMIT_REF_NAME" policy: pull From 43615f253cac49e5e87425ff9c4b423801c1aa6f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 20 May 2021 20:09:01 -0700 Subject: [PATCH 528/842] Removed TravisCI. --- .github/workflows/gradle.yml | 2 +- .travis.yml | 13 ------------- README.md | 2 +- version.properties | 6 +++--- 4 files changed, 5 insertions(+), 18 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b11bffb..c447ffe 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,4 +1,4 @@ -name: Java CI with Gradle +name: gradle-ci on: [push, pull_request, workflow_dispatch] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 19b0ad1..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: java -dist: trusty - -env: - global: - - CI_NAME="Travis CI" - -jdk: - - openjdk11 - - openjdk15 - -before_install: - - chmod +x gradlew diff --git a/README.md b/README.md index 4b2ed1c..1a28679 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![Build Status](https://travis-ci.com/ethauvin/mobibot.svg?branch=master)](https://travis-ci.com/ethauvin/mobibot) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: diff --git a/version.properties b/version.properties index b7bbbbd..18e0fb1 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat May 15 12:48:51 PDT 2021 -version.buildmeta=803 +#Wed May 19 19:28:39 PDT 2021 +version.buildmeta=842 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+803 +version.semver=0.8.0-beta+842 From 05b7ca8a411ce7a962300f24b592be81224c3cd4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 25 May 2021 16:33:17 -0700 Subject: [PATCH 529/842] Format cryptoprice as currency. --- build.gradle | 6 +++--- config/detekt/baseline.xml | 1 + .../net/thauvin/erik/mobibot/modules/CryptoPrices.kt | 10 +++++++++- version.properties | 6 +++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 4d4a074..b4aed72 100644 --- a/build.gradle +++ b/build.gradle @@ -3,12 +3,12 @@ plugins { id 'checkstyle' id 'com.github.ben-manes.versions' version '0.38.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.17.0' + id 'io.gitlab.arturbosch.detekt' version '1.17.1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.0' - id 'org.jetbrains.kotlin.kapt' version '1.5.0' + id 'org.jetbrains.kotlin.jvm' version '1.5.10' + id 'org.jetbrains.kotlin.kapt' version '1.5.10' id 'org.sonarqube' version '3.2.0' id 'pmd' } diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 0b6f182..6b6b2e0 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -39,6 +39,7 @@ <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> + <ID>SwallowedException:CryptoPrices.kt$CryptoPrices$catch (e: IllegalArgumentException) { price.amount }</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 4cd57c2..8f98fa7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -38,6 +38,9 @@ import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.msg.PublicMessage +import java.text.NumberFormat +import java.util.Currency + /** * The Cryptocurrency Prices module. */ @@ -51,7 +54,12 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { try { val price = currentPrice(args.split(' ')) - send(sender, PublicMessage("${price.base}: ${price.amount} [${price.currency}]")) + val amount = try { + price.toCurrency() + } catch (e: IllegalArgumentException) { + price.amount + } + send(sender, PublicMessage("${price.base}: $amount [${price.currency}]")) } catch (e: CryptoException) { if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) send(e.message) diff --git a/version.properties b/version.properties index 18e0fb1..854ee64 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed May 19 19:28:39 PDT 2021 -version.buildmeta=842 +#Tue May 25 18:01:58 PDT 2021 +version.buildmeta=848 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+842 +version.semver=0.8.0-beta+848 From 2c1ea56e79849dbcfdb24573151f323790cec2a7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 28 May 2021 11:29:39 -0700 Subject: [PATCH 530/842] Updated dependencies. --- build.gradle | 2 +- config/detekt/baseline.xml | 1 - .../net/thauvin/erik/mobibot/modules/CryptoPrices.kt | 11 ++++------- .../thauvin/erik/mobibot/modules/CryptoPricesTest.kt | 4 ++-- version.properties | 6 +++--- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index b4aed72..9380b95 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'application' id 'checkstyle' - id 'com.github.ben-manes.versions' version '0.38.0' + id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.17.1' id 'jacoco' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 6b6b2e0..0b6f182 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -39,7 +39,6 @@ <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> - <ID>SwallowedException:CryptoPrices.kt$CryptoPrices$catch (e: IllegalArgumentException) { price.amount }</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 8f98fa7..c4b2fcd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -33,14 +33,11 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.crypto.CryptoException import net.thauvin.erik.crypto.CryptoPrice -import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice +import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.msg.PublicMessage -import java.text.NumberFormat -import java.util.Currency - /** * The Cryptocurrency Prices module. */ @@ -56,7 +53,7 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { val price = currentPrice(args.split(' ')) val amount = try { price.toCurrency() - } catch (e: IllegalArgumentException) { + } catch (ignore: IllegalArgumentException) { price.amount } send(sender, PublicMessage("${price.base}: $amount [${price.currency}]")) @@ -82,9 +79,9 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { */ fun currentPrice(args: List<String>): CryptoPrice { return if (args.size == 2) - marketPrice(args[0], args[1]) + spotPrice(args[0], args[1]) else - marketPrice(args[0]) + spotPrice(args[0]) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index f046b59..98a72cb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -45,11 +45,11 @@ class CryptoPricesTest { var price = currentPrice(listOf("BTC")) assertThat(price.base).describedAs("is BTC").isEqualTo("BTC") assertThat(price.currency).describedAs("is USD").isEqualTo("USD") - assertThat(price.amount).describedAs("BTC > 0").isGreaterThan(0.00) + assertThat(price.amount.signum() > 0).describedAs("BTC > 0").isTrue price = currentPrice(listOf("ETH", "EUR")) assertThat(price.base).describedAs("is ETH").isEqualTo("ETH") assertThat(price.currency).describedAs("is EUR").isEqualTo("EUR") - assertThat(price.amount).describedAs("ETH > 0").isGreaterThan(0.00) + assertThat(price.amount.signum() > 0).describedAs("ETH > 0").isTrue } } diff --git a/version.properties b/version.properties index 854ee64..68e52a4 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 25 18:01:58 PDT 2021 -version.buildmeta=848 +#Fri May 28 11:26:02 PDT 2021 +version.buildmeta=854 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+848 +version.semver=0.8.0-beta+854 From e873d01338dfa56f4c48da47ed41fe9330d09ad8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 28 May 2021 11:37:25 -0700 Subject: [PATCH 531/842] Fixed Google search test for GitHub CI. --- .../net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index f2fec0b..aa4d98e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -49,7 +49,7 @@ class GoogleSearchTest : LocalProperties() { try { var messages = searchGoogle("mobitopia", apiKey, cseKey) assertThat(messages).describedAs("mobitopia results not empty").isNotEmpty - assertThat(messages[0].msg).describedAs("found mobibtopia").contains("mobitopia") + assertThat(messages[0].msg).describedAs("found mobibtopia").containsIgnoringCase("mobitopia") messages = searchGoogle("aapl", apiKey, cseKey) assertThat(messages).describedAs("aapl results not empty").isNotEmpty assertThat(messages[0].msg).describedAs("found apple").containsIgnoringCase("apple") diff --git a/version.properties b/version.properties index 68e52a4..b0d3bda 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri May 28 11:26:02 PDT 2021 -version.buildmeta=854 +#Fri May 28 11:36:41 PDT 2021 +version.buildmeta=855 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+854 +version.semver=0.8.0-beta+855 From 3f628e58f02399ca4adf435218fb52b8af25110f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 29 May 2021 22:01:18 -0700 Subject: [PATCH 532/842] Updated PMD rules. --- build.gradle | 31 +- config/pmd.xml | 290 +++++------------- .../net/thauvin/erik/mobibot/modules/War.java | 6 +- version.properties | 6 +- 4 files changed, 90 insertions(+), 243 deletions(-) diff --git a/build.gradle b/build.gradle index 9380b95..b1cd168 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" ext.versions = [ log4j : '2.14.1', - pmd : '6.34.0', + pmd : '6.35.0', ] repositories { @@ -65,8 +65,8 @@ dependencies { test { testLogging { - exceptionFormat "full" - events /* "passed", */ "skipped", "failed" + exceptionFormat = "full" + events(/* "passed", */ "skipped", "failed") } useTestNG() { options.suites('src/test/resources/testng.xml') @@ -129,7 +129,7 @@ clean { } run { - args '-d' + args('-d') } incrementBuildMeta { @@ -144,9 +144,9 @@ incrementBuildMeta { sonarqube { properties { - property "sonar.organization", "ethauvin-github" - property "sonar.projectKey", "ethauvin_mobibot" - property "sonar.host.url", "https://sonarcloud.io" + property("sonar.organization", "ethauvin-github") + property("sonar.projectKey", "ethauvin_mobibot") + property("sonar.host.url", "https://sonarcloud.io") } } @@ -155,7 +155,7 @@ jacoco { } jacocoTestReport { - dependsOn test + dependsOn(test) reports { html.enabled = true xml.enabled = true @@ -175,27 +175,26 @@ tasks.sonarqube { task copyToDeploy(type: Copy) { from('properties') - from jar - into deployDir + from(jar) + into(deployDir) } task copyToDeployLib(type: Copy) { from(configurations.runtimeClasspath) { - exclude 'annotations-*.jar' + exclude('annotations-*.jar') } - into deployDir + '/lib' + into(deployDir + '/lib') } task deploy(dependsOn: ['clean', 'build', 'jar']) { description = "Copies all needed files to the ${deployDir} directory." group = 'Publishing' - outputs.dir deployDir - inputs.files copyToDeploy - inputs.files copyToDeployLib + outputs.dir(deployDir) + inputs.files(copyToDeploy, copyToDeployLib) doLast { file(deployDir + '/logs').mkdir() } - mustRunAfter clean + mustRunAfter(clean) } task release(dependsOn: ['wrapper', 'deploy']) { diff --git a/config/pmd.xml b/config/pmd.xml index 2760bff..890a490 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -1,116 +1,82 @@ <?xml version="1.0"?> <ruleset name="erik" - xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>Erik's Ruleset</description> <!-- BEST PRACTICES --> - <rule ref="category/java/bestpractices.xml/AvoidMessageDigestField"/> - <!-- <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/> --> - <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/> - <rule ref="category/java/bestpractices.xml/CheckResultSet"/> - <rule ref="category/java/bestpractices.xml/ConstantsInInterface"/> - <rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitchStmt"/> - <rule ref="category/java/bestpractices.xml/DoubleBraceInitialization"/> - <rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/> - <rule ref="category/java/bestpractices.xml/GuardLogStatement"/> - <rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons" /> - <!-- <rule ref="category/java/bestpractices.xml/LooseCoupling"/> --> - <rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray"/> + <rule ref="category/java/bestpractices.xml"> + <exclude name="AvoidPrintStackTrace"/> + <exclude name="JUnit4TestShouldUseTestAnnotation"/> + <exclude name="JUnitTestContainsTooManyAsserts"/> + </rule> + <rule ref="category/java/bestpractices.xml/MissingOverride"> <properties> - <property name="violationSuppressXPath" value="//MethodDeclaration[@Name='hashCode' or @Name='equals' or @Name='toString']"/> + <property name="violationSuppressXPath" + value="//MethodDeclaration[@Name='hashCode' or @Name='equals' or @Name='toString']"/> </properties> </rule> - <rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"/> - <rule ref="category/java/bestpractices.xml/PreserveStackTrace"/> - <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/> - <!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> - <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/> --> - <rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/> - <rule ref="category/java/bestpractices.xml/SystemPrintln"/> - <rule ref="category/java/bestpractices.xml/UnusedAssignment"/> - <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/> - <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/> - <rule ref="category/java/bestpractices.xml/UnusedPrivateField"/> - <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/> - <rule ref="category/java/bestpractices.xml/UseAssertEqualsInsteadOfAssertTrue"/> - <rule ref="category/java/bestpractices.xml/UseAssertNullInsteadOfAssertTrue"/> - <rule ref="category/java/bestpractices.xml/UseAssertSameInsteadOfAssertTrue"/> - <rule ref="category/java/bestpractices.xml/UseAssertTrueInsteadOfAssertEquals"/> - <rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"/> - <rule ref="category/java/bestpractices.xml/UseVarargs"/> - <!-- NAMING CONVENTIONS --> - <!-- <rule ref="category/java/codestyle.xml/ClassNamingConventions"/> --> - <rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"/> - <rule ref="category/java/codestyle.xml/GenericsNaming"/> - <rule ref="category/java/codestyle.xml/LocalVariableNamingConventions"/> - <rule ref="category/java/codestyle.xml/MethodNamingConventions"/> - <!-- <rule ref="category/java/codestyle.xml/PackageCase"/> --> - - - <!-- OTHER --> - <rule ref="category/java/codestyle.xml/AvoidDollarSigns"/> - <rule ref="category/java/codestyle.xml/AvoidProtectedFieldInFinalClass"/> - <rule ref="category/java/codestyle.xml/AvoidProtectedMethodInFinalClassNotExtending"/> - <rule ref="category/java/codestyle.xml/AvoidUsingNativeCode"/> - <rule ref="category/java/codestyle.xml/BooleanGetMethodName"/> - <rule ref="category/java/codestyle.xml/CallSuperInConstructor"/> - <rule ref="category/java/codestyle.xml/ControlStatementBraces"/> - <rule ref="category/java/codestyle.xml/EmptyMethodInAbstractClassShouldBeAbstract"/> - <rule ref="category/java/codestyle.xml/ExtendsObject"/> - <rule ref="category/java/codestyle.xml/FieldDeclarationsShouldBeAtStartOfClass"/> - <rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"/> - <rule ref="category/java/codestyle.xml/IdenticalCatchBranches"/> - <rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal"/> - <rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal"/> - <rule ref="category/java/codestyle.xml/NoPackage"/> - <rule ref="category/java/codestyle.xml/PrematureDeclaration"/> - <rule ref="category/java/codestyle.xml/TooManyStaticImports"/> - <rule ref="category/java/codestyle.xml/UnnecessaryAnnotationValueElement"/> - <rule ref="category/java/codestyle.xml/UnnecessaryConstructor"/> - <rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName"/> - <rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/> - <!-- <rule ref="category/java/codestyle.xml/UnnecessaryModifier"/> --> - <rule ref="category/java/codestyle.xml/UnnecessaryReturn"/> - <!-- <rule ref="category/java/codestyle.xml/UselessParentheses"/> --> - <rule ref="category/java/codestyle.xml/UselessQualifiedThis"/> - + <!-- CODE STYLE --> + <rule ref="category/java/codestyle.xml"> + <exclude name="AtLeastOneConstructor"/> + <exclude name="ClassNamingConventions"/> + <exclude name="ConfusingTernary"/> + <exclude name="CommentDefaultAccessModifier"/> + <exclude name="DefaultPackage"/> + <exclude name="FieldNamingConventions"/> + <exclude name="LongVariable"/> + <exclude name="OnlyOneReturn"/> + <exclude name="PackageCase"/> + <exclude name="ShortClassName"/> + <exclude name="ShortMethodName"/> + <exclude name="ShortVariable"/> + <exclude name="UselessParentheses"/> + <exclude name="UseUnderscoresInNumericLiterals"/> + </rule> <!-- DESIGN --> - <rule ref="category/java/design.xml/AbstractClassWithoutAnyMethod"/> - <rule ref="category/java/design.xml/AvoidRethrowingException"/> - <rule ref="category/java/design.xml/AvoidThrowingNewInstanceOfSameException"/> - <rule ref="category/java/design.xml/AvoidThrowingNullPointerException"/> - <rule ref="category/java/design.xml/AvoidThrowingRawExceptionTypes"/> - <rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"/> - <rule ref="category/java/design.xml/CollapsibleIfStatements"/> - <rule ref="category/java/design.xml/CouplingBetweenObjects"/> - <rule ref="category/java/design.xml/DataClass"/> - <rule ref="category/java/design.xml/DoNotExtendJavaLangError"/> - <rule ref="category/java/design.xml/ExceptionAsFlowControl"/> - <!-- <rule ref="category/java/design.xml/ExcessivePublicCount"/> --> - <rule ref="category/java/design.xml/FinalFieldCouldBeStatic"/> - <rule ref="category/java/design.xml/ImmutableField"/> - <rule ref="category/java/design.xml/LogicInversion"/> - <rule ref="category/java/design.xml/SignatureDeclareThrowsException"/> - <rule ref="category/java/design.xml/SimplifiedTernary"/> - <rule ref="category/java/design.xml/SimplifyBooleanAssertion"/> - <rule ref="category/java/design.xml/SimplifyBooleanExpressions"/> - <rule ref="category/java/design.xml/SimplifyBooleanReturns"/> - <rule ref="category/java/design.xml/SimplifyConditional"/> - <rule ref="category/java/design.xml/SingularField"/> - <rule ref="category/java/design.xml/SwitchDensity"/> - <rule ref="category/java/design.xml/UselessOverridingMethod"/> - <rule ref="category/java/design.xml/UseUtilityClass"/> + <rule ref="category/java/design.xml"> + <exclude name="AvoidCatchingGenericException"/> + <exclude name="AvoidDeeplyNestedIfStmts"/> + <exclude name="AvoidUncheckedExceptionsInSignatures"/> + <exclude name="CognitiveComplexity"/> + <exclude name="CyclomaticComplexity"/> + <exclude name="ExcessiveClassLength"/> + <exclude name="ExcessiveMethodLength"/> + <exclude name="ExcessiveParameterList"/> + <exclude name="ExcessivePublicCount"/> + <exclude name="GodClass"/> + <exclude name="LawOfDemeter"/> + <exclude name="LoosePackageCoupling"/> + <exclude name="NPathComplexity"/> + <exclude name="NcssCount"/> + <exclude name="TooManyFields"/> + <exclude name="TooManyMethods"/> + <exclude name="UseObjectForClearerAPI"/> + </rule> <!-- DOCUMENTATION --> - <rule ref="category/java/documentation.xml/UncommentedEmptyConstructor"/> - <rule ref="category/java/documentation.xml/UncommentedEmptyMethodBody"/> - + <rule ref="category/java/documentation.xml"> + <exclude name="CommentRequired"/> + <exclude name="CommentSize"/> + </rule> <!-- ERROR PRONE --> + <rule ref="category/java/errorprone.xml"> + <exclude name="AssignmentInOperand"/> + <exclude name="AvoidCatchingNPE"/> + <exclude name="AvoidDuplicateLiterals"/> + <exclude name="AvoidFieldNameMatchingMethodName"/> + <exclude name="AvoidFieldNameMatchingTypeName"/> + <exclude name="AvoidLiteralsInIfCondition"/> + <exclude name="BeanMembersShouldSerialize"/> + <exclude name="EmptyCatchBlock"/> + <exclude name="NullAssignment"/> + </rule> + <rule ref="category/java/errorprone.xml/AssignmentInOperand"> <properties> <property name="allowWhile" value="true"/> @@ -118,85 +84,11 @@ <property name="allowIf" value="true"/> </properties> </rule> - <rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"/> - <rule ref="category/java/errorprone.xml/AvoidAccessibilityAlteration"/> - <rule ref="category/java/errorprone.xml/AvoidAssertAsIdentifier"/> - <rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"/> - <rule ref="category/java/errorprone.xml/AvoidCallingFinalize"/> - <rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"/> - <rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/> <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals"> <properties> <property name="skipAnnotations" value="true"/> </properties> </rule> - <rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier"/> - <rule ref="category/java/errorprone.xml/AvoidInstanceofChecksInCatchClause"/> - <rule ref="category/java/errorprone.xml/AvoidLosingExceptionInformation"/> - <rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators"/> - <rule ref="category/java/errorprone.xml/AvoidUsingOctalValues"/> - <rule ref="category/java/errorprone.xml/BadComparison"/> - <rule ref="category/java/errorprone.xml/BrokenNullCheck"/> - <rule ref="category/java/errorprone.xml/CallSuperFirst"/> - <rule ref="category/java/errorprone.xml/CallSuperLast"/> - <rule ref="category/java/errorprone.xml/CheckSkipResult"/> - <rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray"/> - <rule ref="category/java/errorprone.xml/CloneMethodMustBePublic"/> - <rule ref="category/java/errorprone.xml/CloneMethodMustImplementCloneable"/> - <rule ref="category/java/errorprone.xml/CloneMethodReturnTypeMustMatchClassName"/> - <rule ref="category/java/errorprone.xml/CloneThrowsCloneNotSupportedException"/> - <rule ref="category/java/errorprone.xml/CloseResource"/> - <rule ref="category/java/errorprone.xml/CompareObjectsWithEquals"/> - <rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod"/> - <rule ref="category/java/errorprone.xml/DoNotCallGarbageCollectionExplicitly"/> - <rule ref="category/java/errorprone.xml/DoNotExtendJavaLangThrowable"/> - <rule ref="category/java/errorprone.xml/DoNotHardCodeSDCard"/> - <rule ref="category/java/errorprone.xml/DoNotThrowExceptionInFinally"/> - <rule ref="category/java/errorprone.xml/DontImportSun"/> - <rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices"/> - <rule ref="category/java/errorprone.xml/EqualsNull"/> - <rule ref="category/java/errorprone.xml/FinalizeDoesNotCallSuperFinalize"/> - <rule ref="category/java/errorprone.xml/FinalizeOnlyCallsSuperFinalize"/> - <rule ref="category/java/errorprone.xml/FinalizeOverloaded"/> - <rule ref="category/java/errorprone.xml/FinalizeShouldBeProtected"/> - <rule ref="category/java/errorprone.xml/IdempotentOperations"/> - <rule ref="category/java/errorprone.xml/InstantiationToGetClass"/> - <rule ref="category/java/errorprone.xml/InvalidLogMessageFormat"/> - <rule ref="category/java/errorprone.xml/JumbledIncrementer"/> - <rule ref="category/java/errorprone.xml/JUnitSpelling"/> - <rule ref="category/java/errorprone.xml/JUnitStaticSuite"/> - <rule ref="category/java/errorprone.xml/MethodWithSameNameAsEnclosingClass"/> - <rule ref="category/java/errorprone.xml/MisplacedNullCheck"/> - <rule ref="category/java/errorprone.xml/MissingBreakInSwitch"/> - <rule ref="category/java/errorprone.xml/MissingSerialVersionUID"/> - <rule ref="category/java/errorprone.xml/MissingStaticMethodInNonInstantiatableClass"/> - <rule ref="category/java/errorprone.xml/MoreThanOneLogger"/> - <rule ref="category/java/errorprone.xml/NonCaseLabelInSwitchStatement"/> - <rule ref="category/java/errorprone.xml/NonStaticInitializer"/> - <!-- <rule ref="category/java/errorprone.xml/NullAssignment"/> --> - <rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"/> - <rule ref="category/java/errorprone.xml/ProperCloneImplementation"/> - <rule ref="category/java/errorprone.xml/ProperLogger"/> - <rule ref="category/java/errorprone.xml/ReturnEmptyArrayRatherThanNull"/> - <rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock"/> - <rule ref="category/java/errorprone.xml/SimpleDateFormatNeedsLocale"/> - <rule ref="category/java/errorprone.xml/SingleMethodSingleton"/> - <rule ref="category/java/errorprone.xml/SingletonClassReturningNewInstance"/> - <rule ref="category/java/errorprone.xml/StaticEJBFieldShouldBeFinal"/> - <rule ref="category/java/errorprone.xml/StringBufferInstantiationWithChar"/> - <rule ref="category/java/errorprone.xml/SuspiciousEqualsMethodName"/> - <rule ref="category/java/errorprone.xml/SuspiciousHashcodeMethodName"/> - <rule ref="category/java/errorprone.xml/SuspiciousOctalEscape"/> - <rule ref="category/java/errorprone.xml/TestClassWithoutTestCases"/> - <rule ref="category/java/errorprone.xml/UnconditionalIfStatement"/> - <rule ref="category/java/errorprone.xml/UnnecessaryBooleanAssertion"/> - <rule ref="category/java/errorprone.xml/UnnecessaryCaseChange"/> - <rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary"/> - <rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals"/> - <rule ref="category/java/errorprone.xml/UseCorrectExceptionLogging"/> - <rule ref="category/java/errorprone.xml/UseEqualsToCompareStrings"/> - <rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"/> - <rule ref="category/java/errorprone.xml/UseLocaleWithCaseConversions"/> <rule ref="category/java/errorprone.xml/EmptyCatchBlock"> <properties> <property name="allowExceptionNameRegex"> @@ -204,60 +96,16 @@ </property> </properties> </rule> - <rule ref="category/java/errorprone.xml/EmptyFinalizer"/> - <rule ref="category/java/errorprone.xml/EmptyFinallyBlock"/> - <rule ref="category/java/errorprone.xml/EmptyIfStmt"/> - <rule ref="category/java/errorprone.xml/EmptyInitializer"/> - <rule ref="category/java/errorprone.xml/EmptyStatementBlock"/> - <rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop"/> - <rule ref="category/java/errorprone.xml/EmptySwitchStatements"/> - <rule ref="category/java/errorprone.xml/EmptySynchronizedBlock"/> - <rule ref="category/java/errorprone.xml/EmptyTryBlock"/> - <rule ref="category/java/errorprone.xml/EmptyWhileStmt"/> - <!-- MULTITHREADING --> - <rule ref="category/java/multithreading.xml/AvoidSynchronizedAtMethodLevel"/> - <rule ref="category/java/multithreading.xml/AvoidThreadGroup"/> - <rule ref="category/java/multithreading.xml/AvoidUsingVolatile"/> - <rule ref="category/java/multithreading.xml/DontCallThreadRun"/> - <rule ref="category/java/multithreading.xml/DoubleCheckedLocking"/> - <rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"/> - <rule ref="category/java/multithreading.xml/UnsynchronizedStaticFormatter"/> - <rule ref="category/java/multithreading.xml/UseConcurrentHashMap"/> - <rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"/> + <rule ref="category/java/multithreading.xml"> + </rule> <!-- PERFORMANCE --> - <rule ref="category/java/performance.xml/AddEmptyString"/> - <rule ref="category/java/performance.xml/AppendCharacterWithChar"/> - <rule ref="category/java/performance.xml/AvoidArrayLoops"/> - <rule ref="category/java/performance.xml/AvoidFileStream"/> - <rule ref="category/java/performance.xml/AvoidInstantiatingObjectsInLoops"/> - <rule ref="category/java/performance.xml/BigIntegerInstantiation"/> - <rule ref="category/java/performance.xml/BooleanInstantiation"/> - <rule ref="category/java/performance.xml/ByteInstantiation"/> - <rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse"/> - <rule ref="category/java/performance.xml/ConsecutiveLiteralAppends"/> - <rule ref="category/java/performance.xml/InefficientEmptyStringCheck"/> - <rule ref="category/java/performance.xml/InefficientStringBuffering"/> - <rule ref="category/java/performance.xml/InsufficientStringBufferDeclaration"/> - <rule ref="category/java/performance.xml/IntegerInstantiation"/> - <rule ref="category/java/performance.xml/LongInstantiation"/> - <rule ref="category/java/performance.xml/OptimizableToArrayCall"/> - <rule ref="category/java/performance.xml/RedundantFieldInitializer"/> - <rule ref="category/java/performance.xml/ShortInstantiation"/> - <rule ref="category/java/performance.xml/StringInstantiation"/> - <rule ref="category/java/performance.xml/StringToString"/> - <rule ref="category/java/performance.xml/TooFewBranchesForASwitchStatement"/> - <rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation"/> - <!-- <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector"/> --> - <rule ref="category/java/performance.xml/UseArraysAsList"/> - <rule ref="category/java/performance.xml/UseIndexOfChar"/> - <rule ref="category/java/performance.xml/UselessStringValueOf"/> - <rule ref="category/java/performance.xml/UseStringBufferForStringAppends"/> - <rule ref="category/java/performance.xml/UseStringBufferLength"/> + <rule ref="category/java/performance.xml"> + </rule> <!-- SECURITY --> - <rule ref="category/java/security.xml/HardCodedCryptoKey"/> - <rule ref="category/java/security.xml/InsecureCryptoIv"/> + <rule ref="category/java/security.xml"> + </rule> </ruleset> diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 32cf9e6..88e70f7 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -52,10 +52,10 @@ public final class War extends AbstractModule { // War command private static final String WAR_CMD = "war"; // Deck of card - private static final String[] WAR_DECK = - new String[]{"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; + private static final String[] WAR_DECK = + {"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; // Suits for the deck of card - private static final String[] WAR_SUITS = new String[]{"Hearts", "Spades", "Diamonds", "Clubs"}; + private static final String[] WAR_SUITS = {"Hearts", "Spades", "Diamonds", "Clubs"}; /** * The default constructor. diff --git a/version.properties b/version.properties index b0d3bda..772f2ee 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri May 28 11:36:41 PDT 2021 -version.buildmeta=855 +#Sat May 29 22:00:14 PDT 2021 +version.buildmeta=866 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+855 +version.semver=0.8.0-beta+866 From b2dcf781c200e9f209ebef58e8b7b7e38f218af1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 31 May 2021 01:49:35 -0700 Subject: [PATCH 533/842] Added Gihub workflow matrix for Java 11 & 15. --- .github/workflows/gradle.yml | 25 ++++++++++++++++++------- build.gradle | 34 ++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c447ffe..a673ac4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,17 +5,24 @@ on: [push, pull_request, workflow_dispatch] jobs: build: runs-on: ubuntu-latest + env: + GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m" + SONAR_JDK: "11" + strategy: + matrix: + java-version: [ 11, 15 ] steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Set up JDK 11 + - name: Set up JDK ${{ matrix.java-version }} uses: actions/setup-java@v1 with: - java-version: 11 + java-version: ${{ matrix.java-version }} - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Cache SonarCloud packages + if: matrix.java-version == env.SONAR_JDK uses: actions/cache@v1 with: path: ~/.sonar/cache @@ -27,25 +34,29 @@ jobs: path: | ~/.gradle/caches ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | - ${{ runner.os }}-gradle- + ${{ runner.os }}-gradle-${{ matrix.java-version }}- - name: Test with Gradle env: CI_NAME: "GitHub CI" ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} GOOGLE_CSE_CX: ${{ secrets.GOOGLE_CSE_CX }} OWM_API_KEY: ${{ secrets.OWM_API_KEY }} PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} TWITTER_CONSUMERKEY: ${{ secrets.TWITTER_CONSUMERKEY }} TWITTER_CONSUMERSECRET: ${{ secrets.TWITTER_CONSUMERSECRET }} TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} - run: ./gradlew check sonarqube + run: ./gradlew build check --stacktrace + - name: SonarCloud + if: success() && matrix.java-version == env.SONAR_JDK + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: ./gradlew sonarqube - name: Cleanup Gradle Cache run: | rm -f ~/.gradle/caches/modules-2/modules-2.lock diff --git a/build.gradle b/build.gradle index b1cd168..500333a 100644 --- a/build.gradle +++ b/build.gradle @@ -65,8 +65,8 @@ dependencies { test { testLogging { - exceptionFormat = "full" - events(/* "passed", */ "skipped", "failed") + exceptionFormat = 'full' + events('skipped', 'failed') } useTestNG() { options.suites('src/test/resources/testng.xml') @@ -80,7 +80,7 @@ java { kapt { arguments { - arg("semver.project.dir", projectDir) + arg('semver.project.dir', projectDir) } } @@ -93,7 +93,7 @@ tasks.withType(JavaCompile) { } compileJava { - dependsOn('incrementBuildMeta') + dependsOn 'incrementBuildMeta' options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation'] } @@ -137,16 +137,16 @@ incrementBuildMeta { if (!System.getenv('CI')) { buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) } else { - println "No increment on CIs." + println 'No increment on CIs.' } } } sonarqube { properties { - property("sonar.organization", "ethauvin-github") - property("sonar.projectKey", "ethauvin_mobibot") - property("sonar.host.url", "https://sonarcloud.io") + property('sonar.organization', 'ethauvin-github') + property('sonar.projectKey', 'ethauvin_mobibot') + property('sonar.host.url', 'https://sonarcloud.io') } } @@ -170,34 +170,36 @@ tasks.withType(Checkstyle) { } tasks.sonarqube { - dependsOn("jacocoTestReport") + dependsOn('jacocoTestReport') } task copyToDeploy(type: Copy) { from('properties') - from(jar) - into(deployDir) + from jar + into deployDir } task copyToDeployLib(type: Copy) { from(configurations.runtimeClasspath) { - exclude('annotations-*.jar') + exclude 'annotations-*.jar' } into(deployDir + '/lib') } -task deploy(dependsOn: ['clean', 'build', 'jar']) { +task deploy { description = "Copies all needed files to the ${deployDir} directory." group = 'Publishing' - outputs.dir(deployDir) + dependsOn(clean, build, jar) + outputs.dir deployDir inputs.files(copyToDeploy, copyToDeployLib) doLast { file(deployDir + '/logs').mkdir() } - mustRunAfter(clean) + mustRunAfter clean } -task release(dependsOn: ['wrapper', 'deploy']) { +task release { group = 'Publishing' description = 'Releases new version.' + dependsOn(wrapper, 'deploy') } From e35d112b28285e8163f17844a9b71d80014f2c05 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 11 Jun 2021 20:22:57 -0700 Subject: [PATCH 534/842] Cleanup. --- .github/workflows/gradle.yml | 10 ++++++++++ build.gradle | 10 ++++------ version.properties | 6 +++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index a673ac4..715e6fe 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,22 +5,28 @@ on: [push, pull_request, workflow_dispatch] jobs: build: runs-on: ubuntu-latest + env: GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m" SONAR_JDK: "11" + strategy: matrix: java-version: [ 11, 15 ] + steps: - uses: actions/checkout@v2 with: fetch-depth: 0 + - name: Set up JDK ${{ matrix.java-version }} uses: actions/setup-java@v1 with: java-version: ${{ matrix.java-version }} + - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Cache SonarCloud packages if: matrix.java-version == env.SONAR_JDK uses: actions/cache@v1 @@ -28,6 +34,7 @@ jobs: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar + - name: Cache Gradle packages uses: actions/cache@v2 with: @@ -37,6 +44,7 @@ jobs: key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle-${{ matrix.java-version }}- + - name: Test with Gradle env: CI_NAME: "GitHub CI" @@ -51,12 +59,14 @@ jobs: TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} run: ./gradlew build check --stacktrace + - name: SonarCloud if: success() && matrix.java-version == env.SONAR_JDK env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: ./gradlew sonarqube + - name: Cleanup Gradle Cache run: | rm -f ~/.gradle/caches/modules-2/modules-2.lock diff --git a/build.gradle b/build.gradle index 500333a..5c5d0b4 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.5.10' id 'org.jetbrains.kotlin.kapt' version '1.5.10' - id 'org.sonarqube' version '3.2.0' + id 'org.sonarqube' version '3.3' id 'pmd' } @@ -19,6 +19,8 @@ final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' final def semverProcessor = "net.thauvin.erik:semver:1.2.0" +mainClassName = packageName + '.Mobibot' + ext.versions = [ log4j : '2.14.1', pmd : '6.35.0', @@ -84,10 +86,6 @@ kapt { } } -application { - mainClassName = packageName + '.Mobibot' -} - tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } @@ -129,7 +127,7 @@ clean { } run { - args('-d') + args('-h') } incrementBuildMeta { diff --git a/version.properties b/version.properties index 772f2ee..f04ec96 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat May 29 22:00:14 PDT 2021 -version.buildmeta=866 +#Mon May 31 14:55:18 PDT 2021 +version.buildmeta=870 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+866 +version.semver=0.8.0-beta+870 From 05ef252bf74ce86c6c1d28446b64513d430ad830 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 16 Jun 2021 16:17:18 -0700 Subject: [PATCH 535/842] Moved to irc.libera.chat. --- website/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/index.html b/website/index.html index 6dfd579..fe9468a 100644 --- a/website/index.html +++ b/website/index.html @@ -115,8 +115,8 @@ <h3>Using mobibot</h3> - <p>To use mobibot, simply <strong>join <a href="irc://irc.freenode.net/#mobitopia">#mobitopia</a></strong> on - <strong>irc.freenode.net</strong> and type:</p> + <p>To use mobibot, simply <strong>join <a href="irc://irc.libera.chat/#mobitopia">#mobitopia</a></strong> on + <strong>irc.libera.chat</strong> and type:</p> <p><code>mobibot: help</code></p> From fdd1fb7e4299dafc5ea254c3f0f8eb4bdc750a2f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 16 Jun 2021 21:58:11 -0700 Subject: [PATCH 536/842] Updated dependencies. --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 5c5d0b4..7f5e9cb 100644 --- a/build.gradle +++ b/build.gradle @@ -61,7 +61,7 @@ dependencies { implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.1' testImplementation 'org.testng:testng:7.4.0' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18435 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�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 B8dv}T delta 17845 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 X49Luk)baa85H9$5YCsTPT`SVRWMtMW diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0f80bbf..69a9715 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..744e882 100755 --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) From 7c2ad09ff1726d6fc5bfb09de0904b4e579e4241 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 18 Jun 2021 21:31:40 -0700 Subject: [PATCH 537/842] Removed checkstyle, using IDEA default code format. --- .idea/codeStyles/Project.xml | 10 +- .idea/inspectionProfiles/Project_Default.xml | 36 +++ .idea/jarRepositories.xml | 5 + .idea/misc.xml | 3 + build.gradle | 19 +- config/checkstyle/checkstyle.xml | 277 ------------------ .../net/thauvin/erik/mobibot/modules/War.java | 4 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 2 +- version.properties | 6 +- 9 files changed, 56 insertions(+), 306 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 config/checkstyle/checkstyle.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index e310056..3ad4dc5 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -9,15 +9,7 @@ <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </JetCodeStyleSettings> <codeStyleSettings language="kotlin"> - <option name="CALL_PARAMETERS_WRAP" value="5" /> - <option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> - <option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> - <option name="METHOD_PARAMETERS_WRAP" value="5" /> - <option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> - <option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> - <option name="EXTENDS_LIST_WRAP" value="1" /> - <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> - <option name="ASSIGNMENT_WRAP" value="1" /> + <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </codeStyleSettings> </code_scheme> </component> \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..f243f08 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,36 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="TOP_LEVEL_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="INNER_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="METHOD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> + </value> + </option> + <option name="FIELD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="IGNORE_DEPRECATED" value="false" /> + <option name="IGNORE_JAVADOC_PERIOD" value="true" /> + <option name="IGNORE_DUPLICATED_THROWS" value="false" /> + <option name="IGNORE_POINT_TO_ITSELF" value="false" /> + <option name="myAdditionalJavadocTags" value="created,created,created" /> + </inspection_tool> + </profile> +</component> \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 39b5ad2..8920634 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -26,5 +26,10 @@ <option name="name" value="maven" /> <option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" /> </remote-repository> + <remote-repository> + <option name="id" value="MavenLocal" /> + <option name="name" value="MavenLocal" /> + <option name="url" value="file:$MAVEN_REPOSITORY$/" /> + </remote-repository> </component> </project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0474152..1503b42 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> + <component name="EntryPointsManager"> + <pattern value="net.thauvin.erik.mobibot.modules.War" method="War" /> + </component> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="15" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 7f5e9cb..412ab78 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,5 @@ plugins { id 'application' - id 'checkstyle' id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.17.1' @@ -153,27 +152,19 @@ jacoco { } jacocoTestReport { - dependsOn(test) + dependsOn test reports { - html.enabled = true - xml.enabled = true - } -} - -tasks.withType(Checkstyle) { - reports { - xml.enabled = false - html.enabled = true + html.required = true + xml.required = true } } tasks.sonarqube { - dependsOn('jacocoTestReport') + dependsOn 'jacocoTestReport' } task copyToDeploy(type: Copy) { - from('properties') - from jar + from('properties', jar) into deployDir } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml deleted file mode 100644 index c5f6465..0000000 --- a/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,277 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE module PUBLIC - "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" - "https://checkstyle.org/dtds/configuration_1_3.dtd"> - -<!-- - Checkstyle configuration that checks the Google coding conventions from Google Java Style - that can be found at https://google.github.io/styleguide/javaguide.html. - - Checkstyle is very configurable. Be sure to read the documentation at - http://checkstyle.sf.net (or in your downloaded distribution). - - To completely disable a check, just comment it out or delete it from the file. - - Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov. - --> - -<module name="Checker"> - <property name="charset" value="UTF-8"/> - - <property name="severity" value="warning"/> - - <property name="fileExtensions" value="java, properties, xml"/> - <!-- Excludes all 'module-info.java' files --> - <!-- See https://checkstyle.org/config_filefilters.html --> - <module name="BeforeExecutionExclusionFileFilter"> - <property name="fileNamePattern" value="module\-info\.java$"/> - </module> - <!-- Checks for whitespace --> - <!-- See http://checkstyle.sf.net/config_whitespace.html --> - <module name="FileTabCharacter"> - <property name="eachLine" value="true"/> - </module> - - <module name="LineLength"> - <property name="max" value="120"/> - <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> - </module> - - <module name="SuppressWarningsFilter"/> - - <module name="SuppressWithPlainTextCommentFilter"> - <property name="offCommentFormat" value="CHECKSTYLE_OFF"/> - <property name="onCommentFormat" value="CHECKSTYLE_ON"/> - <property name="checkFormat" - value="^((?!(FileTabCharacterCheck)).)*$"/> - </module> - - <module name="TreeWalker"> - <module name="SuppressWarningsHolder"/> - <module name="SuppressionCommentFilter"> - <property name="offCommentFormat" value="CHECKSTYLE_OFF: ALMOST_ALL"/> - <property name="onCommentFormat" value="CHECKSTYLE_ON: ALMOST_ALL"/> - <property name="checkFormat" value="^((?!(EqualsHashCode)).)*$"/> - </module> - <module name="OuterTypeFilename"/> - <module name="IllegalTokenText"> - <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/> - <property name="format" - value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/> - <property name="message" - value="Consider using special escape sequence instead of octal value or Unicode escaped value."/> - </module> - <module name="AvoidEscapedUnicodeCharacters"> - <property name="allowEscapesForControlCharacters" value="true"/> - <property name="allowByTailComment" value="true"/> - <property name="allowNonPrintableEscapes" value="true"/> - </module> - <module name="AvoidStarImport"/> - <module name="OneTopLevelClass"/> - <module name="NoLineWrap"/> - <module name="EmptyBlock"> - <property name="option" value="TEXT"/> - <property name="tokens" - value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/> - </module> - <module name="NeedBraces"/> - <module name="LeftCurly"/> - <module name="RightCurly"> - <property name="id" value="RightCurlySame"/> - <property name="tokens" - value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, - LITERAL_DO"/> - </module> - <module name="RightCurly"> - <property name="id" value="RightCurlyAlone"/> - <property name="option" value="alone"/> - <property name="tokens" - value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, - INSTANCE_INIT"/> - </module> - <module name="WhitespaceAround"> - <property name="allowEmptyConstructors" value="true"/> - <property name="allowEmptyLambdas" value="true"/> - <property name="allowEmptyMethods" value="true"/> - <property name="allowEmptyTypes" value="true"/> - <property name="allowEmptyLoops" value="true"/> - <message key="ws.notFollowed" - value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> - <message key="ws.notPreceded" - value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/> - </module> - <module name="OneStatementPerLine"/> - <module name="MultipleVariableDeclarations"/> - <module name="ArrayTypeStyle"/> - <module name="MissingSwitchDefault"/> - <module name="FallThrough"/> - <module name="UpperEll"/> - <module name="ModifierOrder"/> - <module name="EmptyLineSeparator"> - <property name="allowNoEmptyLineBetweenFields" value="true"/> - </module> - <module name="SeparatorWrap"> - <property name="id" value="SeparatorWrapDot"/> - <property name="tokens" value="DOT"/> - <property name="option" value="nl"/> - </module> - <module name="SeparatorWrap"> - <property name="id" value="SeparatorWrapComma"/> - <property name="tokens" value="COMMA"/> - <property name="option" value="EOL"/> - </module> - <module name="SeparatorWrap"> - <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 --> - <property name="id" value="SeparatorWrapEllipsis"/> - <property name="tokens" value="ELLIPSIS"/> - <property name="option" value="EOL"/> - </module> - <module name="SeparatorWrap"> - <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 --> - <property name="id" value="SeparatorWrapArrayDeclarator"/> - <property name="tokens" value="ARRAY_DECLARATOR"/> - <property name="option" value="EOL"/> - </module> - <module name="SeparatorWrap"> - <property name="id" value="SeparatorWrapMethodRef"/> - <property name="tokens" value="METHOD_REF"/> - <property name="option" value="nl"/> - </module> - <module name="PackageName"> - <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> - <message key="name.invalidPattern" - value="Package name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="TypeName"> - <message key="name.invalidPattern" - value="Type name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="MemberName"> - <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/> - <message key="name.invalidPattern" - value="Member name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="ParameterName"> - <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> - <message key="name.invalidPattern" - value="Parameter name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="LambdaParameterName"> - <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> - <message key="name.invalidPattern" - value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="CatchParameterName"> - <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> - <message key="name.invalidPattern" - value="Catch parameter name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="LocalVariableName"> - <property name="tokens" value="VARIABLE_DEF"/> - <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> - <message key="name.invalidPattern" - value="Local variable name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="ClassTypeParameterName"> - <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> - <message key="name.invalidPattern" - value="Class type name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="MethodTypeParameterName"> - <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> - <message key="name.invalidPattern" - value="Method type name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="InterfaceTypeParameterName"> - <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> - <message key="name.invalidPattern" - value="Interface type name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="NoFinalizer"/> - <module name="GenericWhitespace"> - <message key="ws.followed" - value="GenericWhitespace ''{0}'' is followed by whitespace."/> - <message key="ws.preceded" - value="GenericWhitespace ''{0}'' is preceded with whitespace."/> - <message key="ws.illegalFollow" - value="GenericWhitespace ''{0}'' should followed by whitespace."/> - <message key="ws.notPreceded" - value="GenericWhitespace ''{0}'' is not preceded with whitespace."/> - </module> - <module name="Indentation"> - <property name="basicOffset" value="4"/> - <property name="braceAdjustment" value="0"/> - <property name="caseIndent" value="4"/> - <property name="throwsIndent" value="4"/> - <property name="lineWrappingIndentation" value="4"/> - <property name="arrayInitIndent" value="4"/> - </module> - <module name="AbbreviationAsWordInName"> - <property name="ignoreFinal" value="false"/> - <property name="allowedAbbreviationLength" value="1"/> - </module> - <module name="OverloadMethodsDeclarationOrder"/> - <module name="VariableDeclarationUsageDistance"/> - <module name="CustomImportOrder"> - <property name="customImportOrderRules" - value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/> - <property name="specialImportsRegExp" value="^javax\."/> - <property name="standardPackageRegExp" value="^java\."/> - <property name="sortImportsInGroupAlphabetically" value="true"/> - <property name="separateLineBetweenGroups" value="false"/> - </module> - <module name="MethodParamPad"/> - <module name="NoWhitespaceBefore"> - <property name="tokens" - value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/> - <property name="allowLineBreaks" value="true"/> - </module> - <module name="ParenPad"/> - <module name="OperatorWrap"> - <property name="option" value="NL"/> - <property name="tokens" - value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, - LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/> - </module> - <module name="AnnotationLocation"> - <property name="id" value="AnnotationLocationMostCases"/> - <property name="tokens" - value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/> - </module> - <module name="AnnotationLocation"> - <property name="id" value="AnnotationLocationVariables"/> - <property name="tokens" value="VARIABLE_DEF"/> - <property name="allowSamelineMultipleAnnotations" value="true"/> - </module> - <module name="NonEmptyAtclauseDescription"/> - <module name="JavadocTagContinuationIndentation"/> - <module name="SummaryJavadoc"> - <property name="forbiddenSummaryFragments" - value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/> - </module> - <module name="JavadocParagraph"/> - <module name="AtclauseOrder"> - <property name="tagOrder" value="@param, @return, @throws, @deprecated"/> - <property name="target" - value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/> - </module> - <module name="JavadocMethod"> - <property name="scope" value="public"/> - <property name="allowMissingParamTags" value="true"/> - <property name="allowMissingReturnTag" value="true"/> - <property name="allowedAnnotations" value="Override, Test"/> - </module> - <module name="MethodName"> - <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/> - <message key="name.invalidPattern" - value="Method name ''{0}'' must match pattern ''{1}''."/> - </module> - <module name="SingleLineJavadoc"> - <property name="ignoreInlineTags" value="false"/> - </module> - <module name="EmptyCatchBlock"> - <property name="exceptionVariableName" value="expected"/> - </module> - <!-- <module name="CommentsIndentation"/> --> - </module> -</module> diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 88e70f7..1f86130 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -52,8 +52,8 @@ public final class War extends AbstractModule { // War command private static final String WAR_CMD = "war"; // Deck of card - private static final String[] WAR_DECK = - {"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; + private static final String[] WAR_DECK = + {"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; // Suits for the deck of card private static final String[] WAR_SUITS = {"Hearts", "Spades", "Diamonds", "Clubs"}; diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 09441a4..f018be9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -130,7 +130,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { } post(message = msg, isDm = false) } catch (e: ModuleException) { - if (bot.logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e) + if (logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e) } }.start() removeEntry(index) diff --git a/version.properties b/version.properties index f04ec96..d03d290 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon May 31 14:55:18 PDT 2021 -version.buildmeta=870 +#Fri Jun 18 20:20:40 PDT 2021 +version.buildmeta=872 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+870 +version.semver=0.8.0-beta+872 From a8a45001255c902b08eede5504655371a6139a54 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 6 Jul 2021 17:08:41 -0700 Subject: [PATCH 538/842] Updated dependencies. --- .circleci/config.yml | 9 ++- .editorconfig | 2 +- .github/workflows/gradle.yml | 2 +- .gitignore | 73 ++++++++++--------- build.gradle | 16 ++-- deploy.sh | 13 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- properties/log4j2.xml | 28 +++---- .../net/thauvin/erik/mobibot/FeedReader.kt | 4 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 10 +-- .../net/thauvin/erik/mobibot/UtilsTest.kt | 8 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- version.properties | 6 +- 13 files changed, 90 insertions(+), 85 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 19da0e1..cd06643 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,7 +18,8 @@ defaults_gradle: &defaults_gradle name: Gradle Dependencies command: ./gradlew dependencies - save_cache: - paths: ~/.m2 + paths: + - ~/.m2 key: gradle-dependencies-{{ checksum "build.gradle" }} - run: name: Run All Checks @@ -49,7 +50,7 @@ jobs: workflows: version: 2 gradle: - jobs: - - build_gradle_jdk11 - - build_gradle_jdk15 + jobs: + - build_gradle_jdk11 + - build_gradle_jdk15 diff --git a/.editorconfig b/.editorconfig index a6971e1..1f808de 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ [*] -insert_final_newline=true +insert_final_newline = true diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 715e6fe..1bb63d7 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,6 +1,6 @@ name: gradle-ci -on: [push, pull_request, workflow_dispatch] +on: [ push, pull_request, workflow_dispatch ] jobs: build: diff --git a/.gitignore b/.gitignore index 4d04f45..2b959f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,44 +1,9 @@ -__pycache__ !.vscode/extensions.json !.vscode/launch.json !.vscode/settings.json !.vscode/tasks.json !gradle-wrapper.jar !properties/* -.classpath -.DS_Store -.gradle -.history -.idea_modules/ -.idea/**/contentModel.xml -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/dataSources/ -.idea/**/dbnavigator.xml -.idea/**/dictionaries -.idea/**/dynamic.xml -.idea/**/gradle.xml -.idea/**/libraries -.idea/**/mongoSettings.xml -.idea/**/shelf -.idea/**/sqlDataSources.xml -.idea/**/tasks.xml -.idea/**/uiDesigner.xml -.idea/**/usage.statistics.xml -.idea/**/workspace.xml -.idea/**/caches/build_file_checksums.ser -.idea/**/httpRequests -.idea/**/replstate.xml -.idea/**/shelf/ -.kobalt -.mtj.tmp/ -.mvn/timing.properties -.mvn/wrapper/maven-wrapper.jar -.nb-gradle -.project -.scannerwork -.settings -.vscode/* *.class *.code-workspace *.ctxt @@ -52,6 +17,43 @@ __pycache__ *.tar.gz *.war *.zip +.DS_Store +.classpath +.gradle +.history +.idea/**/caches/build_file_checksums.ser +.idea/**/contentModel.xml +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/dataSources/ +.idea/**/dbnavigator.xml +.idea/**/dictionaries +.idea/**/dynamic.xml +.idea/**/gradle.xml +.idea/**/httpRequests +.idea/**/libraries +.idea/**/mongoSettings.xml +.idea/**/replstate.xml +.idea/**/shelf +.idea/**/shelf/ +.idea/**/sonarlint* +.idea/**/sqlDataSources.xml +.idea/**/tasks.xml +.idea/**/uiDesigner.xml +.idea/**/usage.statistics.xml +.idea/**/workspace.xml +.idea_modules/ +.kobalt +.mtj.tmp/ +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.nb-gradle +.project +.scannerwork +.settings +.vscode/* +Thumbs.db +__pycache__ atlassian-ide-plugin.xml bin/ build/ @@ -85,5 +87,4 @@ project.properties release.properties target/ test-output -Thumbs.db venv diff --git a/build.gradle b/build.gradle index 412ab78..e87ec18 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ plugins { id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.10' - id 'org.jetbrains.kotlin.kapt' version '1.5.10' + id 'org.jetbrains.kotlin.jvm' version '1.5.20' + id 'org.jetbrains.kotlin.kapt' version '1.5.20' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -21,8 +21,8 @@ final def semverProcessor = "net.thauvin.erik:semver:1.2.0" mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j : '2.14.1', - pmd : '6.35.0', + log4j: '2.14.1', + pmd : '6.35.0', ] repositories { @@ -48,19 +48,19 @@ dependencies { implementation 'commons-net:commons-net:3.8.0' implementation 'org.apache.commons:commons-lang3:3.12.0' - implementation 'com.rometools:rome:1.15.0' + implementation 'com.rometools:rome:1.16.0' implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - + implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - + implementation 'org.json:json:20210307' implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' - testImplementation 'org.assertj:assertj-core:3.20.1' + testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'org.testng:testng:7.4.0' } diff --git a/deploy.sh b/deploy.sh index 9ac698d..11bbc7b 100755 --- a/deploy.sh +++ b/deploy.sh @@ -2,12 +2,11 @@ DEPLOYDIR=/home/erik/mobitopia/mobibot -if [ -f "deploy/mobibot.jar" ] -then - /bin/cp deploy/mobibot.jar $DEPLOYDIR - rm -rf $DEPLOYDIR/lib/*.jar - cp deploy/lib/*.jar $DEPLOYDIR/lib - chmod 755 $DEPLOYDIR/*.jar $DEPLOYDIR/lib/*.jar +if [ -f "deploy/mobibot.jar" ]; then + /bin/cp deploy/mobibot.jar $DEPLOYDIR + rm -rf $DEPLOYDIR/lib/*.jar + cp deploy/lib/*.jar $DEPLOYDIR/lib + chmod 755 $DEPLOYDIR/*.jar $DEPLOYDIR/lib/*.jar else - echo "mobibot.jar not found." + echo "mobibot.jar not found." fi diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 69a9715..05679dc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/properties/log4j2.xml b/properties/log4j2.xml index 6c658d9..2842592 100644 --- a/properties/log4j2.xml +++ b/properties/log4j2.xml @@ -1,16 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> - <Appenders> - <Console name="stderr" target="SYSTEM_ERR"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> - </Console> - </Appenders> - <Loggers> - <Logger name="net.thauvin.erik.mobibot" level="warn" additivity="false"> - <AppenderRef ref="stderr"/> - </Logger> - <Root level="error"> - <AppenderRef ref="stderr"/> - </Root> - </Loggers> -</Configuration> \ No newline at end of file + <Appenders> + <Console name="stderr" target="SYSTEM_ERR"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> + </Console> + </Appenders> + <Loggers> + <Logger name="net.thauvin.erik.mobibot" level="warn" additivity="false"> + <AppenderRef ref="stderr"/> + </Logger> + <Root level="error"> + <AppenderRef ref="stderr"/> + </Root> + </Loggers> +</Configuration> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 884ca78..5930ca2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -62,8 +62,8 @@ class FeedReader( send(sender, it) } } catch (e: FeedException) { - if (logger.isDebugEnabled) logger.debug("Unabled to parse the feed at $url", e) - send(sender, "An error has occured while parsing the feed: ${e.message}", false) + if (logger.isDebugEnabled) logger.debug("Unable to parse the feed at $url", e) + send(sender, "An error has occurred while parsing the feed: ${e.message}", false) } catch (e: IOException) { if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed at $url", e) send(sender, "An error has occurred while fetching the feed: ${e.message}", false) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index f55ff61..60895b3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -260,12 +260,10 @@ object Utils { days %= 30 val weeks = days / 7 days %= 7 - val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours( - TimeUnit.MILLISECONDS.toDays(uptime) - ) - val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes( - TimeUnit.MILLISECONDS.toHours(uptime) - ) + val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(uptime)) + val minutes = + TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(uptime)) + with(info) { if (years > 0) { append(years).append(" year ".plural(years, " years ")) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 2dd8dd5..953c2b6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -77,7 +77,10 @@ class UtilsTest { @Test fun testBold() { assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + // @TODO causes problems with Kotlin 1.5.20 + // assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(bold("test")).describedAs("bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) + } @Test @@ -94,6 +97,9 @@ class UtilsTest { ) assertThat(colorize(ascii, Colors.RED)).describedAs("colorize(red)") .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + // @TODO casues problems with Kotlin 1.5.20 + // assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)") + // .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo(Colors.NORMAL) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 073d089..eabd984 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -41,7 +41,7 @@ import org.testng.annotations.Test class JokeTest { @Test @Throws(ModuleException::class) - fun testRamdomJoke() { + fun testRandomJoke() { assertThat(randomJoke().msg.isNotEmpty()).describedAs("randomJoke() > 0").isTrue assertThat(randomJoke().msg).describedAs("randomJoke()").containsIgnoringCase("chuck") } diff --git a/version.properties b/version.properties index d03d290..89f3d69 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Jun 18 20:20:40 PDT 2021 -version.buildmeta=872 +#Tue Jul 06 14:30:07 PDT 2021 +version.buildmeta=885 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+872 +version.semver=0.8.0-beta+885 From 663f7d682aba5f6bce576575fc2b2c87ac6745e7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 6 Jul 2021 18:24:13 -0700 Subject: [PATCH 539/842] Reverted to Kotlin 1.5.10. --- build.gradle | 4 ++-- src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 8 +++----- version.properties | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index e87ec18..b933dac 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ plugins { id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.20' - id 'org.jetbrains.kotlin.kapt' version '1.5.20' + id 'org.jetbrains.kotlin.jvm' version '1.5.10' + id 'org.jetbrains.kotlin.kapt' version '1.5.10' id 'org.sonarqube' version '3.3' id 'pmd' } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 953c2b6..4978cbf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -77,8 +77,7 @@ class UtilsTest { @Test fun testBold() { assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - // @TODO causes problems with Kotlin 1.5.20 - // assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(bold("test")).describedAs("bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) } @@ -97,9 +96,8 @@ class UtilsTest { ) assertThat(colorize(ascii, Colors.RED)).describedAs("colorize(red)") .isEqualTo(Colors.RED + ascii + Colors.NORMAL) - // @TODO casues problems with Kotlin 1.5.20 - // assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)") - // .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)") + .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo(Colors.NORMAL) } diff --git a/version.properties b/version.properties index 89f3d69..d365de4 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Jul 06 14:30:07 PDT 2021 -version.buildmeta=885 +#Tue Jul 06 18:00:45 PDT 2021 +version.buildmeta=927 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+885 +version.semver=0.8.0-beta+927 From 5d90571843b194a901647b460616f72418ba37aa Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 8 Jul 2021 12:27:57 -0700 Subject: [PATCH 540/842] Switched back to Java 1.8 compatibility. --- build.gradle | 8 ++++---- version.properties | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index b933dac..9b77fb4 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ plugins { id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.10' - id 'org.jetbrains.kotlin.kapt' version '1.5.10' + id 'org.jetbrains.kotlin.jvm' version '1.5.20' + id 'org.jetbrains.kotlin.kapt' version '1.5.20' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -75,8 +75,8 @@ test { } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } kapt { diff --git a/version.properties b/version.properties index d365de4..a78c1d5 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Jul 06 18:00:45 PDT 2021 -version.buildmeta=927 +#Thu Jul 08 12:26:26 PDT 2021 +version.buildmeta=930 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+927 +version.semver=0.8.0-beta+930 From d8930a95204dbf16040716632755ae1d58cec93a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 01:14:06 -0700 Subject: [PATCH 541/842] Dependencies update. --- .circleci/config.yml | 5 ++--- .idea/misc.xml | 2 +- .idea/runConfigurations.xml | 10 ++++++++++ build.gradle | 15 +++++++-------- settings.gradle | 17 +++++++++++++++++ 5 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 .idea/runConfigurations.xml diff --git a/.circleci/config.yml b/.circleci/config.yml index cd06643..a7df501 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,7 +35,7 @@ jobs: <<: *defaults docker: - - image: cimg/openjdk:15.0.2 + - image: cimg/openjdk:15.0 <<: *defaults_gradle @@ -43,7 +43,7 @@ jobs: <<: *defaults docker: - - image: cimg/openjdk:11.0.10 + - image: cimg/openjdk:11.0 <<: *defaults_gradle @@ -53,4 +53,3 @@ workflows: jobs: - build_gradle_jdk11 - build_gradle_jdk15 - diff --git a/.idea/misc.xml b/.idea/misc.xml index 1503b42..d755e4c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ <pattern value="net.thauvin.erik.mobibot.modules.War" method="War" /> </component> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="15" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="16" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RunConfigurationProducerService"> + <option name="ignoredProducers"> + <set> + <option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" /> + </set> + </option> + </component> +</project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9b77fb4..ccdd746 100644 --- a/build.gradle +++ b/build.gradle @@ -2,12 +2,12 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.17.1' + id 'io.gitlab.arturbosch.detekt' version '1.18.0-RC2' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.20' - id 'org.jetbrains.kotlin.kapt' version '1.5.20' + id 'org.jetbrains.kotlin.jvm' version '1.5.21' + id 'org.jetbrains.kotlin.kapt' version '1.5.21' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -39,14 +39,13 @@ dependencies { compileOnly 'pircbot:pircbot:1.5.0:sources' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" implementation 'commons-cli:commons-cli:1.4' implementation 'commons-net:commons-net:3.8.0' - implementation 'org.apache.commons:commons-lang3:3.12.0' implementation 'com.rometools:rome:1.16.0' implementation 'com.squareup.okhttp3:okhttp:4.9.1' @@ -57,7 +56,7 @@ dependencies { implementation 'net.thauvin.erik:pinboard-poster:1.0.3' implementation 'org.json:json:20210307' - implementation 'org.jsoup:jsoup:1.13.1' + implementation 'org.jsoup:jsoup:1.14.1' implementation 'org.twitter4j:twitter4j-core:4.0.7' testImplementation 'org.assertj:assertj-core:3.20.2' @@ -75,8 +74,8 @@ test { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } kapt { diff --git a/settings.gradle b/settings.gradle index 48e3810..53bc4db 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,18 @@ +plugins { + id "com.gradle.enterprise" version "3.6.3" +} + +gradleEnterprise { + buildScan { + link("GitHub", "https://github.com/ethauvin/pinboard-poster/tree/master") + if (System.getenv("CI")) { + uploadInBackground = false + publishOnFailure() + tag "CI" + } + termsOfServiceUrl = "https://gradle.com/terms-of-service" + termsOfServiceAgree = "yes" + } +} + rootProject.name = 'mobibot' From 153529029092fc398262b7a0eeef85a2e5c70fed Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 01:19:31 -0700 Subject: [PATCH 542/842] Implemented appendIfMissing and replaceEach extension functions to remove dependencies on StringUtils. --- .../net/thauvin/erik/mobibot/Mobibot.kt | 9 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 79 ++++++------- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 9 +- .../erik/mobibot/modules/CurrencyConverter.kt | 3 +- .../erik/mobibot/modules/ModuleException.kt | 8 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 107 ++++++++++++++---- 7 files changed, 143 insertions(+), 74 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 0cb7100..c532b4b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -34,11 +34,11 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.PinboardUtils.addPin import net.thauvin.erik.mobibot.PinboardUtils.deletePin import net.thauvin.erik.mobibot.PinboardUtils.updatePin +import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.toDir import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AddLog @@ -95,6 +95,7 @@ import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger import org.jibble.pircbot.PircBot import java.io.BufferedOutputStream +import java.io.File import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.IOException @@ -586,7 +587,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) val channel = p.getProperty("channel") - val logsDir = p.getProperty("logs", ".").toDir() + val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) // Redirect stdout and stderr if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { @@ -656,14 +657,14 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert // Set the URLs weblogUrl = p.getProperty("weblog", "") - backlogsUrl = p.getProperty("backlogs", weblogUrl).toDir(true) + backlogsUrl = p.getProperty("backlogs", weblogUrl).appendIfMissing('/') // Load the current entries and backlogs, if any try { LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) if (logger.isDebugEnabled) logger.debug("Last feed: ${LinksMgr.startDate}") } catch (e: Exception) { - logger.error("An error occurred while loading the logs.", e) + if (logger.isErrorEnabled) logger.error("An error occurred while loading the logs.", e) } // Set the pinboard authentication diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 60895b3..a1545b1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -31,7 +31,6 @@ */ package net.thauvin.erik.mobibot -import org.apache.commons.lang3.StringUtils import org.jibble.pircbot.Colors import org.jsoup.Jsoup import java.io.BufferedReader @@ -56,6 +55,18 @@ import java.util.stream.Collectors object Utils { private val searchFlags = arrayOf("%c", "%n") + /** + * Appends a suffix to the end of the String if not present. + */ + @JvmStatic + fun String.appendIfMissing(suffix: Char) : String { + return if (this.last() != suffix) { + "$this${suffix}" + } else { + this + } + } + /** * Makes the given int bold. */ @@ -81,7 +92,7 @@ object Utils { @JvmStatic fun buildCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) - return StringUtils.replaceEach(text, searchFlags, replace) + return text.replaceEach(searchFlags, replace) } /** @@ -95,7 +106,7 @@ object Utils { */ @JvmStatic fun colorize(s: String?, color: String): String { - return if (s.isNullOrBlank()) { + return if (s.isNullOrEmpty()) { Colors.NORMAL } else if (Colors.BOLD == color || Colors.REVERSE == color) { color + s + color @@ -136,7 +147,8 @@ object Utils { @JvmStatic @JvmOverloads fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { - return (if (isIndent) " " else "").plus(if (isBold) bold(help) else help) + val s = if (isBold) bold(help) else help + return if (isIndent) s.prependIndent() else s } /** @@ -145,21 +157,16 @@ object Utils { @JvmStatic fun String.obfuscate(): String { return if (this.isNotBlank()) { - StringUtils.repeat('x', this.length) + "x".repeat(this.length) } else this } - /** - * Returns the plural form of a word, if count > 1. - */ - fun String.plural(count: Int, plural: String): String = this.plural(count.toLong(), plural) - /** * Returns the plural form of a word, if count > 1. */ @JvmStatic - fun String.plural(count: Long, plural: String): String { - return if (count > 1) plural else this + fun String.plural(count: Long): String { + return if (count > 1) "${this}s" else this } /** @@ -168,6 +175,20 @@ object Utils { @JvmStatic fun red(s: String?): String = colorize(s, Colors.RED) + /** + * Replaces all occurrences of Strings within another String. + */ + @JvmStatic + fun String.replaceEach(search: Array<out String>, replace: Array<out String>): String { + var result = this + if (search.size == replace.size) { + search.forEachIndexed { i, s -> + result = result.replace(s, replace[i]) + } + } + return result + } + /** * Makes the given string reverse color. */ @@ -180,26 +201,6 @@ object Utils { @JvmStatic fun today(): String = LocalDateTime.now().toIsoLocalDate() - /** - * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. - */ - @JvmStatic - fun String.toDir(isUrl: Boolean = false): String { - return if (isUrl) { - if (this.last() == '/') { - this - } else { - "$this/" - } - } else { - if (this.last() == File.separatorChar) { - this - } else { - this + File.separatorChar - } - } - } - /** * Converts a string to an int. */ @@ -266,21 +267,23 @@ object Utils { with(info) { if (years > 0) { - append(years).append(" year ".plural(years, " years ")) + append(years).append(" year".plural(years)).append(' ') } if (months > 0) { - append(weeks).append(" month ".plural(months, " months ")) + append(weeks).append(" month".plural(months)).append(' ') } if (weeks > 0) { - append(weeks).append(" week ".plural(weeks, " weeks ")) + append(weeks).append(" week".plural(weeks)).append(' ') } if (days > 0) { - append(days).append(" day ".plural(days, " days ")) + append(days).append(" day".plural(days)).append(' ') } if (hours > 0) { - append(hours).append(" hour ".plural(hours, " hours ")) + append(hours).append(" hour".plural(hours)).append(' ') } - append(minutes).append(" minute ".plural(minutes, " minutes")) + + append(minutes).append(" minute".plural(minutes)) + return toString() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 0792eaa..ddacd50 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -122,7 +122,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { helpFormat("%c $name <nick> <message>"), "To view queued and sent messages:", helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${bold(maxDays)}" + " day.".plural(maxDays, " days.") + "Messages are kept for ${bold(maxDays)}" + " day".plural(maxDays.toLong()) + '.' ) override val isOp: Boolean = false override val isPublic: Boolean = isEnabled() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index b87f777..1a8468f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.entries import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import net.thauvin.erik.mobibot.commands.links.LinksMgr -import org.apache.commons.lang3.StringUtils import java.io.Serializable import java.util.Calendar import java.util.Date @@ -144,9 +143,11 @@ class EntryLink : Serializable { * Returns true if a string is contained in the link, title, or nick. */ fun matches(match: String?): Boolean { - return (StringUtils.containsIgnoreCase(link, match) - || StringUtils.containsIgnoreCase(title, match) - || StringUtils.containsIgnoreCase(nick, match)) + return if (match.isNullOrEmpty()) { + false + } else { + link.contains(match, true) || title.contains(match, true) || nick.contains(match, true) + } } /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index b714e9a..54a3dc2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -40,7 +40,6 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage -import org.apache.commons.lang3.StringUtils import org.jdom2.JDOMException import org.jdom2.input.SAXBuilder import java.io.IOException @@ -193,7 +192,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { val rates = mutableListOf<String>() for ((key, value) in EXCHANGE_RATES) { @Suppress("MagicNumber") - rates.add(" $key: ${StringUtils.leftPad(value, 8)}") + rates.add(" $key: ${value.padStart(8)}") } return rates } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 48a0462..5318787 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.obfuscate -import org.apache.commons.lang3.StringUtils +import net.thauvin.erik.mobibot.Utils.replaceEach /** * The `ModuleException` class. @@ -70,11 +70,11 @@ class ModuleException : Exception { fun getSanitizedMessage(vararg sanitize: String): String { val obfuscate = sanitize.map { it.obfuscate() }.toTypedArray() return when { - cause != null -> { - cause.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate) + cause?.message != null -> { + cause.javaClass.name + ": " + cause.message!!.replaceEach(sanitize, obfuscate) } message != null -> { - message.javaClass.name + ": " + StringUtils.replaceEach(message, sanitize, obfuscate) + message.javaClass.name + ": " + message.replaceEach(sanitize, obfuscate) } else -> "" } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 4978cbf..15a19e4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -31,16 +31,21 @@ */ package net.thauvin.erik.mobibot +import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.cyan +import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.red +import net.thauvin.erik.mobibot.Utils.replaceEach import net.thauvin.erik.mobibot.Utils.reverseColor -import net.thauvin.erik.mobibot.Utils.toDir import net.thauvin.erik.mobibot.Utils.toIntOrDefault import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.toUtcDateTime @@ -48,7 +53,6 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.Utils.urlReader -import org.apache.commons.lang3.StringUtils import org.assertj.core.api.Assertions.assertThat import org.jibble.pircbot.Colors import org.testng.annotations.BeforeClass @@ -68,24 +72,47 @@ class UtilsTest { " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" private val cal = Calendar.getInstance() private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) + private val test = "This is a test." @BeforeClass fun setUp() { cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 } + @Test + fun testAppendIfMissing() { + val dir = "dir" + val sep = '/' + val url = "https://erik.thauvin.net" + assertThat(dir.appendIfMissing(File.separatorChar)).describedAs("appendIfMissing(dir)") + .isEqualTo(dir + File.separatorChar) + assertThat(url.appendIfMissing(sep)).describedAs("appendIfMissing(url)").isEqualTo("$url$sep") + assertThat("$url$sep".appendIfMissing(sep)).describedAs("appendIfMissing($url$sep)").isEqualTo("$url$sep") + } + @Test fun testBold() { assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(bold(2L)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(bold("test")).describedAs("bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) - } + @Test + fun testBuildCmdSyntax() { + val bot = "mobibot" + assertThat(buildCmdSyntax("%c $test %n $test", bot, false)).describedAs("public") + .isEqualTo("$bot: $test $bot $test") + assertThat(buildCmdSyntax("%c %n $test %c $test %n", bot, true)).describedAs("public") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + } + + @Test fun testCapitalise() { assertThat("test".capitalise()).describedAs("capitalize(test)").isEqualTo("Test") assertThat("Test".capitalise()).describedAs("capitalize(Test)").isEqualTo("Test") + assertThat(test.capitalise()).describedAs("capitalize($test)").isEqualTo(test) assertThat("".capitalise()).describedAs("capitalize()").isEqualTo("") } @@ -99,6 +126,8 @@ class UtilsTest { assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)") .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo(Colors.NORMAL) + assertThat(colorize("", Colors.RED)).describedAs("colorize()").isEqualTo(Colors.NORMAL) + } @Test @@ -106,13 +135,19 @@ class UtilsTest { assertThat(cyan(ascii)).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) } + @Test + fun testEncodeUrl() { + assertThat(encodeUrl("Hello Günter")).isEqualTo("Hello+G%C3%BCnter") + } + @Test fun testGetIntProperty() { val p = Properties() p["one"] = "1" - p["two"] = "foo" - assertThat(p.getIntProperty("one", 1)).describedAs("getIntProperty(one)").isEqualTo(1) + p["two"] = "two" + assertThat(p.getIntProperty("one", 9)).describedAs("getIntProperty(one)").isEqualTo(1) assertThat(p.getIntProperty("two", 2)).describedAs("getIntProperty(two)").isEqualTo(2) + assertThat(p.getIntProperty("foo", 3)).describedAs("getIntProperty(foo)").isEqualTo(3) } @Test @@ -120,6 +155,17 @@ class UtilsTest { assertThat(green(ascii)).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) } + @Test + fun testHelpFormat() { + assertThat(helpFormat(test, isBold = true, isIndent = false)).describedAs("bold") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + assertThat(helpFormat(test, isBold = false, isIndent = true)).describedAs("indent") + .isEqualTo(test.prependIndent()) + assertThat(helpFormat(test, isBold = true, isIndent = true)).describedAs("bold-indent") + .isEqualTo(colorize(test, Colors.BOLD).prependIndent()) + + } + @Test fun testIsoLocalDate() { assertThat(cal.time.toIsoLocalDate()).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17") @@ -129,7 +175,7 @@ class UtilsTest { @Test fun testObfuscate() { assertThat(ascii.obfuscate().length).describedAs("obfuscate is right length").isEqualTo(ascii.length) - assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) + assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo("x".repeat(ascii.length)) assertThat(" ".obfuscate()).describedAs("obfuscate(blank)").isEqualTo(" ") } @@ -137,10 +183,31 @@ class UtilsTest { fun testPlural() { val week = "week" val weeks = "weeks" - assertThat(week.plural(-1, weeks)).describedAs("plural(-1)").isEqualTo(week) - assertThat(week.plural(0, weeks)).describedAs("plural(0)").isEqualTo(week) - assertThat(week.plural(1, weeks)).describedAs("plural(1)").isEqualTo(week) - assertThat(week.plural(2, weeks)).describedAs("plural(2)").isEqualTo(weeks) + + for (i in -1..3) { + assertThat(week.plural(i.toLong())).describedAs("plural($i)").isEqualTo(if (i > 1) weeks else week) + } + } + + @Test + fun testReplaceEach() { + val search = arrayOf("one", "two", "three") + val replace = arrayOf("1", "2", "3") + assertThat(search.joinToString(",").replaceEach(search, replace)).describedAs("replaceEach(1,2,3") + .isEqualTo(replace.joinToString(",")) + + assertThat(test.replaceEach(search, replace)).describedAs("replaceEach(nothing)").isEqualTo(test) + + assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E"))).describedAs("replaceEach($test)") + .isEqualTo(test.replace("t", "").replace("e", "E")) + + assertThat(test.replaceEach(search, emptyArray())).describedAs("replaceEach(search, empty)") + .isEqualTo(test) + } + + @Test + fun testRed() { + assertThat(red(ascii)).isEqualTo(colorize(ascii, Colors.RED)) } @Test @@ -153,13 +220,6 @@ class UtilsTest { assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate()) } - @Test - fun testToDir() { - assertThat("dir".toDir(false)).describedAs("toDir(dir, false)").isEqualTo("dir" + File.separatorChar) - assertThat("https://erik.thauvin.net".toDir(true)).describedAs("toDir(erik.thauvin.net, true)") - .isEqualTo("https://erik.thauvin.net/") - } - @Test fun testToIntOrDefault() { assertThat("10".toIntOrDefault(1)).describedAs("toIntOrDefault(10, 1)").isEqualTo(10) @@ -175,16 +235,21 @@ class UtilsTest { @Test fun testUptime() { - assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(uptime(547800300076L)) + assertThat(uptime(547800300076L)).describedAs("full") + .isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes") + assertThat(uptime(2700000L)).describedAs("minutes").isEqualTo("45 minutes") + assertThat(uptime(24300000L)).describedAs("hours minutes").isEqualTo("6 hours 45 minutes") + assertThat(uptime(110700000L)).describedAs("days hours minutes").isEqualTo("1 day 6 hours 45 minutes") + assertThat(uptime(1320300000L)).describedAs("weeks days hours minutes") + .isEqualTo("2 weeks 1 day 6 hours 45 minutes") + assertThat(uptime(0L)).describedAs("0 minutes").isEqualTo("0 minute") } @Test @Throws(IOException::class) fun testUrlReader() { assertThat(urlReader(URL("https://postman-echo.com/status/200"))).describedAs("urlReader()") - .isEqualTo( - "{\"status\":200}" - ) + .isEqualTo("{\"status\":200}") } @Test From af9546e3d1abd3a461b7faf99fe631ac3f2a5a3d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 01:26:12 -0700 Subject: [PATCH 543/842] Cleanup. --- .../thauvin/erik/mobibot/commands/Ignore.kt | 7 +-- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 18 +++---- .../erik/mobibot/entries/EntriesMgr.kt | 4 +- .../erik/mobibot/modules/CryptoPrices.kt | 14 +++--- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 12 +++-- .../erik/mobibot/modules/StockQuote.kt | 47 ++++++++++++++----- .../thauvin/erik/mobibot/modules/Weather2.kt | 36 +++++++------- .../thauvin/erik/mobibot/modules/WorldTime.kt | 10 ++-- 10 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index ce4fd32..69e9688 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -54,11 +54,8 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { "To toggle your ignore status:", helpFormat("%c $name $me") ) - private val helpOp = listOf( - "To ignore a link posted to the channel:", - helpFormat("https://www.foo.bar " + bold("%n"), false), - "To add/remove nicks from the ignored list:", - helpFormat("%c $name <nick> [<nick> ...]") + private val helpOp = help.plus( + arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) ) override val isOp = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index b4495b3..2f956e2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -37,7 +37,7 @@ import net.thauvin.erik.mobibot.Utils.helpFormat class Nick(bot: Mobibot) : AbstractCommand(bot) { override val name = "nick" - override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <nick>")) + override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>")) override val isOp = true override val isPublic = true override val isVisible = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 4db1781..de83bdf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -51,15 +51,17 @@ class Users(bot: Mobibot) : AbstractCommand(bot) { isPrivate: Boolean ) { val nicks = mutableListOf<String>() - bot.getUsers(bot.channel).forEach { user -> - if (bot.isOp(user.nick)) { - nicks.add("@${user.nick}") - } else { - nicks.add(user.nick) + with(bot) { + getUsers(channel).forEach { user -> + if (isOp(user.nick)) { + nicks.add("@${user.nick}") + } else { + nicks.add(user.nick) + } } - } - @Suppress("MagicNumber") - bot.sendList(sender, nicks.sorted(), 8, isPrivate, isIndent = true) + @Suppress("MagicNumber") + sendList(sender, nicks.sorted(), 8, isPrivate, isIndent = true) + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 6f9ffff..6fe809b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -114,7 +114,9 @@ object EntriesMgr { output.output(rss, fw) } } else { - bot.logger.warn("Unable to generate the backlogs feed. No property configured.") + if (bot.logger.isErrorEnabled) { + bot.logger.warn("Unable to generate the backlogs feed. No property configured.") + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index c4b2fcd..db28550 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -87,11 +87,13 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(CRYPTO_CMD) - help.add("To retrieve a cryptocurrency's market price:") - help.add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) - help.add("For example:") - help.add(helpFormat("%c $CRYPTO_CMD BTC")) - help.add(helpFormat("%c $CRYPTO_CMD ETH EUR")) - help.add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) + with(help) { + add("To retrieve a cryptocurrency's market price:") + add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) + add("For example:") + add(helpFormat("%c $CRYPTO_CMD BTC")) + add(helpFormat("%c $CRYPTO_CMD ETH EUR")) + add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 46f788c..ec3a0e6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -75,7 +75,7 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { private fun roll(): Pair<Int, Int> { @Suppress("MagicNumber") - return Pair(Random.nextInt(1, 7), Random.nextInt(1, 7)) + return Random.nextInt(1, 7) to Random.nextInt(1, 7) } companion object { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index f2624ec..40b6fc0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -59,11 +59,13 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { * Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/). */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - try { - bot.send(cyan(randomJoke().msg)) - } catch (e: ModuleException) { - if (bot.logger.isWarnEnabled) bot.logger.warn(e.debugMessage, e) - bot.send(sender, e.message, isPrivate) + with(bot) { + try { + send(cyan(randomJoke().msg)) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + send(sender, e.message, isPrivate) + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 1459914..e00838d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -51,7 +51,7 @@ import java.net.URL */ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { /** - * Returns the specified stock quote from Alpha Avantage. + * Returns the specified stock quote from Alpha Vantage. */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { with(bot) { @@ -163,27 +163,52 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { add(ErrorMessage(INVALID_SYMBOL)) return messages } + add( PublicMessage( "Symbol: " + unescapeXml(quote.getString("01. symbol")) + " [" + unescapeXml(symbolInfo.getString("2. name")) + ']' ) ) - add(PublicMessage(" Price: " + unescapeXml(quote.getString("05. price")))) - add(PublicMessage(" Previous: " + unescapeXml(quote.getString("08. previous close")))) - add(NoticeMessage(" Open: " + unescapeXml(quote.getString("02. open")))) - add(NoticeMessage(" High: " + unescapeXml(quote.getString("03. high")))) - add(NoticeMessage(" Low: " + unescapeXml(quote.getString("04. low")))) - add(NoticeMessage(" Volume: " + unescapeXml(quote.getString("06. volume")))) + + @Suppress("MagicNumber") + val pad = 10 + add( - NoticeMessage( - " Latest: " + unescapeXml(quote.getString("07. latest trading day")) + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString("05. price")) ) ) + add( + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString("08. previous close")) + ) + ) + + val data = arrayOf( + "Open" to "02. open", + "High" to "03. high", + "Low" to "04. low", + "Volume" to "06. volume", + "Latest" to "07. latest trading day" + ) + + data.forEach { + add( + NoticeMessage( + "${it.first}:".padEnd(pad).prependIndent(), + unescapeXml(quote.getString(it.second)) + ) + ) + } + add( NoticeMessage( - " Change: " + unescapeXml(quote.getString("09. change")) + " [" - + unescapeXml(quote.getString("10. change percent")) + ']' + "Change:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString("09. change")) + + " [" + unescapeXml(quote.getString("10. change percent")) + ']' ) ) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index b1f7d3e..9ae9e50 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -56,18 +56,20 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { */ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { if (args.isNotBlank()) { - try { - val messages = getWeather(args, properties[OWM_API_KEY_PROP]) - if (messages[0].isError) { - helpResponse(sender, isPrivate) - } else { - for (msg in messages) { - bot.send(sender, msg) + with(bot) { + try { + val messages = getWeather(args, properties[OWM_API_KEY_PROP]) + if (messages[0].isError) { + helpResponse(sender, isPrivate) + } else { + for (msg in messages) { + send(sender, msg) + } } + } catch (e: ModuleException) { + if (logger.isDebugEnabled) logger.debug(e.debugMessage, e) + send(e.message) } - } catch (e: ModuleException) { - if (bot.logger.isDebugEnabled) bot.logger.debug(e.debugMessage, e) - bot.send(e.message) } } else { helpResponse(sender, isPrivate) @@ -174,7 +176,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } } } catch (e: APIException) { - throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e) + throw ModuleException("getWeather($query)", "A weather API error has occurred.", e) } catch (e: NullPointerException) { throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e) } @@ -195,11 +197,13 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { init { commands.add(WEATHER_CMD) - help.add("To display weather information:") - help.add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) - help.add("For example:") - help.add(helpFormat("%c $WEATHER_CMD paris, fr")) - help.add("The default ISO 3166 country code is ${bold("US")}. Zip codes supported in most countries.") + with(help) { + add("To display weather information:") + add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) + add("For example:") + add(helpFormat("%c $WEATHER_CMD paris, fr")) + add("The default ISO 3166 country code is ${bold("US")}. Zip codes supported in most countries.") + } initProperties(OWM_API_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 0837590..a9be03d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -211,10 +211,12 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { override val isPrivateMsgEnabled = true init { - help.add("To display a country's current date/time:") - help.add(helpFormat("%c $TIME_CMD [<country code>]")) - help.add("For a listing of the supported countries:") - help.add(helpFormat("%c $TIME_CMD")) + with(help) { + add("To display a country's current date/time:") + add(helpFormat("%c $TIME_CMD [<country code>]")) + add("For a listing of the supported countries:") + add(helpFormat("%c $TIME_CMD")) + } commands.add(TIME_CMD) } } From 04e88264cd730ec836d8927e096bfe124ea12c50 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 01:26:52 -0700 Subject: [PATCH 544/842] Cleaned up and added more tests. --- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 4 +-- .../net/thauvin/erik/mobibot/msg/Message.kt | 4 +++ .../thauvin/erik/mobibot/PinboardUtilsTest.kt | 20 +++++++++---- .../mobibot/commands/tell/TellMessageTest.kt | 2 ++ .../erik/mobibot/entries/EntryLinkTest.kt | 30 +++++++++++++++++++ .../mobibot/modules/ModuleExceptionTest.kt | 21 +++++++++---- .../erik/mobibot/modules/StockQuoteTest.kt | 4 ++- version.properties | 6 ++-- 9 files changed, 76 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index a1545b1..9c05336 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -106,7 +106,7 @@ object Utils { */ @JvmStatic fun colorize(s: String?, color: String): String { - return if (s.isNullOrEmpty()) { + return if (s.isNullOrBlank()) { Colors.NORMAL } else if (Colors.BOLD == color || Colors.REVERSE == color) { color + s + color diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index e00838d..333d9d4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -198,8 +198,8 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { data.forEach { add( NoticeMessage( - "${it.first}:".padEnd(pad).prependIndent(), - unescapeXml(quote.getString(it.second)) + "${it.first}:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString(it.second)) ) ) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 6cdf592..b62c1d9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -72,4 +72,8 @@ open class Message { this.isError = isError this.isPrivate = isPrivate } + + override fun toString(): String { + return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, msg='$msg')" + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index 7fcd726..d9c7354 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -51,11 +51,15 @@ class PinboardUtilsTest : LocalProperties() { val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) PinboardUtils.addPin(pinboard, ircServer, entry) - assertTrue(validatePin(apiToken, ircServer, entry.link), "addPin") - entry.link = "https://www.foo.com/" + assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin") + entry.link = "https://www.foo.com/" PinboardUtils.updatePin(pinboard, ircServer, url, entry) - assertTrue(validatePin(apiToken, ircServer, entry.link), "updatePin") + assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin") + + entry.title = "Foo Title" + PinboardUtils.updatePin(pinboard, ircServer, entry.link, entry) + assertTrue(validatePin(apiToken, url = entry.link, entry.title), "update title") PinboardUtils.deletePin(pinboard, entry) assertFalse(validatePin(apiToken, url = entry.link), "deletePin") @@ -67,7 +71,7 @@ class PinboardUtilsTest : LocalProperties() { assertTrue(d.toTimestamp().matches("[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z".toRegex())) } - private fun validatePin(apiToken: String, ircServer: String = "", url: String): Boolean { + private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { val response = Utils.urlReader( URL( "https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" @@ -75,6 +79,12 @@ class PinboardUtilsTest : LocalProperties() { ) ) - return response.contains(url) && response.contains(ircServer) + matches.forEach { + if (!response.contains(it)) { + return false + } + } + + return response.contains(url) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 4fdd2db..7f5de32 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -58,6 +58,8 @@ class TellMessageTest { assertThat(tellMessage.isMatch(sender)).describedAs("match sender").isTrue assertThat(tellMessage.isMatch(recipient)).describedAs("match recipient").isTrue assertThat(tellMessage.isMatch("foo")).describedAs("foo is no match").isFalse + tellMessage.isReceived = false + assertThat(tellMessage.receptionDate).describedAs("reception date not set").isEqualTo(LocalDateTime.MIN) tellMessage.isReceived = true assertThat(tellMessage.isReceived).describedAs("is received").isTrue assertThat(isValidDate(tellMessage.receptionDate)).describedAs("received is valid date/time").isTrue diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index b6a83dc..3af8f40 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -32,9 +32,11 @@ package net.thauvin.erik.mobibot.entries import com.rometools.rome.feed.synd.SyndCategory +import com.rometools.rome.feed.synd.SyndCategoryImpl import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test import java.security.SecureRandom +import java.util.Date /** * The `EntryUtilsTest` class. @@ -74,6 +76,29 @@ class EntryLinkTest { assertThat(entryLink.getComment(0).comment).describedAs("getComment(something)").isEqualTo("something") } + @Test + fun testConstructor() { + val tag = "test" + val tags = listOf(SyndCategoryImpl().apply { name = tag }) + val link = EntryLink("link", "title", "nick", "channel", Date(), tags) + assertThat(link.tags.size).describedAs("check tag size").isEqualTo(tags.size) + assertThat(link.tags[0].name).describedAs("check tag name").isEqualTo(tag) + assertThat(link.pinboardTags).describedAs("check pinboard tag").isEqualTo("nick,$tag") + } + + @Test + fun testMatches() { + assertThat(entryLink.matches("mobitopia")).describedAs("match mobitopia").isTrue + assertThat(entryLink.matches("skynx")).describedAs("match nick").isTrue + assertThat(entryLink.matches("www.mobitopia.org")).describedAs("match url").isTrue + assertThat(entryLink.matches("foo")).describedAs("match foo").isFalse + assertThat(entryLink.matches("")).describedAs("match empty").isFalse + assertThat(entryLink.matches(null)).describedAs("match null").isFalse + + + } + + @Test fun testTags() { val tags: List<SyndCategory> = entryLink.tags @@ -88,5 +113,10 @@ class EntryLinkTest { entryLink.setTags("-mobitopia") assertThat(entryLink.pinboardTags).describedAs("getPinboardTags()") .isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia") + val size = entryLink.tags.size + entryLink.setTags("") + assertThat(entryLink.tags.size).describedAs("empty tag").isEqualTo(size) + entryLink.setTags(" ") + assertThat(entryLink.tags.size).describedAs("blank tag").isEqualTo(size) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index c2d61e3..4e63def 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot.modules -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.DataProvider import org.testng.annotations.Test import java.io.IOException @@ -57,19 +57,30 @@ class ModuleExceptionTest { @Test(dataProvider = "dp") fun testGetDebugMessage(e: ModuleException) { - Assertions.assertThat(e.debugMessage).describedAs("get debug message").isEqualTo(debugMessage) + assertThat(e.debugMessage).describedAs("get debug message").isEqualTo(debugMessage) } @Test(dataProvider = "dp") fun testGetMessage(e: ModuleException) { - Assertions.assertThat(e.message).describedAs("get message").isEqualTo(message) + assertThat(e.message).describedAs("get message").isEqualTo(message) } @Test fun testGetSanitizedMessage() { val apiKey = "1234567890" - val e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) - Assertions.assertThat(e.getSanitizedMessage(apiKey)).describedAs("sanitized url") + var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) + assertThat(e.getSanitizedMessage(apiKey)).describedAs("sanitized url") .contains("xxxxxxxxxx").doesNotContain(apiKey) + + e = ModuleException(debugMessage, message, null) + assertThat(e.getSanitizedMessage(apiKey)).describedAs("no cause").contains(message) + + val msg: String? = null + e = ModuleException(debugMessage, msg, IOException(msg)) + assertThat(e.getSanitizedMessage(apiKey)).describedAs("no message").isEqualTo("") + + + e = ModuleException(msg, msg, IOException(apiKey)) + assertThat(e.getSanitizedMessage(apiKey)).describedAs("null message").doesNotContain(apiKey) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 60875cf..7f46935 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -49,7 +49,9 @@ class StockQuoteTest : LocalProperties() { val messages = getQuote("apple inc", apiKey) assertThat(messages).describedAs("response not empty").isNotEmpty assertThat(messages[0].msg).describedAs("same stock symbol").startsWith("Symbol: AAPL") - assertThat(messages[1].msg).describedAs("price label").startsWith(" Price: ") + assertThat(messages[1].msg).describedAs("price label").startsWith("Price: ".prependIndent()) + assertThat(messages[2].msg).describedAs("previous label").startsWith("Previous: ".prependIndent()) + assertThat(messages[3].msg).describedAs("open label").startsWith("Open: ".prependIndent()) try { getQuote("blahfoo", apiKey) } catch (e: ModuleException) { diff --git a/version.properties b/version.properties index a78c1d5..6cb4e53 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jul 08 12:26:26 PDT 2021 -version.buildmeta=930 +#Fri Jul 30 02:43:43 PDT 2021 +version.buildmeta=1111 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+930 +version.semver=0.8.0-beta+1111 From 00caee4bc1f3b006e18f112388bfee4dbae4139a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 03:25:45 -0700 Subject: [PATCH 545/842] Fixed colorize function. --- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 10 ++++++---- .../kotlin/net/thauvin/erik/mobibot/msg/Message.kt | 3 ++- src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 9 ++++++--- version.properties | 6 +++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 9c05336..e671380 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -31,10 +31,10 @@ */ package net.thauvin.erik.mobibot +import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR import org.jibble.pircbot.Colors import org.jsoup.Jsoup import java.io.BufferedReader -import java.io.File import java.io.IOException import java.io.InputStreamReader import java.net.URL @@ -59,7 +59,7 @@ object Utils { * Appends a suffix to the end of the String if not present. */ @JvmStatic - fun String.appendIfMissing(suffix: Char) : String { + fun String.appendIfMissing(suffix: Char): String { return if (this.last() != suffix) { "$this${suffix}" } else { @@ -106,8 +106,10 @@ object Utils { */ @JvmStatic fun colorize(s: String?, color: String): String { - return if (s.isNullOrBlank()) { - Colors.NORMAL + return if (s.isNullOrEmpty()) { + "" + } else if (color == DEFAULT_COLOR) { + s } else if (Colors.BOLD == color || Colors.REVERSE == color) { color + s + color } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index b62c1d9..c1ced5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -31,6 +31,7 @@ */ package net.thauvin.erik.mobibot.msg +import net.thauvin.erik.semver.Constants import org.jibble.pircbot.Colors /** @@ -38,7 +39,7 @@ import org.jibble.pircbot.Colors */ open class Message { companion object { - var DEFAULT_COLOR = Colors.NORMAL + var DEFAULT_COLOR = Constants.EMPTY } /** Message color. */ diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 15a19e4..905b32f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -53,6 +53,7 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.Utils.urlReader +import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR import org.assertj.core.api.Assertions.assertThat import org.jibble.pircbot.Colors import org.testng.annotations.BeforeClass @@ -125,9 +126,11 @@ class UtilsTest { .isEqualTo(Colors.RED + ascii + Colors.NORMAL) assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)") .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo(Colors.NORMAL) - assertThat(colorize("", Colors.RED)).describedAs("colorize()").isEqualTo(Colors.NORMAL) - + assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo("") + assertThat(colorize("", Colors.RED)).describedAs("colorize()").isEqualTo("") + assertThat(colorize(ascii, DEFAULT_COLOR)).describedAs("colorize(none)").isEqualTo(ascii) + assertThat(colorize(" ", Colors.NORMAL)).describedAs("colorize(blank)") + .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) } @Test diff --git a/version.properties b/version.properties index 6cb4e53..3ad6de0 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Jul 30 02:43:43 PDT 2021 -version.buildmeta=1111 +#Fri Jul 30 03:23:18 PDT 2021 +version.buildmeta=1115 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+1111 +version.semver=0.8.0-beta+1115 From d05499a76f0e401a130dd88e8053887b009b3db3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 12:23:52 -0700 Subject: [PATCH 546/842] Cleaned up and added test to messages. --- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 1 - .../net/thauvin/erik/mobibot/msg/Message.kt | 13 ++++- .../thauvin/erik/mobibot/msg/TestMessage.kt | 55 +++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 96ef549..741d557 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -39,6 +39,5 @@ class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAUL this.msg = msg this.color = color isError = true - isNotice = true } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index c1ced5f..b410663 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.msg import net.thauvin.erik.semver.Constants -import org.jibble.pircbot.Colors /** * The `Message` class. @@ -47,6 +46,10 @@ open class Message { /** Error flag. */ var isError = false + set(value) { + if (value) isNotice = value + field = value + } /** Notice flag. */ var isNotice = false @@ -66,7 +69,13 @@ open class Message { * Creates a new message. */ @JvmOverloads - constructor(msg: String, color: String = DEFAULT_COLOR, isNotice: Boolean, isError: Boolean, isPrivate: Boolean) { + constructor( + msg: String, + color: String = DEFAULT_COLOR, + isNotice: Boolean = false, + isError: Boolean = false, + isPrivate: Boolean = false + ) { this.msg = msg this.color = color this.isNotice = isNotice diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt new file mode 100644 index 0000000..6ab62c9 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt @@ -0,0 +1,55 @@ +/* + * TestMessage.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.msg + +import org.assertj.core.api.Assertions.assertThat +import org.testng.annotations.Test + +class TestMessage { + @Test + fun testConstructor() { + var msg = Message() + + msg.isError = true + assertThat(msg.isNotice).describedAs("message is notice").isTrue + + msg = Message("foo", isError = true) + assertThat(msg.isNotice).describedAs("message is notice too").isTrue + } + + @Test + fun testErrorMessage() { + val msg = ErrorMessage("foo") + assertThat(msg.isNotice).describedAs("error message is notice").isTrue + } +} From 1d15d669b1cb6f229d62b9358a32c824a2fa0e29 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 14:51:24 -0700 Subject: [PATCH 547/842] Fixed country code lookup and added more tests. --- .../thauvin/erik/mobibot/modules/Weather2.kt | 38 +++++++++----- .../erik/mobibot/modules/Weather2Test.kt | 51 ++++++++++++++++--- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 9ae9e50..ecdd1b1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -84,21 +84,28 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { // Weather command private const val WEATHER_CMD = "weather" - private fun getCountry(countryCode: String): Country { + + /** + * Converts and rounds temperature from °F to °C. + */ + fun ftoC(d: Double?): Pair<Int, Int> { + @Suppress("MagicNumber") + val c = (d!! - 32) * 5 / 9 + return d.roundToInt() to c.roundToInt() + } + + /** + * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. + */ + fun getCountry(countryCode: String): Country { for (c in Country.values()) { - if (c.name.equals(countryCode, ignoreCase = true)) { + if (c.value.equals(countryCode, ignoreCase = true)) { return c } } return Country.UNITED_STATES } - private fun getTemps(d: Double?): String { - @Suppress("MagicNumber") - val c = (d!! - 32) * 5 / 9 - return "${d.roundToInt()} °F, ${c.roundToInt()} °C" - } - /** * Retrieves the weather data. */ @@ -131,7 +138,8 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { with(cwd.mainData) { if (this != null) { if (hasTemp()) { - messages.add(PublicMessage("Temperature: ${getTemps(temp)}")) + val t = ftoC(temp) + messages.add(PublicMessage("Temperature: ${t.first}°F, ${t.second}°C")) } if (hasHumidity() && humidity != null) { messages.add(NoticeMessage("Humidity: ${(humidity!!).roundToInt()}%")) @@ -141,7 +149,8 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { if (cwd.hasWindData()) { with(cwd.windData) { if (this != null && hasSpeed() && speed != null) { - messages.add(NoticeMessage("Wind: ${wind(speed!!)}")) + val w = mphToKmh(speed!!) + messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h")) } } } @@ -176,7 +185,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } } } catch (e: APIException) { - throw ModuleException("getWeather($query)", "A weather API error has occurred.", e) + throw ModuleException("getWeather($query)", "A weather API error has occurred: ${e.message}", e) } catch (e: NullPointerException) { throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e) } @@ -188,10 +197,13 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { return messages } - private fun wind(w: Double): String { + /** + * Converts and rounds temperature from mph to km/h. + */ + fun mphToKmh(w: Double): Pair<Int, Int> { @Suppress("MagicNumber") val kmh = w * 1.60934 - return "${w.roundToInt()} mph, ${kmh.roundToInt()} km/h" + return w.roundToInt() to kmh.roundToInt() } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 23d9b16..aead144 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -32,29 +32,68 @@ package net.thauvin.erik.mobibot.modules import net.aksingh.owmjapis.api.APIException +import net.aksingh.owmjapis.core.OWM import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Weather2.Companion.OWM_API_KEY_PROP +import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC +import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather +import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test +import kotlin.random.Random /** * The `Weather2Test` class. */ class Weather2Test : LocalProperties() { + @Test + fun testFtoC() { + val t = ftoC(32.0) + assertThat(t.second).describedAs("32 °F is 0 °C").isEqualTo(0) + } + + @Test + fun testGetCountry() { + assertThat(getCountry("foo")).describedAs("not a country").isEqualTo(OWM.Country.UNITED_STATES) + assertThat(getCountry("fr")).describedAs("fr is france").isEqualTo(OWM.Country.FRANCE) + + val country = OWM.Country.values() + repeat(3) { + val rand = country[Random.nextInt(0, country.size - 1)] + assertThat(getCountry(rand.value)).describedAs(rand.name).isEqualTo(rand) + } + } + + @Test + fun testMphToKmh() { + val w = mphToKmh(0.62) + assertThat(w.second).describedAs("0.62 mph is 1 km/h").isEqualTo(1) + } + @Test @Throws(ModuleException::class) fun testWeather() { - var messages = getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP)) + var messages = getWeather("98204", getProperty(OWM_API_KEY_PROP)) assertThat(messages[0].msg).describedAs("is Everett").contains("Everett").contains("US") - assertThat(messages[messages.size - 1].msg).describedAs("is City Search").endsWith("98204%2CUS") - messages = getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP)) + assertThat(messages[messages.size - 1].msg).describedAs("is Everett zip code").endsWith("98204%2CUS") + + messages = getWeather("San Francisco", getProperty(OWM_API_KEY_PROP)) + assertThat(messages[0].msg).describedAs("is San Francisco").contains("San Francisco").contains("US") + assertThat(messages[messages.size - 1].msg).describedAs("is San Fran city code").endsWith("5391959") + + messages = getWeather("London, UK", getProperty(OWM_API_KEY_PROP)) assertThat(messages[0].msg).describedAs("is UK").contains("London").contains("UK") - assertThat(messages[messages.size - 1].msg).describedAs("is City Code").endsWith("4517009") - assertThatThrownBy { getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)) } - .describedAs("Montpellier not found").hasCauseInstanceOf(APIException::class.java) + assertThat(messages[messages.size - 1].msg).describedAs("is London city code").endsWith("4517009") + + assertThatThrownBy { getWeather("Foo, US", getProperty(OWM_API_KEY_PROP)) } + .describedAs("foo not found").hasCauseInstanceOf(APIException::class.java) assertThatThrownBy { getWeather("test", "") } .describedAs("no API key").isInstanceOf(ModuleException::class.java).hasNoCause() + assertThatThrownBy { getWeather("test", null) } + .describedAs("null API key").isInstanceOf(ModuleException::class.java).hasNoCause() + messages = getWeather("", "apikey") assertThat(messages[0].isError).describedAs("no query").isTrue } From 9f82690ec7f56ca95533bdb07876f9f644399eed Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 16:58:04 -0700 Subject: [PATCH 548/842] Sorted the currency rates. --- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 54a3dc2..ecf214d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -190,7 +190,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { @JvmStatic fun currencyRates(): List<String> { val rates = mutableListOf<String>() - for ((key, value) in EXCHANGE_RATES) { + for ((key, value) in EXCHANGE_RATES.toSortedMap()) { @Suppress("MagicNumber") rates.add(" $key: ${value.padStart(8)}") } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 3af8f40..7b4c159 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -83,7 +83,7 @@ class EntryLinkTest { val link = EntryLink("link", "title", "nick", "channel", Date(), tags) assertThat(link.tags.size).describedAs("check tag size").isEqualTo(tags.size) assertThat(link.tags[0].name).describedAs("check tag name").isEqualTo(tag) - assertThat(link.pinboardTags).describedAs("check pinboard tag").isEqualTo("nick,$tag") + assertThat(link.pinboardTags).describedAs("check pinboard tags").isEqualTo("nick,$tag") } @Test @@ -94,8 +94,6 @@ class EntryLinkTest { assertThat(entryLink.matches("foo")).describedAs("match foo").isFalse assertThat(entryLink.matches("")).describedAs("match empty").isFalse assertThat(entryLink.matches(null)).describedAs("match null").isFalse - - } From 6f297a0d6b40a9dbdf12c445fad7cde57bc86494 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 18:33:09 -0700 Subject: [PATCH 549/842] Fixed country code. --- .../net/thauvin/erik/mobibot/modules/Weather2.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index ecdd1b1..e3162f4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -122,19 +122,20 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { val argv = query.split(",") if (argv.size in 1..2) { val city = argv[0].trim() - val country: String = if (argv.size > 1 && argv[1].isNotBlank()) { + val code: String = if (argv.size > 1 && argv[1].isNotBlank()) { argv[1].trim() } else { "US" } try { + val country = getCountry(code) val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) { - owm.currentWeatherByZipCode(city.toInt(), getCountry(country)) + owm.currentWeatherByZipCode(city.toInt(), country) } else { - owm.currentWeatherByCityName(city, getCountry(country)) + owm.currentWeatherByCityName(city, country) } if (cwd.hasCityName()) { - messages.add(PublicMessage("City: ${cwd.cityName} [${country.uppercase()}]")) + messages.add(PublicMessage("City: ${cwd.cityName} [${country.value}]")) with(cwd.mainData) { if (this != null) { if (hasTemp()) { @@ -177,7 +178,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { messages.add( NoticeMessage( "https://openweathermap.org/find?q=" - + encodeUrl("$city,${country.uppercase()}"), + + encodeUrl("$city,${code.uppercase()}"), Colors.GREEN ) ) @@ -185,7 +186,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { } } } catch (e: APIException) { - throw ModuleException("getWeather($query)", "A weather API error has occurred: ${e.message}", e) + throw ModuleException("getWeather($query)", e.message, e) } catch (e: NullPointerException) { throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e) } From 79efd005d163ba48aa09b62390adcd3011f4f371 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 30 Jul 2021 18:33:56 -0700 Subject: [PATCH 550/842] Fixed me keyword use for ops. --- src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 69e9688..537b19c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -80,9 +80,9 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { isOp: Boolean, isPrivate: Boolean ) { - if (!isOp) { + val isMe = args.trim().equals(me, true) + if (isMe || !isOp) { val nick = sender.lowercase() - val isMe = args.lowercase().startsWith(me) ignoreNick(bot, nick, isMe, isPrivate) } else { ignoreOp(bot, sender, args, isPrivate) From aab28b59792d5863268d560b368d0368726bacc2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 31 Jul 2021 14:23:43 -0700 Subject: [PATCH 551/842] Added capitalizeWords extension function. --- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 6 ++++++ .../kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt | 8 +++++++- src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 9 +++++++++ .../net/thauvin/erik/mobibot/modules/Weather2Test.kt | 8 ++++---- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index e671380..986230a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -101,6 +101,12 @@ object Utils { @JvmStatic fun String.capitalise(): String = this.replaceFirstChar { it.uppercase() } + /** + * Capitalize words + */ + fun String.capitalizeWords(): String = split(" ").map { it.lowercase().capitalise() }.joinToString(" ") + + /** * Colorize a string. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index e3162f4..ef6cb1f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -38,6 +38,7 @@ import net.aksingh.owmjapis.model.CurrentWeather import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.capitalizeWords import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.msg.ErrorMessage @@ -135,7 +136,12 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { owm.currentWeatherByCityName(city, country) } if (cwd.hasCityName()) { - messages.add(PublicMessage("City: ${cwd.cityName} [${country.value}]")) + messages.add( + PublicMessage( + "City: ${cwd.cityName}, " + + country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" + ) + ) with(cwd.mainData) { if (this != null) { if (hasTemp()) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 905b32f..16434f2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -35,6 +35,7 @@ import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.capitalizeWords import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.encodeUrl @@ -117,6 +118,14 @@ class UtilsTest { assertThat("".capitalise()).describedAs("capitalize()").isEqualTo("") } + @Test + fun textCapitaliseWords() { + assertThat(test.capitalizeWords()).describedAs("captiatlizeWords(test)").isEqualTo("This Is A Test.") + assertThat("Already Capitalized".capitalizeWords()).describedAs("already capitalized") + .isEqualTo("Already Capitalized") + assertThat(" a test ".capitalizeWords()).describedAs("with spaces").isEqualTo(" A Test ") + } + @Test fun testColorize() { assertThat(colorize(ascii, Colors.REVERSE)).describedAs("colorize(reverse)").isEqualTo( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index aead144..0aea8d2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -76,16 +76,16 @@ class Weather2Test : LocalProperties() { @Throws(ModuleException::class) fun testWeather() { var messages = getWeather("98204", getProperty(OWM_API_KEY_PROP)) - assertThat(messages[0].msg).describedAs("is Everett").contains("Everett").contains("US") + assertThat(messages[0].msg).describedAs("is Everett").contains("Everett, United States").contains("US") assertThat(messages[messages.size - 1].msg).describedAs("is Everett zip code").endsWith("98204%2CUS") messages = getWeather("San Francisco", getProperty(OWM_API_KEY_PROP)) assertThat(messages[0].msg).describedAs("is San Francisco").contains("San Francisco").contains("US") assertThat(messages[messages.size - 1].msg).describedAs("is San Fran city code").endsWith("5391959") - messages = getWeather("London, UK", getProperty(OWM_API_KEY_PROP)) - assertThat(messages[0].msg).describedAs("is UK").contains("London").contains("UK") - assertThat(messages[messages.size - 1].msg).describedAs("is London city code").endsWith("4517009") + messages = getWeather("London, GB", getProperty(OWM_API_KEY_PROP)) + assertThat(messages[0].msg).describedAs("is UK").contains("London, United Kingdom").contains("GB") + assertThat(messages[messages.size - 1].msg).describedAs("is London city code").endsWith("2643743") assertThatThrownBy { getWeather("Foo, US", getProperty(OWM_API_KEY_PROP)) } .describedAs("foo not found").hasCauseInstanceOf(APIException::class.java) From abd34bde80b0e06593515d2e86632e8fe7338a1d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 31 Jul 2021 14:24:49 -0700 Subject: [PATCH 552/842] Added more countries. --- .../thauvin/erik/mobibot/modules/WorldTime.kt | 206 +++++++++++++++++- .../erik/mobibot/modules/WordTimeTest.kt | 16 +- 2 files changed, 211 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index a9be03d..8244e15 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -50,10 +50,10 @@ import java.util.Locale class WorldTime(bot: Mobibot) : AbstractModule(bot) { companion object { // Beats (Internet Time) keyword - private const val BEATS_KEYWORD = ".beats" + const val BEATS_KEYWORD = ".beats" // Supported countries - private var COUNTRIES_MAP: Map<String, String> + var COUNTRIES_MAP: Map<String, String> // The Time command private const val TIME_CMD = "time" @@ -95,85 +95,265 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { init { // Initialize the countries map val countries = mutableMapOf<String, String>() + countries["AD"] = "Europe/Andorra" countries["AE"] = "Asia/Dubai" countries["AF"] = "Asia/Kabul" + countries["AKDT"] = "America/Anchorage" + countries["AKST"] = "America/Anchorage" countries["AQ"] = "Antarctica/South_Pole" + countries["AR"] = "America/Argentina/Buenos_Aires" + countries["AS"] = "Pacific/Pago_Pago" countries["AT"] = "Europe/Vienna" countries["AU"] = "Australia/Sydney" - countries["AKST"] = "America/Anchorage" - countries["AKDT"] = "America/Anchorage" + countries["AX"] = "Europe/Mariehamn" + countries["AZ"] = "Asia/Baku" + countries["BA"] = "Europe/Sarajevo" + countries["BB"] = "America/Barbados" + countries["BD"] = "Asia/Dhaka" countries["BE"] = "Europe/Brussels" + countries["BEAT"] = BEATS_KEYWORD + countries["BF"] = "Africa/Ouagadougou" + countries["BG"] = "Europe/Sofia" + countries["BH"] = "Asia/Bahrain" + countries["BI"] = "Africa/Bujumbura" + countries["BJ"] = "Africa/Porto-Novo" + countries["BL"] = "America/St_Barthelemy" + countries["BM"] = "Atlantic/Bermuda" + countries["BMT"] = BEATS_KEYWORD + countries["BN"] = "Asia/Brunei" + countries["BQ"] = "America/Kralendijk" countries["BR"] = "America/Sao_Paulo" + countries["BS"] = "America/Toronto" + countries["BT"] = "Asia/Thimphu" + countries["BW"] = "Africa/Gaborone" + countries["BY"] = "Europe/Minsk" + countries["BZ"] = "America/Belize" countries["CA"] = "America/Montreal" + countries["CC"] = "Indian/Cocos" + countries["CD"] = "Africa/Kinshasa" countries["CDT"] = "America/Chicago" countries["CET"] = "CET" + countries["CF"] = "Africa/Bangui" + countries["CG"] = "Africa/Brazzaville" countries["CH"] = "Europe/Zurich" + countries["CI"] = "Africa/Abidjan" + countries["CK"] = "Pacific/Rarotonga" + countries["CL"] = "America/Santiago" countries["CN"] = "Asia/Shanghai" + countries["CO"] = "America/Bogota" + countries["CR"] = "America/Costa_Rica" countries["CST"] = "America/Chicago" countries["CU"] = "Cuba" + countries["CV"] = "Atlantic/Cape_Verde" + countries["CW"] = "America/Curacao" + countries["CX"] = "Indian/Christmas" + countries["CY"] = "Asia/Nicosia" + countries["CZ"] = "Europe/Prague" countries["DE"] = "Europe/Berlin" + countries["DJ"] = "Africa/Djibouti" countries["DK"] = "Europe/Copenhagen" + countries["DM"] = "America/Dominica" + countries["DO"] = "America/Santo_Domingo" + countries["DZ"] = "Africa/Algiers" + countries["EC"] = "Pacific/Galapagos" countries["EDT"] = "America/New_York" + countries["EE"] = "Europe/Tallinn" countries["EG"] = "Africa/Cairo" + countries["EH"] = "Africa/El_Aaiun" countries["ER"] = "Africa/Asmara" countries["ES"] = "Europe/Madrid" countries["EST"] = "America/New_York" + countries["ET"] = "Africa/Addis_Ababa" countries["FI"] = "Europe/Helsinki" + countries["FJ"] = "Pacific/Fiji" + countries["FK"] = "Atlantic/Stanley" + countries["FM"] = "Pacific/Port_Moresby" + countries["FO"] = "Atlantic/Faroe" countries["FR"] = "Europe/Paris" + countries["GA"] = "Asia/Tbilisi" countries["GB"] = "Europe/London" + countries["GD"] = "America/Grenada" + countries["GE"] = "Asia/Tbilisi" + countries["GF"] = "America/Cayenne" + countries["GG"] = "Europe/London" + countries["GH"] = "Africa/Accra" + countries["GI"] = "Europe/Gibraltar" + countries["GL"] = "America/Thule" + countries["GM"] = "Africa/Banjul" countries["GMT"] = "GMT" + countries["GN"] = "Africa/Conakry" + countries["GP"] = "America/Guadeloupe" + countries["GQ"] = "Africa/Malabo" countries["GR"] = "Europe/Athens" + countries["GS"] = "Atlantic/South_Georgia" + countries["GT"] = "America/Guatemala" + countries["GU"] = "Pacific/Guam" + countries["GW"] = "Africa/Bissau" + countries["GY"] = "America/Guyana" countries["HK"] = "Asia/Hong_Kong" + countries["HN"] = "America/Tegucigalpa" + countries["HR"] = "Europe/Zagreb" countries["HST"] = "Pacific/Honolulu" + countries["HT"] = "America/Port-au-Prince" + countries["HU"] = "Europe/Budapest" + countries["ID"] = "Asia/Jakarta" countries["IE"] = "Europe/Dublin" countries["IL"] = "Asia/Tel_Aviv" + countries["IM"] = "Europe/London" countries["IN"] = "Asia/Kolkata" countries["IQ"] = "Asia/Baghdad" countries["IR"] = "Asia/Tehran" countries["IS"] = "Atlantic/Reykjavik" countries["IT"] = "Europe/Rome" + countries["JE"] = "Europe/London" countries["JM"] = "Jamaica" + countries["JO"] = "Asia/Amman" countries["JP"] = "Asia/Tokyo" + countries["KE"] = "Africa/Nairobi" + countries["KG"] = "Asia/Bishkek" + countries["KH"] = "Asia/Phnom_Penh" + countries["KI"] = "Pacific/Tarawa" + countries["KM"] = "Indian/Comoro" + countries["KN"] = "America/St_Kitts" + countries["KP"] = "Asia/Pyongyang" + countries["KR"] = "Asia/Seoul" + countries["KW"] = "Asia/Riyadh" + countries["KY"] = "America/Cayman" + countries["KZ"] = "Asia/Oral" + countries["LA"] = "Asia/Vientiane" + countries["LB"] = "Asia/Beirut" + countries["LC"] = "America/St_Lucia" + countries["LI"] = "Europe/Vaduz" + countries["LK"] = "Asia/Colombo" + countries["LR"] = "Africa/Monrovia" + countries["LS"] = "Africa/Maseru" + countries["LT"] = "Europe/Vilnius" + countries["LU"] = "Europe/Luxembourg" + countries["LV"] = "Europe/Riga" countries["LY"] = "Africa/Tripoli" countries["MA"] = "Africa/Casablanca" + countries["MC"] = "Europe/Monaco" + countries["MD"] = "Europe/Chisinau" countries["MDT"] = "America/Denver" + countries["ME"] = "Europe/Podgorica" + countries["MF"] = "America/Marigot" + countries["MG"] = "Indian/Antananarivo" countries["MH"] = "Kwajalein" + countries["MK"] = "Europe/Skopje" + countries["ML"] = "Africa/Bamako" + countries["MM"] = "Asia/Yangon" + countries["MN"] = "Asia/Ulaanbaatar" + countries["MO"] = "Asia/Macau" + countries["MP"] = "Pacific/Saipan" countries["MQ"] = "America/Martinique" + countries["MR"] = "Africa/Nouakchott" + countries["MS"] = "America/Montserrat" countries["MST"] = "America/Denver" + countries["MT"] = "Europe/Malta" + countries["MU"] = "Indian/Mauritius" + countries["MV"] = "Indian/Maldives" + countries["MW"] = "Africa/Blantyre" countries["MX"] = "America/Mexico_City" + countries["MY"] = "Asia/Kuala_Lumpur" + countries["MZ"] = "Africa/Maputo" + countries["NA"] = "Africa/Windhoek" + countries["NC"] = "Pacific/Noumea" + countries["NE"] = "Africa/Niamey" + countries["NF"] = "Pacific/Norfolk" + countries["NG"] = "Africa/Lagos" + countries["NI"] = "America/Managua" countries["NL"] = "Europe/Amsterdam" countries["NO"] = "Europe/Oslo" countries["NP"] = "Asia/Kathmandu" + countries["NR"] = "Pacific/Nauru" + countries["NU"] = "Pacific/Niue" countries["NZ"] = "Pacific/Auckland" + countries["OM"] = "Asia/Muscat" + countries["PA"] = "America/Panama" countries["PDT"] = "America/Los_Angeles" + countries["PE"] = "America/Lima" + countries["PF"] = "Pacific/Tahiti" + countries["PG"] = "Pacific/Pohnpei" countries["PH"] = "Asia/Manila" countries["PK"] = "Asia/Karachi" countries["PL"] = "Europe/Warsaw" + countries["PM"] = "America/Miquelon" + countries["PN"] = "Pacific/Pitcairn" + countries["PR"] = "America/Puerto_Rico" + countries["PS"] = "Asia/Gaza" countries["PST"] = "America/Los_Angeles" countries["PT"] = "Europe/Lisbon" - countries["PR"] = "America/Puerto_Rico" + countries["PW"] = "Pacific/Palau" + countries["PY"] = "America/Asuncion" + countries["QA"] = "Asia/Qatar" + countries["RE"] = "Indian/Reunion" + countries["RO"] = "Europe/Bucharest" + countries["RS"] = "Europe/Belgrade" countries["RU"] = "Europe/Moscow" + countries["RW"] = "Africa/Kigali" + countries["SA"] = "Africa/Johannesburg" + countries["SB"] = "Pacific/Guadalcanal" + countries["SC"] = "Indian/Mahe" + countries["SD"] = "Africa/Khartoum" countries["SE"] = "Europe/Stockholm" countries["SG"] = "Asia/Singapore" + countries["SH"] = "Atlantic/St_Helena" + countries["SI"] = "Europe/Ljubljana" + countries["SJ"] = "Atlantic/Jan_Mayen" + countries["SK"] = "Europe/Bratislava" + countries["SL"] = "Africa/Freetown" + countries["SM"] = "Europe/San_Marino" + countries["SN"] = "Africa/Dakar" + countries["SO"] = "Africa/Mogadishu" + countries["SR"] = "America/Paramaribo" + countries["SS"] = "Africa/Juba" + countries["ST"] = "Africa/Sao_Tome" + countries["SV"] = "America/El_Salvador" + countries["SX"] = "America/Marigot" + countries["SY"] = "Asia/Damascus" + countries["SZ"] = "Africa/Mbabane" + countries["TC"] = "America/Grand_Turk" + countries["TD"] = "Africa/Ndjamena" + countries["TF"] = "Indian/Kerguelen" + countries["TG"] = "Africa/Lome" countries["TH"] = "Asia/Bangkok" + countries["TJ"] = "Asia/Dushanbe" + countries["TK"] = "Pacific/Fakaofo" + countries["TL"] = "Asia/Dili" countries["TM"] = "Asia/Ashgabat" countries["TN"] = "Africa/Tunis" + countries["TO"] = "Pacific/Tongatapu" countries["TR"] = "Europe/Istanbul" + countries["TT"] = "America/Port_of_Spain" + countries["TV"] = "Pacific/Funafuti" countries["TW"] = "Asia/Taipei" + countries["TZ"] = "Africa/Dar_es_Salaam" + countries["UA"] = "Europe/Kiev" + countries["UG"] = "Africa/Kampala" countries["UK"] = "Europe/London" + countries["UM"] = "Pacific/Johnston" countries["US"] = "America/New_York" countries["UTC"] = "UTC" + countries["UY"] = "America/Montevideo" + countries["UZ"] = "Asia/Tashkent" countries["VA"] = "Europe/Vatican" + countries["VC"] = "America/St_Vincent" countries["VE"] = "America/Caracas" + countries["VG"] = "America/Tortola" + countries["VI"] = "America/St_Johns" countries["VN"] = "Asia/Ho_Chi_Minh" + countries["VU"] = "Pacific/Efate" + countries["WF"] = "Pacific/Wallis" + countries["WS"] = "Pacific/Apia" + countries["YE"] = "Asia/Aden" + countries["YT"] = "Indian/Mayotte" countries["ZA"] = "Africa/Johannesburg" + countries["ZM"] = "Africa/Lusaka" countries["ZULU"] = "Zulu" - countries["INTERNET"] = BEATS_KEYWORD - countries["BEATS"] = BEATS_KEYWORD @Suppress("MagicNumber") ZoneId.getAvailableZoneIds().stream() .filter { tz: String -> - !tz.contains("/") && tz.length == 3 && !countries.containsKey(tz) + tz.length <= 3 && !countries.containsKey(tz) } .forEach { tz: String -> countries[tz] = tz @@ -192,7 +372,13 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { if (args.isEmpty()) { send(sender, "The supported countries/zones are: ", isPrivate) @Suppress("MagicNumber") - sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false) + sendList( + sender, + COUNTRIES_MAP.keys.sorted().stream().map { it.padEnd(4) }.toList(), + 14, + isPrivate = false, + isIndent = true + ) } else { val msg = time(args) if (isPrivate) { @@ -213,7 +399,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { init { with(help) { add("To display a country's current date/time:") - add(helpFormat("%c $TIME_CMD [<country code>]")) + add(helpFormat("%c $TIME_CMD <country code>")) add("For a listing of the supported countries:") add(helpFormat("%c $TIME_CMD")) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 73a2007..fa5dd90 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -32,9 +32,13 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD +import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test +import java.time.ZoneId +import java.time.zone.ZoneRulesException /** * The `WordTimeTest` class. @@ -44,6 +48,16 @@ class WordTimeTest { fun testTime() { assertThat(time("PST").msg).describedAs("PST").endsWith(bold("Los Angeles")) assertThat(time("BLAH").isError).describedAs("BLAH").isTrue - assertThat(time("BEATS").msg).describedAs("BEATS").contains("@") + assertThat(time("BEAT").msg).describedAs(BEATS_KEYWORD).endsWith(BEATS_KEYWORD).contains("@") + } + + @Test + @Throws(ZoneRulesException::class) + fun testCountries() { + COUNTRIES_MAP.toSortedMap().forEach { + if (it.value != BEATS_KEYWORD) { + ZoneId.of(it.value) + } + } } } From ef6c600dbcdff2f7ffca38cec7f613fd7f2ecb98 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 1 Aug 2021 00:52:20 -0700 Subject: [PATCH 553/842] Added dice & cards emoji/unicode to games. --- config/detekt/baseline.xml | 1 + .../net/thauvin/erik/mobibot/modules/War.java | 26 +++++++++++-------- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 4 +-- .../net/thauvin/erik/mobibot/modules/Dice.kt | 19 +++++++------- .../erik/mobibot/modules/RockPaperScissors.kt | 19 ++++++++------ 5 files changed, 38 insertions(+), 31 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 0b6f182..b92e51b 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -15,6 +15,7 @@ <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> + <ID>NestedBlockDepth:Calc.kt$Calc$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> <ID>NestedBlockDepth:CryptoPrices.kt$CryptoPrices$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 1f86130..2e83533 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -51,11 +51,17 @@ public final class War extends AbstractModule { private static final SecureRandom RANDOM = new SecureRandom(); // War command private static final String WAR_CMD = "war"; - // Deck of card - private static final String[] WAR_DECK = - {"Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; - // Suits for the deck of card - private static final String[] WAR_SUITS = {"Hearts", "Spades", "Diamonds", "Clubs"}; + + private static final String[] HEARTS = + {"🂱", "🂾", "🂽", "🂼", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲"}; + private static final String[] SPADES = + {"🂡", "🂮", "🂭", "🂬", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢"}; + private static final String[] DIAMONDS = + {"🃁", "🃎", "🃍", "🃌", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂"}; + private static final String[] CLUBS = + {"🃑", "🃞", "🃝", "🃜", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒"}; + + private static final String[][] DECK = {HEARTS, SPADES, DIAMONDS, CLUBS}; /** * The default constructor. @@ -81,13 +87,11 @@ public final class War extends AbstractModule { int y; while (true) { - i = RANDOM.nextInt(WAR_DECK.length); - y = RANDOM.nextInt(WAR_DECK.length); + i = RANDOM.nextInt(HEARTS.length); + y = RANDOM.nextInt(HEARTS.length); - getBot().send(sender + " drew the " + bold(WAR_DECK[i]) + " of " - + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); - getBot().action("drew the " + bold(WAR_DECK[y]) + " of " - + bold(WAR_SUITS[RANDOM.nextInt(WAR_SUITS.length)])); + getBot().send(sender + " drew: " + DECK[RANDOM.nextInt(DECK.length)][i]); + getBot().action("drew: " + DECK[RANDOM.nextInt(DECK.length)][y]); if (i != y) { break; diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 986230a..a16c01b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -99,12 +99,12 @@ object Utils { * Capitalize a string. */ @JvmStatic - fun String.capitalise(): String = this.replaceFirstChar { it.uppercase() } + fun String.capitalise(): String = this.lowercase().replaceFirstChar { it.uppercase() } /** * Capitalize words */ - fun String.capitalizeWords(): String = split(" ").map { it.lowercase().capitalise() }.joinToString(" ") + fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() } /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index ec3a0e6..57e78ca 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.helpFormat import kotlin.random.Random @@ -46,22 +45,20 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { args: String, isPrivate: Boolean ) { + val botRoll = roll() val roll = roll() - val playerRoll = roll() + val botTotal = botRoll.first + botRoll.second val total = roll.first + roll.second - val playerTotal = playerRoll.first + playerRoll.second with(bot) { send( channel, - "$sender rolled two dice: ${bold(playerRoll.first)} and ${bold(playerRoll.second)}" - + " for a total of ${bold(playerTotal)}", + "$sender rolled ${total}: ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]}", isPrivate ) action( - "rolled two dice: ${bold(roll.first)} and ${bold(roll.second)}" + - " for a total of ${bold(total)}" + "rolled ${botTotal}: ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]}" ) - when (winLoseOrTie(total, playerTotal)) { + when (winLoseOrTie(botTotal, total)) { Result.WIN -> action("wins.") Result.LOSE -> action("lost.") else -> action("tied.") @@ -74,14 +71,16 @@ class Dice(bot: Mobibot) : AbstractModule(bot) { } private fun roll(): Pair<Int, Int> { - @Suppress("MagicNumber") - return Random.nextInt(1, 7) to Random.nextInt(1, 7) + return Random.nextInt(1, DICE_FACES.size) to Random.nextInt(1, DICE_FACES.size) } companion object { // Dice command private const val DICE_CMD = "dice" + // Dice faces + private val DICE_FACES = arrayOf("", "⚀", "⚁", "⚂", "⚃", "⚄", "⚅") + fun winLoseOrTie(bot: Int, player: Int): Result { return when { bot > player -> { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index bfcbe23..33b1fce 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -33,10 +33,8 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.red import kotlin.random.Random @@ -67,19 +65,26 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { override fun beats(hand: Hands): Boolean { return hand == SCISSORS } + + override var emoji = "\u270A" }, PAPER("covers") { override fun beats(hand: Hands): Boolean { return hand == ROCK } + + override var emoji = "\u270B" }, SCISSORS("cuts") { override fun beats(hand: Hands): Boolean { return hand == PAPER } + + override var emoji = "\u270C" }; abstract fun beats(hand: Hands): Boolean + abstract var emoji: String } companion object { @@ -100,18 +105,16 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { val hand = Hands.valueOf(cmd.uppercase()) val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] with(bot) { + send("${hand.emoji} vs. ${botHand.emoji}") when { hand == botHand -> { - send("${green(hand.name)} vs. ${green(botHand.name)}") action("tied.") } hand.beats(botHand) -> { - send("${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)}") - action("lost.") + action("lost. ${hand.name.capitalise()} ${hand.action} ${botHand.name.lowercase()}.") } else -> { - send("${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)}") - action("wins.") + action("wins. ${botHand.name.capitalise()} ${botHand.action} ${hand.name.lowercase()}.") } } } From 2784af626b6db01646dc8e7112ec14d135687b86 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 1 Aug 2021 14:36:25 -0700 Subject: [PATCH 554/842] Added more zones. --- .../thauvin/erik/mobibot/modules/WorldTime.kt | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 8244e15..ee728e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -84,7 +84,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { "The current Internet Time is: ${bold(internetTime())} $BEATS_KEYWORD" } else { (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) - + bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' '))) + + bold(tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' '))) } } else { return ErrorMessage("Unsupported country/zone. Please try again.") @@ -98,13 +98,19 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["AD"] = "Europe/Andorra" countries["AE"] = "Asia/Dubai" countries["AF"] = "Asia/Kabul" + countries["AG"] = "America/Antigua" + countries["AI"] = "America/Anguilla" countries["AKDT"] = "America/Anchorage" countries["AKST"] = "America/Anchorage" + countries["AL"] = "Europe/Tirane" + countries["AM"] = "Asia/Yerevan" + countries["AO"] = "Africa/Luanda" countries["AQ"] = "Antarctica/South_Pole" countries["AR"] = "America/Argentina/Buenos_Aires" countries["AS"] = "Pacific/Pago_Pago" countries["AT"] = "Europe/Vienna" countries["AU"] = "Australia/Sydney" + countries["AW"] = "America/Aruba" countries["AX"] = "Europe/Mariehamn" countries["AZ"] = "Asia/Baku" countries["BA"] = "Europe/Sarajevo" @@ -121,9 +127,10 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["BM"] = "Atlantic/Bermuda" countries["BMT"] = BEATS_KEYWORD countries["BN"] = "Asia/Brunei" + countries["BO"] = "America/La_Paz" countries["BQ"] = "America/Kralendijk" countries["BR"] = "America/Sao_Paulo" - countries["BS"] = "America/Toronto" + countries["BS"] = "America/Nassau" countries["BT"] = "Asia/Thimphu" countries["BW"] = "Africa/Gaborone" countries["BY"] = "Europe/Minsk" @@ -139,6 +146,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["CI"] = "Africa/Abidjan" countries["CK"] = "Pacific/Rarotonga" countries["CL"] = "America/Santiago" + countries["CM"] = "Africa/Douala" countries["CN"] = "Asia/Shanghai" countries["CO"] = "America/Bogota" countries["CR"] = "America/Costa_Rica" @@ -167,15 +175,15 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["FI"] = "Europe/Helsinki" countries["FJ"] = "Pacific/Fiji" countries["FK"] = "Atlantic/Stanley" - countries["FM"] = "Pacific/Port_Moresby" + countries["FM"] = "Pacific/Yap" countries["FO"] = "Atlantic/Faroe" countries["FR"] = "Europe/Paris" - countries["GA"] = "Asia/Tbilisi" + countries["GA"] = "Africa/Libreville" countries["GB"] = "Europe/London" countries["GD"] = "America/Grenada" countries["GE"] = "Asia/Tbilisi" countries["GF"] = "America/Cayenne" - countries["GG"] = "Europe/London" + countries["GG"] = "Europe/Guernsey" countries["GH"] = "Africa/Accra" countries["GI"] = "Europe/Gibraltar" countries["GL"] = "America/Thule" @@ -199,13 +207,14 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["ID"] = "Asia/Jakarta" countries["IE"] = "Europe/Dublin" countries["IL"] = "Asia/Tel_Aviv" - countries["IM"] = "Europe/London" + countries["IM"] = "Europe/Isle_of_Man" countries["IN"] = "Asia/Kolkata" + countries["IO"] = "Indian/Chagos" countries["IQ"] = "Asia/Baghdad" countries["IR"] = "Asia/Tehran" countries["IS"] = "Atlantic/Reykjavik" countries["IT"] = "Europe/Rome" - countries["JE"] = "Europe/London" + countries["JE"] = "Europe/Jersey" countries["JM"] = "Jamaica" countries["JO"] = "Asia/Amman" countries["JP"] = "Asia/Tokyo" @@ -238,9 +247,9 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["ME"] = "Europe/Podgorica" countries["MF"] = "America/Marigot" countries["MG"] = "Indian/Antananarivo" - countries["MH"] = "Kwajalein" + countries["MH"] = "Pacific/Majuro" countries["MK"] = "Europe/Skopje" - countries["ML"] = "Africa/Bamako" + countries["ML"] = "Africa/Timbuktu" countries["MM"] = "Asia/Yangon" countries["MN"] = "Asia/Ulaanbaatar" countries["MO"] = "Asia/Macau" @@ -273,7 +282,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["PDT"] = "America/Los_Angeles" countries["PE"] = "America/Lima" countries["PF"] = "Pacific/Tahiti" - countries["PG"] = "Pacific/Pohnpei" + countries["PG"] = "Pacific/Port_Moresby" countries["PH"] = "Asia/Manila" countries["PK"] = "Asia/Karachi" countries["PL"] = "Europe/Warsaw" @@ -291,7 +300,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["RS"] = "Europe/Belgrade" countries["RU"] = "Europe/Moscow" countries["RW"] = "Africa/Kigali" - countries["SA"] = "Africa/Johannesburg" + countries["SA"] = "Asia/Riyadh" countries["SB"] = "Pacific/Guadalcanal" countries["SC"] = "Indian/Mahe" countries["SD"] = "Africa/Khartoum" @@ -309,7 +318,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["SS"] = "Africa/Juba" countries["ST"] = "Africa/Sao_Tome" countries["SV"] = "America/El_Salvador" - countries["SX"] = "America/Marigot" + countries["SX"] = "America/Lower_Princes" countries["SY"] = "Asia/Damascus" countries["SZ"] = "Africa/Mbabane" countries["TC"] = "America/Grand_Turk" @@ -331,7 +340,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["UA"] = "Europe/Kiev" countries["UG"] = "Africa/Kampala" countries["UK"] = "Europe/London" - countries["UM"] = "Pacific/Johnston" + countries["UM"] = "Pacific/Wake" countries["US"] = "America/New_York" countries["UTC"] = "UTC" countries["UY"] = "America/Montevideo" @@ -340,7 +349,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["VC"] = "America/St_Vincent" countries["VE"] = "America/Caracas" countries["VG"] = "America/Tortola" - countries["VI"] = "America/St_Johns" + countries["VI"] = "America/St_Thomas" countries["VN"] = "Asia/Ho_Chi_Minh" countries["VU"] = "Pacific/Efate" countries["WF"] = "Pacific/Wallis" @@ -350,6 +359,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { countries["ZA"] = "Africa/Johannesburg" countries["ZM"] = "Africa/Lusaka" countries["ZULU"] = "Zulu" + countries["ZW"] = "Africa/Harare" @Suppress("MagicNumber") ZoneId.getAvailableZoneIds().stream() .filter { tz: String -> From d11f32189e85b731441db4d5c985a423d9a31157 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 1 Aug 2021 23:23:14 -0700 Subject: [PATCH 555/842] Added separator parameter to sendList() function. --- .../kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 7 ++++--- .../net/thauvin/erik/mobibot/commands/AddLog.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../thauvin/erik/mobibot/PinboardUtilsTest.kt | 16 ++++++++-------- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index c532b4b..6e21c00 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -256,10 +256,10 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert isPrivate ) send(sender, "The commands are:", isPrivate) - sendList(sender, addons.names, 8, isPrivate, isBold = true, isIndent = true) + sendList(sender, addons.names, 8, isPrivate = isPrivate, isBold = true, isIndent = true) if (isOp) { send(sender, "The op commands are:", isPrivate) - sendList(sender, addons.ops, 8, isPrivate, isBold = true, isIndent = true) + sendList(sender, addons.ops, 8, isPrivate = isPrivate, isBold = true, isIndent = true) } } @@ -435,6 +435,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert nick: String, list: List<String>, maxPerLine: Int, + separator: String = " ", isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false @@ -444,7 +445,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert send( nick, helpFormat( - list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(" ", truncated = ""), + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), isBold, isIndent ), diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt index c0c0275..f13aa4d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt @@ -64,7 +64,7 @@ class AddLog(bot: Mobibot) : AbstractCommand(bot) { } } @Suppress("MagicNumber") - bot.sendList(sender, history, 4, isPrivate, isIndent = true) + bot.sendList(sender, history, 4, isPrivate = isPrivate, isIndent = true) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 537b19c..7ae45a2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -140,7 +140,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { if (ignored.size > 0) { bot.send(sender, "The following nicks are ignored:", isPrivate) @Suppress("MagicNumber") - bot.sendList(sender, ignored.sorted(), 8, isPrivate, isIndent = true) + bot.sendList(sender, ignored.sorted(), 8, isPrivate = isPrivate, isIndent = true) } else { bot.send(sender, "No one is currently ${bold("ignored")}.", isPrivate) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 7d2fc84..230539f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -59,7 +59,7 @@ class Info(bot: Mobibot?) : AbstractCommand(bot!!) { isPrivate: Boolean ) { with(bot) { - sendList(sender, allVersions, 1, isPrivate) + sendList(sender, allVersions, 1, isPrivate = isPrivate) val info = StringBuilder() info.append("Uptime: ") .append(uptime(ManagementFactory.getRuntimeMXBean().uptime)) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 2bd43c9..3bd2db1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -56,7 +56,7 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) { } else { send(sender, "The enabled modules are: ", isPrivate) @Suppress("MagicNumber") - sendList(sender, modulesNames, 7, isPrivate, isIndent = true) + sendList(sender, modulesNames, 7, isPrivate = isPrivate, isIndent = true) } } else { helpDefault(sender, isOp, isPrivate) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index de83bdf..82f5277 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -61,7 +61,7 @@ class Users(bot: Mobibot) : AbstractCommand(bot) { } @Suppress("MagicNumber") - sendList(sender, nicks.sorted(), 8, isPrivate, isIndent = true) + sendList(sender, nicks.sorted(), 8, isPrivate = isPrivate, isIndent = true) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 72acc6b..1712add 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -57,7 +57,7 @@ class Versions(bot: Mobibot) : AbstractCommand(bot) { isPrivate: Boolean ) { if (isOp) { - bot.sendList(sender, allVersions, 1, isPrivate) + bot.sendList(sender, allVersions, 1, isPrivate = isPrivate) } else { bot.helpDefault(sender, false, isPrivate) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index d9c7354..dbaf270 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -50,19 +50,19 @@ class PinboardUtilsTest : LocalProperties() { val ircServer = "irc.test.com" val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) - PinboardUtils.addPin(pinboard, ircServer, entry) - assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin") + assertTrue(PinboardUtils.addPin(pinboard, ircServer, entry), "addPin") + assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "validate add") entry.link = "https://www.foo.com/" - PinboardUtils.updatePin(pinboard, ircServer, url, entry) - assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin") + assertTrue(PinboardUtils.updatePin(pinboard, ircServer, url, entry), "updatePin") + assertTrue(validatePin(apiToken, url = entry.link, ircServer), "validate update") entry.title = "Foo Title" - PinboardUtils.updatePin(pinboard, ircServer, entry.link, entry) - assertTrue(validatePin(apiToken, url = entry.link, entry.title), "update title") + assertTrue(PinboardUtils.updatePin(pinboard, ircServer, entry.link, entry), "update title") + assertTrue(validatePin(apiToken, url = entry.link, entry.title), "validate title") - PinboardUtils.deletePin(pinboard, entry) - assertFalse(validatePin(apiToken, url = entry.link), "deletePin") + assertTrue(PinboardUtils.deletePin(pinboard, entry), "daletePin") + assertFalse(validatePin(apiToken, url = entry.link), "validate delete") } @Test From c7961c281709b44770473e686e3df4799a2a316d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 1 Aug 2021 23:23:44 -0700 Subject: [PATCH 556/842] Added currency formatting. --- .../erik/mobibot/modules/CurrencyConverter.kt | 25 +++++++++++-------- .../mobibot/modules/CurrencyConverterTest.kt | 9 ++++--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index ecf214d..e0b7088 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -31,7 +31,6 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.buildCmdSyntax @@ -45,10 +44,12 @@ import org.jdom2.input.SAXBuilder import java.io.IOException import java.net.URL import java.text.NumberFormat +import java.util.Currency +import java.util.Locale import javax.xml.XMLConstants /** - * The CurrentConverter module. + * The CurrencyConverter module. */ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { override fun commandResponse( @@ -87,9 +88,9 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { helpResponse(sender, isPrivate) } } else if (args.contains(CURRENCY_RATES_KEYWORD)) { - send(sender, "The currency rates for ${bold(pubDate)} are:", isPrivate) + send(sender, "The reference rates for ${bold(pubDate)} are:", isPrivate) @Suppress("MagicNumber") - sendList(sender, currencyRates(), 3, isPrivate, isIndent = true) + sendList(sender, currencyRates(), 3, " ", isPrivate, isIndent = true) } else { helpResponse(sender, isPrivate) } @@ -116,7 +117,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { ), isPrivate ) - send(sender, "For a listing of current rates:", isPrivate) + send(sender, "For a listing of current reference rates:", isPrivate) send( sender, helpFormat( @@ -126,7 +127,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { ) send(sender, "The supported currencies are: ", isPrivate) @Suppress("MagicNumber") - sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, isIndent = true) + sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate = isPrivate, isIndent = true) } } return true @@ -151,8 +152,11 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { // Last exchange rates table publication date private var pubDate = "" - private fun Double.formatCurrency(): String = - NumberFormat.getCurrencyInstance(Constants.LOCALE).format(this).substring(1) + private fun Double.formatCurrency(currency: String): String = + NumberFormat.getCurrencyInstance(Locale.getDefault(Locale.Category.FORMAT)).let { + it.currency = Currency.getInstance(currency) + it.format(this) + } /** * Converts from a currency to another. @@ -173,8 +177,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { val doubleFrom = EXCHANGE_RATES[to]!!.toDouble() val doubleTo = EXCHANGE_RATES[from]!!.toDouble() PublicMessage( - amt.formatCurrency() + " ${cmds[1].uppercase()} = " - + (amt * doubleTo / doubleFrom).formatCurrency() + " ${cmds[3].uppercase()}" + amt.formatCurrency(to) + " = " + (amt * doubleTo / doubleFrom).formatCurrency(from) ) } catch (e: NumberFormatException) { ErrorMessage("Let's try with some real numbers next time, okay?") @@ -192,7 +195,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { val rates = mutableListOf<String>() for ((key, value) in EXCHANGE_RATES.toSortedMap()) { @Suppress("MagicNumber") - rates.add(" $key: ${value.padStart(8)}") + rates.add("$key: ${value.padStart(8)}") } return rates } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 5d509d3..88515cc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -51,11 +51,14 @@ class CurrencyConverterTest { @Test fun testConvertCurrency() { assertThat(convertCurrency("100 USD to EUR").msg) - .describedAs("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR") + .describedAs("100 USD to EUR").matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}") assertThat(convertCurrency("100 USD to USD").msg).describedAs("100 USD to USD") .contains("You're kidding, right?") assertThat(convertCurrency("100 USD").msg).describedAs("100 USD").contains("Invalid query.") - assertThat(currencyRates().size).describedAs("currencyRates().size() == 33").isEqualTo(33) - assertThat(currencyRates()).describedAs("currencyRates().get(EUR)").contains(" EUR: 1") + val rates = currencyRates() + assertThat(rates.size).describedAs("currencyRates.size == 33").isEqualTo(33) + assertThat(rates).describedAs("currencyRates(EUR)").contains("EUR: 1") + assertThat(rates.stream().anyMatch { it.matches("USD: .*".toRegex()) }) + .describedAs("currencyRates(USD)").isTrue } } From d130957c6f2b111fa00b5cd068692f981f0ec259 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 24 Aug 2021 18:56:33 -0700 Subject: [PATCH 557/842] Dependencies updates. Kotlin 1.5.30. --- build.gradle | 26 +- config/detekt/baseline.xml | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 269 +++++++++++------- .../net/thauvin/erik/mobibot/modules/Calc.kt | 18 +- version.properties | 6 +- 6 files changed, 196 insertions(+), 127 deletions(-) diff --git a/build.gradle b/build.gradle index ccdd746..fdee3ae 100644 --- a/build.gradle +++ b/build.gradle @@ -2,12 +2,12 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.18.0-RC2' + id 'io.gitlab.arturbosch.detekt' version '1.18.0' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.21' - id 'org.jetbrains.kotlin.kapt' version '1.5.21' + id 'org.jetbrains.kotlin.jvm' version '1.5.30' + id 'org.jetbrains.kotlin.kapt' version '1.5.30' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -56,7 +56,7 @@ dependencies { implementation 'net.thauvin.erik:pinboard-poster:1.0.3' implementation 'org.json:json:20210307' - implementation 'org.jsoup:jsoup:1.14.1' + implementation 'org.jsoup:jsoup:1.14.2' implementation 'org.twitter4j:twitter4j-core:4.0.7' testImplementation 'org.assertj:assertj-core:3.20.2' @@ -78,6 +78,24 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +kotlin { + // Add kapt.use.worker.api=false to gradle.properties (JDK 16+) + // See: https://youtrack.jetbrains.com/issue/KT-45545 + kotlinDaemonJvmArgs = [ + "-Dfile.encoding=UTF-8", + "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + ] +} + kapt { arguments { arg('semver.project.dir', projectDir) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index b92e51b..c3f468f 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -12,7 +12,7 @@ <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> - <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> + <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, separator: String = " ", isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Calc.kt$Calc$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc..ffed3a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 744e882..1b6c787 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 11602e6..6202fdf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -49,14 +49,16 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { isPrivate: Boolean ) { if (args.isNotBlank()) { - try { - bot.send(calculate(args)) - } catch (e: IllegalArgumentException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Failed to calculate: $args", e) - bot.send("No idea. This is the kind of math I don't get.") - } catch (e: UnknownFunctionOrVariableException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to calculate: $args", e) - bot.send("No idea. I must've some form of Dyscalculia.") + with (bot) { + try { + send(calculate(args)) + } catch (e: IllegalArgumentException) { + if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e) + send("No idea. This is the kind of math I don't get.") + } catch (e: UnknownFunctionOrVariableException) { + if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e) + send("No idea. I must've some form of Dyscalculia.") + } } } else { helpResponse(sender, isPrivate) diff --git a/version.properties b/version.properties index 3ad6de0..50f350f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Jul 30 03:23:18 PDT 2021 -version.buildmeta=1115 +#Tue Aug 24 11:34:08 PDT 2021 +version.buildmeta=1366 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+1115 +version.semver=0.8.0-beta+1366 From 1db47a0b22bbc57175308bd5f32dc521b5deba4e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 24 Aug 2021 19:04:51 -0700 Subject: [PATCH 558/842] Fixed kapt daemon configuration for JDK < 16. --- build.gradle | 32 +++++++++++++++++--------------- version.properties | 6 +++--- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index fdee3ae..48c77cd 100644 --- a/build.gradle +++ b/build.gradle @@ -79,21 +79,23 @@ java { } kotlin { - // Add kapt.use.worker.api=false to gradle.properties (JDK 16+) - // See: https://youtrack.jetbrains.com/issue/KT-45545 - kotlinDaemonJvmArgs = [ - "-Dfile.encoding=UTF-8", - "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" - ] + if (JavaVersion.current() >= JavaVersion.VERSION_16) { + // Add kapt.use.worker.api=false to gradle.properties (JDK 16+) + // See: https://youtrack.jetbrains.com/issue/KT-45545 + kotlinDaemonJvmArgs = [ + "-Dfile.encoding=UTF-8", + "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + ] + } } kapt { diff --git a/version.properties b/version.properties index 50f350f..65f4e77 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Aug 24 11:34:08 PDT 2021 -version.buildmeta=1366 +#Tue Aug 24 19:02:49 PDT 2021 +version.buildmeta=1369 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+1366 +version.semver=0.8.0-beta+1369 From e0a51d21f9fcb5f228d743323c16a98ffd72e844 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 24 Aug 2021 20:41:58 -0700 Subject: [PATCH 559/842] Revert "Fixed kapt daemon configuration for JDK < 16." This reverts commit 1db47a0b22bbc57175308bd5f32dc521b5deba4e. --- build.gradle | 32 +++++++++++++++----------------- version.properties | 6 +++--- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 48c77cd..fdee3ae 100644 --- a/build.gradle +++ b/build.gradle @@ -79,23 +79,21 @@ java { } kotlin { - if (JavaVersion.current() >= JavaVersion.VERSION_16) { - // Add kapt.use.worker.api=false to gradle.properties (JDK 16+) - // See: https://youtrack.jetbrains.com/issue/KT-45545 - kotlinDaemonJvmArgs = [ - "-Dfile.encoding=UTF-8", - "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" - ] - } + // Add kapt.use.worker.api=false to gradle.properties (JDK 16+) + // See: https://youtrack.jetbrains.com/issue/KT-45545 + kotlinDaemonJvmArgs = [ + "-Dfile.encoding=UTF-8", + "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + ] } kapt { diff --git a/version.properties b/version.properties index 65f4e77..50f350f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Aug 24 19:02:49 PDT 2021 -version.buildmeta=1369 +#Tue Aug 24 11:34:08 PDT 2021 +version.buildmeta=1366 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+1369 +version.semver=0.8.0-beta+1366 From 6f575bf921c36b2d315552e9cca8c3b33e3b5980 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 24 Aug 2021 20:44:47 -0700 Subject: [PATCH 560/842] Fixed unnecessary JDK 16 only syntax. --- src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index ee728e7..75fc7f5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -384,7 +384,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { @Suppress("MagicNumber") sendList( sender, - COUNTRIES_MAP.keys.sorted().stream().map { it.padEnd(4) }.toList(), + COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, 14, isPrivate = false, isIndent = true From f4c6bf48fa81aeb9e52410e2fb75a9cd0af38f1f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 9 Sep 2021 08:21:32 -0700 Subject: [PATCH 561/842] Trim command before processing private message. --- src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 6e21c00..b7b7d1a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -351,7 +351,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert message: String ) { if (logger.isDebugEnabled) logger.debug(">>> $sender : $message") - val cmds = message.split(" ".toRegex(), 2) + val cmds = message.trim().split(" ".toRegex(), 2) val cmd = cmds[0].lowercase() val args = if (cmds.size > 1) { cmds[1].trim() From 25a0850b715c8386ae46340017a6878a97055a18 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 15 Sep 2021 13:03:01 -0700 Subject: [PATCH 562/842] Now using coroutines in ThreadedModule. --- .idea/misc.xml | 2 +- build.gradle | 5 +++-- .../kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt | 9 ++++++--- .../net/thauvin/erik/mobibot/modules/ThreadedModule.kt | 8 +++++++- version.properties | 6 +++--- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index d755e4c..1503b42 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ <pattern value="net.thauvin.erik.mobibot.modules.War" method="War" /> </component> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="16" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="15" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index fdee3ae..11e2a93 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.18.0' + id 'io.gitlab.arturbosch.detekt' version '1.18.1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' @@ -39,7 +39,7 @@ dependencies { compileOnly 'pircbot:pircbot:1.5.0:sources' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" @@ -97,6 +97,7 @@ kotlin { } kapt { + includeCompileClasspath = false arguments { arg('semver.project.dir', projectDir) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt index 019d152..d3408af 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -47,12 +48,14 @@ import java.util.Date * Handles posts to pinboard.in. */ object PinboardUtils { + private val dispatcher: CoroutineDispatcher = Dispatchers.IO + /** * Adds a pin. */ @JvmStatic fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking { - withContext(Dispatchers.Default) { + withContext(dispatcher) { poster.addPin( entry.link, entry.title, @@ -68,7 +71,7 @@ object PinboardUtils { */ @JvmStatic fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking { - withContext(Dispatchers.Default) { + withContext(dispatcher) { poster.deletePin(entry.link) } } @@ -78,7 +81,7 @@ object PinboardUtils { */ @JvmStatic fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { - withContext(Dispatchers.Default) { + withContext(dispatcher) { with(entry) { if (oldUrl != link) { poster.deletePin(oldUrl) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt index 44a5e24..e382ac5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt @@ -31,6 +31,8 @@ */ package net.thauvin.erik.mobibot.modules +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.Mobibot /** @@ -44,7 +46,11 @@ abstract class ThreadedModule(bot: Mobibot) : AbstractModule(bot) { isPrivate: Boolean ) { if (isEnabled && args.isNotEmpty()) { - Thread { run(sender, cmd, args, isPrivate) }.start() + runBlocking { + launch { + run(sender, cmd, args, isPrivate) + } + } } else { helpResponse(sender, isPrivate) } diff --git a/version.properties b/version.properties index 50f350f..d4b938a 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Aug 24 11:34:08 PDT 2021 -version.buildmeta=1366 +#Wed Sep 15 13:00:35 PDT 2021 +version.buildmeta=1390 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+1366 +version.semver=0.8.0-beta+1390 From 48b5c8a345b680bf1f6adbe4a70980aabf7b6ff5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 15 Sep 2021 16:38:36 -0700 Subject: [PATCH 563/842] Converted all Thread() to coroutines. --- .../net/thauvin/erik/mobibot/PinboardUtils.kt | 80 ++++++++++--------- .../erik/mobibot/commands/ChannelFeed.kt | 8 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 6 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 36 +++++---- .../thauvin/erik/mobibot/PinboardUtilsTest.kt | 8 +- version.properties | 6 +- 6 files changed, 81 insertions(+), 63 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt index d3408af..01bd1f1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -32,10 +32,8 @@ package net.thauvin.erik.mobibot -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.pinboard.PinboardPoster import java.time.ZoneId @@ -48,21 +46,21 @@ import java.util.Date * Handles posts to pinboard.in. */ object PinboardUtils { - private val dispatcher: CoroutineDispatcher = Dispatchers.IO - /** * Adds a pin. */ @JvmStatic - fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking { - withContext(dispatcher) { - poster.addPin( - entry.link, - entry.title, - entry.postedBy(ircServer), - entry.pinboardTags, - entry.date.toTimestamp() - ) + fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) { + runBlocking { + launch { + poster.addPin( + entry.link, + entry.title, + entry.postedBy(ircServer), + entry.pinboardTags, + entry.date.toTimestamp() + ) + } } } @@ -70,9 +68,11 @@ object PinboardUtils { * Deletes a pin. */ @JvmStatic - fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking { - withContext(dispatcher) { - poster.deletePin(entry.link) + fun deletePin(poster: PinboardPoster, entry: EntryLink) { + runBlocking { + launch { + poster.deletePin(entry.link) + } } } @@ -80,28 +80,30 @@ object PinboardUtils { * Updates a pin. */ @JvmStatic - fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking { - withContext(dispatcher) { - with(entry) { - if (oldUrl != link) { - poster.deletePin(oldUrl) - poster.addPin( - link, - title, - entry.postedBy(ircServer), - pinboardTags, - date.toTimestamp() - ) - } else { - poster.addPin( - link, - title, - entry.postedBy(ircServer), - pinboardTags, - date.toTimestamp(), - replace = true, - shared = true - ) + fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) { + runBlocking { + launch { + with(entry) { + if (oldUrl != link) { + poster.deletePin(oldUrl) + poster.addPin( + link, + title, + entry.postedBy(ircServer), + pinboardTags, + date.toTimestamp() + ) + } else { + poster.addPin( + link, + title, + entry.postedBy(ircServer), + pinboardTags, + date.toTimestamp(), + replace = true, + shared = true + ) + } } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 9c50932..f5756da 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -32,6 +32,8 @@ package net.thauvin.erik.mobibot.commands +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.FeedReader import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.helpFormat @@ -60,7 +62,11 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { ) { with(properties[FEED_PROP]) { if (!isNullOrBlank()) { - Thread(FeedReader(bot, sender, this)).start() + runBlocking { + launch { + FeedReader(bot, sender, this@with).run() + } + } } else { bot.send(sender, "There is no feed setup for this channel.", false) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 40b6fc0..d3e0ca8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -31,6 +31,8 @@ */ package net.thauvin.erik.mobibot.modules +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.helpFormat @@ -52,7 +54,9 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) { args: String, isPrivate: Boolean ) { - Thread { run(sender, cmd, args, isPrivate) }.start() + runBlocking { + launch { run(sender, cmd, args, isPrivate) } + } } /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index f018be9..17f2557 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -31,6 +31,8 @@ */ package net.thauvin.erik.mobibot.modules +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.TwitterTimer @@ -87,14 +89,16 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { fun notification(msg: String) { with(bot) { if (isEnabled && !handle.isNullOrBlank()) { - Thread { - try { - post(message = msg, isDm = true) - if (logger.isDebugEnabled) logger.debug("Notified @$handle: $msg") - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn("Failed to notify @$handle: $msg", e) + runBlocking { + launch { + try { + post(message = msg, isDm = true) + if (logger.isDebugEnabled) logger.debug("Notified @$handle: $msg") + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn("Failed to notify @$handle: $msg", e) + } } - }.start() + } } } } @@ -123,16 +127,18 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { if (isAutoPost && hasEntry(index) && LinksMgr.entries.size >= index) { val entry = LinksMgr.entries[index] val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel" - Thread { - try { - if (logger.isDebugEnabled) { - logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkCmd(index)) + runBlocking { + launch { + try { + if (logger.isDebugEnabled) { + logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkCmd(index)) + } + post(message = msg, isDm = false) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e) } - post(message = msg, isDm = false) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e) } - }.start() + } removeEntry(index) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt index dbaf270..93ff940 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt @@ -50,18 +50,18 @@ class PinboardUtilsTest : LocalProperties() { val ircServer = "irc.test.com" val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) - assertTrue(PinboardUtils.addPin(pinboard, ircServer, entry), "addPin") + PinboardUtils.addPin(pinboard, ircServer, entry) assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "validate add") entry.link = "https://www.foo.com/" - assertTrue(PinboardUtils.updatePin(pinboard, ircServer, url, entry), "updatePin") + PinboardUtils.updatePin(pinboard, ircServer, url, entry) assertTrue(validatePin(apiToken, url = entry.link, ircServer), "validate update") entry.title = "Foo Title" - assertTrue(PinboardUtils.updatePin(pinboard, ircServer, entry.link, entry), "update title") + PinboardUtils.updatePin(pinboard, ircServer, entry.link, entry) assertTrue(validatePin(apiToken, url = entry.link, entry.title), "validate title") - assertTrue(PinboardUtils.deletePin(pinboard, entry), "daletePin") + PinboardUtils.deletePin(pinboard, entry) assertFalse(validatePin(apiToken, url = entry.link), "validate delete") } diff --git a/version.properties b/version.properties index d4b938a..772b346 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Sep 15 13:00:35 PDT 2021 -version.buildmeta=1390 +#Wed Sep 15 17:27:25 PDT 2021 +version.buildmeta=1400 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+1390 +version.semver=0.8.0-beta+1400 From d7bdcd66d0d3b53cc00b110c8c3c271bbe9816de Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 4 Oct 2021 14:34:38 -0700 Subject: [PATCH 564/842] Update to Kotlin 1.5.31. --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 11e2a93..9cf5097 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ plugins { id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.30' - id 'org.jetbrains.kotlin.kapt' version '1.5.30' + id 'org.jetbrains.kotlin.jvm' version '1.5.31' + id 'org.jetbrains.kotlin.kapt' version '1.5.31' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -56,10 +56,10 @@ dependencies { implementation 'net.thauvin.erik:pinboard-poster:1.0.3' implementation 'org.json:json:20210307' - implementation 'org.jsoup:jsoup:1.14.2' + implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.twitter4j:twitter4j-core:4.0.7' - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation 'org.testng:testng:7.4.0' } From 22bf1cfed504fb3f9332e6b5f7c06837c50737a8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Oct 2021 13:02:57 -0700 Subject: [PATCH 565/842] Clean up. --- src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt | 7 ------- src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 11 ++++------- .../kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt | 4 ++-- .../kotlin/net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 4b93ac1..2ae420d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -31,8 +31,6 @@ */ package net.thauvin.erik.mobibot -import java.util.Locale - /** * The `Constants`. */ @@ -87,11 +85,6 @@ object Constants { */ const val LINK_CMD = "L" - /** - * Default locale. - */ - val LOCALE: Locale = Locale.getDefault() - /** * The empty title string. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index b7b7d1a..6bfbe8a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -31,9 +31,6 @@ */ package net.thauvin.erik.mobibot -import net.thauvin.erik.mobibot.PinboardUtils.addPin -import net.thauvin.erik.mobibot.PinboardUtils.deletePin -import net.thauvin.erik.mobibot.PinboardUtils.updatePin import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.colorize @@ -196,7 +193,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert */ fun addPin(entry: EntryLink) { if (isPinboardEnabled) { - addPin(pinboard, ircServer, entry) + PinboardUtils.addPin(pinboard, ircServer, entry) } } @@ -236,7 +233,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert */ fun deletePin(index: Int, entry: EntryLink) { if (isPinboardEnabled) { - deletePin(pinboard, entry) + PinboardUtils.deletePin(pinboard, entry) } if (twitter.isAutoPost) { twitter.removeEntry(index) @@ -506,7 +503,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert */ fun updatePin(oldUrl: String, entry: EntryLink) { if (isPinboardEnabled) { - updatePin(pinboard, ircServer, oldUrl, entry) + PinboardUtils.updatePin(pinboard, ircServer, oldUrl, entry) } } @@ -526,7 +523,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert */ @JvmStatic fun main(args: Array<String>) { - // Setup the command line options + // Set up the command line options val options = Options() .addOption( Constants.HELP_ARG.substring(0, 1), diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt index 01bd1f1..2d91b64 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt @@ -68,7 +68,7 @@ object PinboardUtils { * Deletes a pin. */ @JvmStatic - fun deletePin(poster: PinboardPoster, entry: EntryLink) { + fun deletePin(poster: PinboardPoster, entry: EntryLink) { runBlocking { launch { poster.deletePin(entry.link) @@ -80,7 +80,7 @@ object PinboardUtils { * Updates a pin. */ @JvmStatic - fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) { + fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) { runBlocking { launch { with(entry) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 6202fdf..5ff5c73 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -49,7 +49,7 @@ class Calc(bot: Mobibot) : AbstractModule(bot) { isPrivate: Boolean ) { if (args.isNotBlank()) { - with (bot) { + with(bot) { try { send(calculate(args)) } catch (e: IllegalArgumentException) { From 384331b287f469d82ffe371e7e71e75c2adb623e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Oct 2021 13:03:38 -0700 Subject: [PATCH 566/842] Upgraded to Kotlin 1.6.0-RC. --- build.gradle | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 9cf5097..3207f2a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ plugins { id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.5.31' - id 'org.jetbrains.kotlin.kapt' version '1.5.31' + id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC' + id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -78,24 +78,6 @@ java { targetCompatibility = JavaVersion.VERSION_11 } -kotlin { - // Add kapt.use.worker.api=false to gradle.properties (JDK 16+) - // See: https://youtrack.jetbrains.com/issue/KT-45545 - kotlinDaemonJvmArgs = [ - "-Dfile.encoding=UTF-8", - "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" - ] -} - kapt { includeCompileClasspath = false arguments { From 0d144d4b103094d45fde87b6279ace398574a620 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Oct 2021 13:16:59 -0700 Subject: [PATCH 567/842] Added Sanitize function. --- .../erik/mobibot/modules/ModuleException.kt | 19 ------- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 5 +- .../net/thauvin/erik/mobibot/Sanitize.kt | 56 +++++++++++++++++++ .../erik/mobibot/modules/GoogleSearchTest.kt | 5 +- .../mobibot/modules/ModuleExceptionTest.kt | 11 ++-- .../erik/mobibot/modules/StockQuoteTest.kt | 3 +- 7 files changed, 70 insertions(+), 31 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 5318787..71c5004 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -31,9 +31,6 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Utils.obfuscate -import net.thauvin.erik.mobibot.Utils.replaceEach - /** * The `ModuleException` class. */ @@ -64,22 +61,6 @@ class ModuleException : Exception { this.debugMessage = debugMessage } - /** - * Return the sanitized message (e.g. remove API keys, etc.) - */ - fun getSanitizedMessage(vararg sanitize: String): String { - val obfuscate = sanitize.map { it.obfuscate() }.toTypedArray() - return when { - cause?.message != null -> { - cause.javaClass.name + ": " + cause.message!!.replaceEach(sanitize, obfuscate) - } - message != null -> { - message.javaClass.name + ": " + message.replaceEach(sanitize, obfuscate) - } - else -> "" - } - } - companion object { private const val serialVersionUID = 1L } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 333d9d4..f418185 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -190,7 +190,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { val data = arrayOf( "Open" to "02. open", "High" to "03. high", - "Low" to "04. low", + "Low" to "04. low", "Volume" to "06. volume", "Latest" to "07. latest trading day" ) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 75fc7f5..e4deddb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -42,7 +42,6 @@ import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField import java.util.Collections -import java.util.Locale /** * The WorldTime module. @@ -65,12 +64,12 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { /** * Returns the current Internet (beat) Time. */ - @Suppress("MagicNumber") + @Suppress("MagicNumber", "ImplicitDefaultLocale") private fun internetTime(): String { val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() - return String.format(Locale.getDefault(), "%c%03d", '@', beats) + return String.format("%c%03d", '@', beats) } /** diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt b/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt new file mode 100644 index 0000000..9e861c6 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt @@ -0,0 +1,56 @@ +/* + * Sanitize.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.Utils.obfuscate +import net.thauvin.erik.mobibot.Utils.replaceEach + +object Sanitize { + /** + * Return the sanitized exception message (e.g. remove API keys, etc.) + */ + fun sanitizedMessage(e: Throwable, vararg sanitize: String): String { + val obfuscate = sanitize.map { it.obfuscate() }.toTypedArray() + with(e) { + return when { + cause?.message != null -> { + cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(sanitize, obfuscate) + } + message != null -> { + message!!.javaClass.name + ": " + message!!.replaceEach(sanitize, obfuscate) + } + else -> "" + } + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index aa4d98e..100689d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.Sanitize.sanitizedMessage import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -63,8 +64,8 @@ class GoogleSearchTest : LocalProperties() { .describedAs("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI") && apiKey.isNotBlank() && cseKey.isNotBlank()) { - throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey), e) + if ("true" == System.getenv("CI") && (apiKey.isNotBlank() || cseKey.isNotBlank())) { + throw ModuleException(e.debugMessage, sanitizedMessage(e, apiKey, cseKey), e) } else { throw e } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 4e63def..b6250c9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -31,6 +31,7 @@ */ package net.thauvin.erik.mobibot.modules +import net.thauvin.erik.mobibot.Sanitize.sanitizedMessage import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.DataProvider import org.testng.annotations.Test @@ -66,21 +67,21 @@ class ModuleExceptionTest { } @Test - fun testGetSanitizedMessage() { + fun testSanitizeMessage() { val apiKey = "1234567890" var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) - assertThat(e.getSanitizedMessage(apiKey)).describedAs("sanitized url") + assertThat(sanitizedMessage(e, apiKey)).describedAs("sanitized url") .contains("xxxxxxxxxx").doesNotContain(apiKey) e = ModuleException(debugMessage, message, null) - assertThat(e.getSanitizedMessage(apiKey)).describedAs("no cause").contains(message) + assertThat(sanitizedMessage(e, apiKey)).describedAs("no cause").contains(message) val msg: String? = null e = ModuleException(debugMessage, msg, IOException(msg)) - assertThat(e.getSanitizedMessage(apiKey)).describedAs("no message").isEqualTo("") + assertThat(sanitizedMessage(e, apiKey)).describedAs("no message").isEqualTo("") e = ModuleException(msg, msg, IOException(apiKey)) - assertThat(e.getSanitizedMessage(apiKey)).describedAs("null message").doesNotContain(apiKey) + assertThat(sanitizedMessage(e, apiKey)).describedAs("null message").doesNotContain(apiKey) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 7f46935..d8a00f3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.Sanitize.sanitizedMessage import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -64,7 +65,7 @@ class StockQuoteTest : LocalProperties() { } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI") && apiKey.isNotBlank()) { - throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey), e) + throw ModuleException(e.debugMessage, sanitizedMessage(e, apiKey), e) } else { throw e } From 95823db1d91c918704125ae37813c19dee428506 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Oct 2021 17:27:55 -0700 Subject: [PATCH 568/842] Improved sanitize function. --- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 4 +-- .../net/thauvin/erik/mobibot/Sanitize.kt | 28 +++++++++++-------- .../erik/mobibot/modules/GoogleSearchTest.kt | 6 ++-- .../mobibot/modules/ModuleExceptionTest.kt | 28 +++++++++++-------- .../erik/mobibot/modules/StockQuoteTest.kt | 6 ++-- 6 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 5930ca2..3ee5aad 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -42,7 +42,7 @@ import java.io.IOException import java.net.URL /** - * Reads a RSS feed. + * Reads an RSS feed. */ class FeedReader( // Bot diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 6a058f6..2f88e86 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -66,7 +66,7 @@ class TellMessage internal constructor( var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) /** - * Returns {@code true) if a notification was send. + * Returns {@code true) if a notification was sent. */ var isNotified = false @@ -82,7 +82,7 @@ class TellMessage internal constructor( } /** - * Return the message creating date. + * Returns the message creating date. */ var receptionDate: LocalDateTime = LocalDateTime.MIN diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt b/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt index 9e861c6..e8cc448 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt @@ -34,23 +34,29 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.replaceEach +import net.thauvin.erik.mobibot.modules.ModuleException object Sanitize { /** - * Return the sanitized exception message (e.g. remove API keys, etc.) + * Returns a sanitized exception to avoid displaying api keys, etc. in CI logs. */ - fun sanitizedMessage(e: Throwable, vararg sanitize: String): String { - val obfuscate = sanitize.map { it.obfuscate() }.toTypedArray() - with(e) { - return when { - cause?.message != null -> { - cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(sanitize, obfuscate) + fun sanitizeException(e: ModuleException, vararg sanitize: String): ModuleException { + var sanitizedException = e + val search = sanitize.filter { it.isNotBlank() }.toTypedArray() + if (search.isNotEmpty()) { + val obfuscate = search.map { it.obfuscate() }.toTypedArray() + with(e) { + if (cause?.message != null) { + sanitizedException = ModuleException( + debugMessage, + cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(search, obfuscate), + this + ) + } else if (message != null) { + sanitizedException = ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this) } - message != null -> { - message!!.javaClass.name + ": " + message!!.replaceEach(sanitize, obfuscate) - } - else -> "" } } + return sanitizedException } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 100689d..5dfc1f2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.Sanitize.sanitizedMessage +import net.thauvin.erik.mobibot.Sanitize.sanitizeException import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -64,8 +64,8 @@ class GoogleSearchTest : LocalProperties() { .describedAs("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI") && (apiKey.isNotBlank() || cseKey.isNotBlank())) { - throw ModuleException(e.debugMessage, sanitizedMessage(e, apiKey, cseKey), e) + if ("true" == System.getenv("CI")) { + throw sanitizeException(e, apiKey, cseKey) } else { throw e } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index b6250c9..707f617 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* - * ModuleExceptionTest.java + * ModuleExceptionTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,7 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Sanitize.sanitizedMessage +import net.thauvin.erik.mobibot.Sanitize.sanitizeException import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.DataProvider import org.testng.annotations.Test @@ -63,25 +63,31 @@ class ModuleExceptionTest { @Test(dataProvider = "dp") fun testGetMessage(e: ModuleException) { - assertThat(e.message).describedAs("get message").isEqualTo(message) + assertThat(e).describedAs("get message").hasMessage(message) } @Test fun testSanitizeMessage() { val apiKey = "1234567890" var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) - assertThat(sanitizedMessage(e, apiKey)).describedAs("sanitized url") - .contains("xxxxxxxxxx").doesNotContain(apiKey) + assertThat(sanitizeException(e, apiKey, "", "me")).describedAs("sanitized url") + .hasMessageContainingAll("xxxxxxxxxx", "userID=xx").hasMessageNotContainingAny(apiKey, "me") e = ModuleException(debugMessage, message, null) - assertThat(sanitizedMessage(e, apiKey)).describedAs("no cause").contains(message) + assertThat(sanitizeException(e, apiKey)).describedAs("no cause").hasMessage(message) + + e = ModuleException(debugMessage, message, IOException()) + assertThat(sanitizeException(e, apiKey)).describedAs("no cause message").hasMessage(message) + + e = ModuleException(apiKey) + assertThat(sanitizeException(e, apiKey)).describedAs("api key in message").hasMessageNotContaining(apiKey) val msg: String? = null e = ModuleException(debugMessage, msg, IOException(msg)) - assertThat(sanitizedMessage(e, apiKey)).describedAs("no message").isEqualTo("") + assertThat(sanitizeException(e, apiKey).message).describedAs("null message").isNull() - - e = ModuleException(msg, msg, IOException(apiKey)) - assertThat(sanitizedMessage(e, apiKey)).describedAs("null message").doesNotContain(apiKey) + e = ModuleException(msg, msg, IOException("foo is $apiKey")) + assertThat(sanitizeException(e, " ", apiKey, "foo").message).describedAs("key in cause") + .doesNotContain(apiKey).endsWith("xxx is xxxxxxxxxx") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index d8a00f3..b1dc7c0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.Sanitize.sanitizedMessage +import net.thauvin.erik.mobibot.Sanitize.sanitizeException import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -64,8 +64,8 @@ class StockQuoteTest : LocalProperties() { .isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI") && apiKey.isNotBlank()) { - throw ModuleException(e.debugMessage, sanitizedMessage(e, apiKey), e) + if ("true" == System.getenv("CI")) { + throw sanitizeException(e, apiKey) } else { throw e } From 611420757ad945a4c50bab7877779d93d2c76e2a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Oct 2021 18:25:33 -0700 Subject: [PATCH 569/842] Updated copyright. --- src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt | 2 +- src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt | 2 +- .../net/thauvin/erik/mobibot/commands/links/LinksMgr.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/commands/links/View.kt | 2 +- .../net/thauvin/erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt | 2 +- .../net/thauvin/erik/mobibot/modules/ModuleException.kt | 4 ++-- .../kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt | 2 +- src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt | 2 +- src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt | 4 ++-- src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 4 ++-- src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt | 2 +- src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt | 2 +- src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt | 2 +- 26 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 5496fd9..1a0e8dd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,5 +1,5 @@ /* - * Addons.java + * Addons.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index f5756da..8dbe942 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,5 +1,5 @@ /* - * Feed.kt + * ChannelFeed.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 230539f..bffd1f2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,5 +1,5 @@ /* - * Info.java + * Info.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index ebe7067..2bfc57b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,5 +1,5 @@ /* - * Links.kt + * Comment.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index ce0b797..fd7eb27 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -1,5 +1,5 @@ /* - * Links.kt + * LinksMgr.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 77c9b43..140f70c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,5 +1,5 @@ /* - * Links.kt + * Posting.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index e2b6d8f..ab7a983 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,5 +1,5 @@ /* - * Links.kt + * Tags.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 45da0f5..e845a66 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,5 +1,5 @@ /* - * Links.kt + * View.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 2f88e86..47521d4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index c0247c3..cf0bf35 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 71c5004..4c6867a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* - * ModuleException.kit + * ModuleException.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt index e382ac5..d1d58d0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt @@ -1,5 +1,5 @@ /* - * ThreadedModule.java + * ThreadedModule.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 741d557..35409f7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 126fd91..c9fa9dc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 6bbade8..2953b7a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 384bc05..397b3cf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 3360242..234f84b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* - * FeedReaderTest.java + * FeedReaderTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 16434f2..2dc0ef8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* - * UtilsTest.java + * UtilsTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 8a78beb..ad0740e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 88515cc..037ba4e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index eabd984..62397d9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 9fbf64c..bc9c3d4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index 1eba3b7..e79fbcc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index ce5c36d..2257ac4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -1,7 +1,7 @@ /* * TwitterTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 0aea8d2..1c503ca 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index fa5dd90..15f0beb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without From 2a46761dc57c47b268a90faddf7bafdf838a2279 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 25 Oct 2021 23:41:52 -0700 Subject: [PATCH 570/842] Cleaned up tests. --- .../erik/mobibot/commands/tell/TellMessageTest.kt | 5 ++--- .../thauvin/erik/mobibot/entries/EntryLinkTest.kt | 11 +++++------ .../thauvin/erik/mobibot/modules/CryptoPricesTest.kt | 10 ++++------ .../erik/mobibot/modules/CurrencyConverterTest.kt | 5 ++--- .../net/thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../net/thauvin/erik/mobibot/modules/LookupTest.kt | 4 ++-- .../erik/mobibot/modules/ModuleExceptionTest.kt | 3 ++- .../thauvin/erik/mobibot/modules/StockQuoteTest.kt | 12 ++++++++---- .../net/thauvin/erik/mobibot/modules/WordTimeTest.kt | 8 +++----- 9 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 7f5de32..86422ca 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -51,9 +51,8 @@ class TellMessageTest { val recipient = "recipient" val sender = "sender" val tellMessage = TellMessage(sender, recipient, message) - assertThat(tellMessage.sender).describedAs(sender).isEqualTo(sender) - assertThat(tellMessage.recipient).describedAs(recipient).isEqualTo(recipient) - assertThat(tellMessage.message).describedAs(message).isEqualTo(message) + assertThat(tellMessage).extracting("sender", "recipient", "message") + .containsExactly(sender, recipient, message) assertThat(isValidDate(tellMessage.queued)).describedAs("queued is valid date/time").isTrue assertThat(tellMessage.isMatch(sender)).describedAs("match sender").isTrue assertThat(tellMessage.isMatch(recipient)).describedAs("match recipient").isTrue diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 7b4c159..347b508 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -61,19 +61,18 @@ class EntryLinkTest { assertThat(entryLink.comments.size).describedAs("getComments().size() == 5").isEqualTo(i) i = 0 for (comment in entryLink.comments) { - assertThat(comment.comment).describedAs("getComment($i)").isEqualTo("c$i") - assertThat(comment.nick).describedAs("getNick($i)").isEqualTo("u$i") + assertThat(comment).extracting("comment", "nick").containsExactly("c$i", "u$i") i++ } val r = SecureRandom() while (entryLink.comments.size > 0) { entryLink.deleteComment(r.nextInt(entryLink.comments.size)) } - assertThat(entryLink.comments.isNotEmpty()).describedAs("hasComments()").isFalse + assertThat(entryLink.comments).describedAs("hasComments()").isEmpty() entryLink.addComment("nothing", "nobody") entryLink.setComment(0, "something", "somebody") - assertThat(entryLink.getComment(0).nick).describedAs("getNick(somebody)").isEqualTo("somebody") - assertThat(entryLink.getComment(0).comment).describedAs("getComment(something)").isEqualTo("something") + assertThat(entryLink.getComment(0)).describedAs("get first comment").extracting("nick", "comment") + .containsExactly("somebody", "something") } @Test @@ -104,7 +103,7 @@ class EntryLinkTest { assertThat(tag.name).describedAs("tag.getName($i)").isEqualTo("tag" + (i + 1)) } assertThat(entryLink.tags.size).describedAs("getTags().size() is 5").isEqualTo(5) - assertThat(entryLink.tags.isNotEmpty()).describedAs("hasTags() is true").isTrue + assertThat(entryLink.tags).describedAs("hasTags() is true").isNotEmpty entryLink.setTags("-tag5") entryLink.setTags("+mobitopia") entryLink.setTags("tag4") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 98a72cb..8d0c70a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -43,13 +43,11 @@ class CryptoPricesTest { @Throws(ModuleException::class) fun testMarketPrice() { var price = currentPrice(listOf("BTC")) - assertThat(price.base).describedAs("is BTC").isEqualTo("BTC") - assertThat(price.currency).describedAs("is USD").isEqualTo("USD") - assertThat(price.amount.signum() > 0).describedAs("BTC > 0").isTrue + assertThat(price).extracting("base", "currency").containsExactly("BTC", "USD") + assertThat(price.amount.signum()).describedAs("BTC > 0").isGreaterThan(0) price = currentPrice(listOf("ETH", "EUR")) - assertThat(price.base).describedAs("is ETH").isEqualTo("ETH") - assertThat(price.currency).describedAs("is EUR").isEqualTo("EUR") - assertThat(price.amount.signum() > 0).describedAs("ETH > 0").isTrue + assertThat(price).extracting("base", "currency").containsExactly("ETH", "EUR") + assertThat(price.amount.signum()).describedAs("ETH > 0").isGreaterThan(0) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 037ba4e..84fa190 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -57,8 +57,7 @@ class CurrencyConverterTest { assertThat(convertCurrency("100 USD").msg).describedAs("100 USD").contains("Invalid query.") val rates = currencyRates() assertThat(rates.size).describedAs("currencyRates.size == 33").isEqualTo(33) - assertThat(rates).describedAs("currencyRates(EUR)").contains("EUR: 1") - assertThat(rates.stream().anyMatch { it.matches("USD: .*".toRegex()) }) - .describedAs("currencyRates(USD)").isTrue + assertThat(rates).describedAs("currencyRates(EUR< USD)").contains("EUR: 1") + .anyMatch { it.matches("USD: .*".toRegex()) } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 62397d9..28fd592 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -42,7 +42,7 @@ class JokeTest { @Test @Throws(ModuleException::class) fun testRandomJoke() { - assertThat(randomJoke().msg.isNotEmpty()).describedAs("randomJoke() > 0").isTrue + assertThat(randomJoke().msg).describedAs("randomJoke() > 0").isNotEmpty assertThat(randomJoke().msg).describedAs("randomJoke()").containsIgnoringCase("chuck") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index bc9c3d4..211afdd 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -51,7 +51,7 @@ class LookupTest { @Throws(Exception::class) fun testWhois() { val result = whois("17.178.96.59", Lookup.WHOIS_HOST) - assertThat(result.stream().anyMatch { m: String -> m.contains("Apple Inc.") }) - .describedAs("whois(17.178.96.59/Apple Inc.").isTrue + assertThat(result).describedAs("whois(17.178.96.59/Apple Inc.") + .anyMatch { it.contains("Apple Inc.") } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 707f617..4ad980b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -71,7 +71,8 @@ class ModuleExceptionTest { val apiKey = "1234567890" var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) assertThat(sanitizeException(e, apiKey, "", "me")).describedAs("sanitized url") - .hasMessageContainingAll("xxxxxxxxxx", "userID=xx").hasMessageNotContainingAny(apiKey, "me") + .hasMessageContainingAll("xxxxxxxxxx", "userID=xx", "java.io.IOException") + .hasMessageNotContainingAny(apiKey, "me") e = ModuleException(debugMessage, message, null) assertThat(sanitizeException(e, apiKey)).describedAs("no cause").hasMessage(message) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index b1dc7c0..4eb4081 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -42,6 +42,10 @@ import org.testng.annotations.Test * The `StockQuoteTest` class. */ class StockQuoteTest : LocalProperties() { + private fun buildMatch(label: String): String { + return "${label}:[ ]+[0-9.]+".prependIndent() + } + @Test @Throws(ModuleException::class) fun testGetQuote() { @@ -49,10 +53,10 @@ class StockQuoteTest : LocalProperties() { try { val messages = getQuote("apple inc", apiKey) assertThat(messages).describedAs("response not empty").isNotEmpty - assertThat(messages[0].msg).describedAs("same stock symbol").startsWith("Symbol: AAPL") - assertThat(messages[1].msg).describedAs("price label").startsWith("Price: ".prependIndent()) - assertThat(messages[2].msg).describedAs("previous label").startsWith("Previous: ".prependIndent()) - assertThat(messages[3].msg).describedAs("open label").startsWith("Open: ".prependIndent()) + assertThat(messages[0].msg).describedAs("same stock symbol").matches("Symbol: AAPL .*") + assertThat(messages[1].msg).describedAs("price label").matches(buildMatch("Price")) + assertThat(messages[2].msg).describedAs("previous label").matches(buildMatch("Previous")) + assertThat(messages[3].msg).describedAs("open label").matches(buildMatch("Open")) try { getQuote("blahfoo", apiKey) } catch (e: ModuleException) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 15f0beb..49e63c1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -48,16 +48,14 @@ class WordTimeTest { fun testTime() { assertThat(time("PST").msg).describedAs("PST").endsWith(bold("Los Angeles")) assertThat(time("BLAH").isError).describedAs("BLAH").isTrue - assertThat(time("BEAT").msg).describedAs(BEATS_KEYWORD).endsWith(BEATS_KEYWORD).contains("@") + assertThat(time("BEAT").msg).describedAs(BEATS_KEYWORD).matches("[\\w ]+: .?@\\d{3}+.? .beats") } @Test @Throws(ZoneRulesException::class) fun testCountries() { - COUNTRIES_MAP.toSortedMap().forEach { - if (it.value != BEATS_KEYWORD) { - ZoneId.of(it.value) - } + COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { + ZoneId.of(it.value) } } } From 9fb870648e9dced0c0faf490a7720bb1a70caab0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Mon, 8 Nov 2021 13:54:48 -0800 Subject: [PATCH 571/842] Moved to PircBotX and assertk. --- .idea/jarRepositories.xml | 5 + .idea/mobibot.iml | 7 + build.gradle | 28 +- config/detekt/baseline.xml | 88 ++- properties/log4j2.xml | 32 +- properties/mobibot.properties | 7 +- .../net/thauvin/erik/mobibot/modules/War.java | 22 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 27 +- .../net/thauvin/erik/mobibot/Constants.kt | 17 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 44 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 631 +++++------------- .../mobibot/{PinboardUtils.kt => Pinboard.kt} | 83 ++- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 1 - .../net/thauvin/erik/mobibot/TwitterTimer.kt | 5 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 72 +- .../erik/mobibot/commands/AbstractCommand.kt | 26 +- .../thauvin/erik/mobibot/commands/AddLog.kt | 70 -- .../erik/mobibot/commands/ChannelFeed.kt | 19 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 36 +- .../thauvin/erik/mobibot/commands/Debug.kt | 59 -- .../net/thauvin/erik/mobibot/commands/Die.kt | 36 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 58 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 48 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 22 +- .../thauvin/erik/mobibot/commands/Modules.kt | 34 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 27 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 22 +- .../thauvin/erik/mobibot/commands/Recap.kt | 20 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 22 +- .../thauvin/erik/mobibot/commands/Users.kt | 33 +- .../thauvin/erik/mobibot/commands/Versions.kt | 22 +- .../erik/mobibot/commands/links/Comment.kt | 103 ++- .../erik/mobibot/commands/links/LinksMgr.kt | 127 ++-- .../erik/mobibot/commands/links/Posting.kt | 112 ++-- .../erik/mobibot/commands/links/Tags.kt | 35 +- .../erik/mobibot/commands/links/View.kt | 51 +- .../erik/mobibot/commands/tell/Tell.kt | 163 ++--- .../erik/mobibot/commands/tell/TellMessage.kt | 4 +- .../mobibot/commands/tell/TellMessagesMgr.kt | 39 +- .../{commands/Kill.kt => entries/Entries.kt} | 38 +- .../erik/mobibot/entries/EntriesMgr.kt | 262 -------- .../erik/mobibot/entries/EntriesUtils.kt | 10 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 15 +- .../thauvin/erik/mobibot/entries/FeedsMgr.kt | 190 ++++++ .../erik/mobibot/modules/AbstractModule.kt | 18 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 35 +- .../erik/mobibot/modules/CryptoPrices.kt | 48 +- .../erik/mobibot/modules/CurrencyConverter.kt | 113 ++-- .../net/thauvin/erik/mobibot/modules/Dice.kt | 32 +- .../erik/mobibot/modules/GoogleSearch.kt | 64 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 27 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 78 +-- .../net/thauvin/erik/mobibot/modules/Ping.kt | 14 +- .../erik/mobibot/modules/RockPaperScissors.kt | 23 +- .../erik/mobibot/modules/StockQuote.kt | 114 ++-- .../erik/mobibot/modules/ThreadedModule.kt | 24 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 105 ++- .../thauvin/erik/mobibot/modules/Weather2.kt | 39 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 611 ++++++++--------- .../{Sanitize.kt => ExceptionSanitizer.kt} | 19 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 28 +- .../{PinboardUtilsTest.kt => PinboardTest.kt} | 28 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 105 +-- .../mobibot/commands/tell/TellMessageTest.kt | 30 +- .../erik/mobibot/entries/EntryLinkTest.kt | 57 +- .../erik/mobibot/entries/FeedMgrTest.kt | 121 ++++ .../thauvin/erik/mobibot/modules/CalcTest.kt | 18 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 21 +- .../mobibot/modules/CurrencyConverterTest.kt | 43 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 9 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 47 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 11 +- .../erik/mobibot/modules/LookupTest.kt | 9 +- .../mobibot/modules/ModuleExceptionTest.kt | 38 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 8 +- .../mobibot/modules/RockPaperScissorsTest.kt | 22 +- .../erik/mobibot/modules/StockQuoteTest.kt | 44 +- .../erik/mobibot/modules/TwitterTest.kt | 10 +- .../erik/mobibot/modules/Weather2Test.kt | 61 +- .../erik/mobibot/modules/WordTimeTest.kt | 27 +- .../thauvin/erik/mobibot/msg/TestMessage.kt | 9 +- src/test/resources/current.xml | 36 + version.properties | 6 +- 83 files changed, 2347 insertions(+), 2577 deletions(-) create mode 100644 .idea/mobibot.iml rename src/main/kotlin/net/thauvin/erik/mobibot/{PinboardUtils.kt => Pinboard.kt} (63%) delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/commands/Debug.kt rename src/main/kotlin/net/thauvin/erik/mobibot/{commands/Kill.kt => entries/Entries.kt} (74%) delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt rename src/test/kotlin/net/thauvin/erik/mobibot/{Sanitize.kt => ExceptionSanitizer.kt} (82%) rename src/test/kotlin/net/thauvin/erik/mobibot/{PinboardUtilsTest.kt => PinboardTest.kt} (81%) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt create mode 100644 src/test/resources/current.xml diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 8920634..75b7aaa 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -31,5 +31,10 @@ <option name="name" value="MavenLocal" /> <option name="url" value="file:$MAVEN_REPOSITORY$/" /> </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://jitpack.io" /> + </remote-repository> </component> </project> \ No newline at end of file diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml new file mode 100644 index 0000000..b03a311 --- /dev/null +++ b/.idea/mobibot.iml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module version="4"> + <component name="SonarLintModuleSettings"> + <option name="idePathPrefix" value="bin/main" /> + <option name="sqPathPrefix" value="src/main/kotlin" /> + </component> +</module> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3207f2a..020006e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,12 +2,12 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.18.1' + id 'io.gitlab.arturbosch.detekt' version '1.19.0-RC1' id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC' - id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC' + id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC2' + id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC2' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -22,12 +22,13 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j: '2.14.1', - pmd : '6.35.0', + pmd : '6.40.0', ] repositories { mavenLocal() mavenCentral() + maven { url 'https://jitpack.io' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } } @@ -35,8 +36,13 @@ dependencies { kapt(semverProcessor) compileOnly(semverProcessor) - implementation 'pircbot:pircbot:1.5.0' - compileOnly 'pircbot:pircbot:1.5.0:sources' + implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' + + implementation 'org.apache.commons:commons-text:1.9' + implementation 'org.apache.commons:commons-lang3:3.12.0' + implementation 'org.slf4j:slf4j-api:1.7.32' + implementation 'commons-codec:commons-codec:1.15' + implementation 'com.google.guava:guava:31.0.1-jre' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' @@ -44,11 +50,12 @@ dependencies { implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" - implementation 'commons-cli:commons-cli:1.4' + + implementation 'commons-cli:commons-cli:1.5.0' implementation 'commons-net:commons-net:3.8.0' implementation 'com.rometools:rome:1.16.0' - implementation 'com.squareup.okhttp3:okhttp:4.9.1' + implementation 'com.squareup.okhttp3:okhttp:4.9.2' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' @@ -59,7 +66,9 @@ dependencies { implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.twitter4j:twitter4j-core:4.0.7' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' +// testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' +// testImplementation "org.mockito:mockito-core:4.0.0" testImplementation 'org.testng:testng:7.4.0' } @@ -117,6 +126,7 @@ jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) archiveVersion.set("") + exclude('log4j2.xml') } clean { diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index c3f468f..9bc0b96 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,54 +2,76 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> - <ID>ComplexMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> + <ID>ComplexMethod:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>LongMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> - <ID>LongMethod:Mobibot.kt$Mobibot.Companion$ @JvmStatic fun main(args: Array<String>)</ID> + <ID>LongMethod:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>LongMethod:Mobibot.kt$Mobibot.Companion$@Throws(Exception::class) @JvmStatic fun main(args: Array<String>)</ID> <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>LongParameterList:Addons.kt$Addons$(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID> - <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> - <ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID> - <ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID> - <ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, separator: String = " ", isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID> + <ID>LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> + <ID>MagicNumber:Comment.kt$Comment$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID> + <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID> + <ID>MagicNumber:Cycle.kt$Cycle$10</ID> + <ID>MagicNumber:Cycle.kt$Cycle$1000L</ID> + <ID>MagicNumber:Ignore.kt$Ignore$8</ID> + <ID>MagicNumber:Mobibot.kt$Mobibot$8</ID> + <ID>MagicNumber:Modules.kt$Modules$7</ID> + <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> + <ID>MagicNumber:StockQuote.kt$StockQuote.Companion$10</ID> + <ID>MagicNumber:Tell.kt$Tell$50</ID> + <ID>MagicNumber:Tell.kt$Tell$7</ID> + <ID>MagicNumber:Twitter.kt$Twitter$1000L</ID> + <ID>MagicNumber:Twitter.kt$Twitter$60L</ID> + <ID>MagicNumber:TwitterOAuth.kt$TwitterOAuth$401</ID> + <ID>MagicNumber:Users.kt$Users$8</ID> + <ID>MagicNumber:Utils.kt$Utils$30</ID> + <ID>MagicNumber:Utils.kt$Utils$365</ID> + <ID>MagicNumber:Utils.kt$Utils$7</ID> + <ID>MagicNumber:View.kt$View$6</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID> + <ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime$14</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime$4</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> + <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> - <ID>NestedBlockDepth:Calc.kt$Calc$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> - <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> - <ID>NestedBlockDepth:CryptoPrices.kt$CryptoPrices$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean): Boolean</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @Suppress("MagicNumber") @JvmStatic fun convertCurrency(query: String): Message</ID> - <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID> - <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID> - <ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$// Daily backup private fun dailyBackup( bot: Mobibot, history: MutableList<String> )</ID> + <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> - <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> - <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID> - <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> - <ID>NestedBlockDepth:Mobibot.kt$Mobibot$ fun connect()</ID> - <ID>NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> + <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> + <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> + <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> + <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID> + <ID>NestedBlockDepth:Posting.kt$Posting$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> - <ID>NestedBlockDepth:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID> - <ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID> - <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?, logger: Logger)</ID> + <ID>NestedBlockDepth:Tell.kt$Tell$ fun send(event: GenericUserEvent)</ID> + <ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent)</ID> + <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun load(file: String): List<TellMessage></ID> + <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?)</ID> <ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @Throws(TwitterException::class, IOException::class) @JvmStatic fun main(args: Array<String>)</ID> - <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID> + <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID> - <ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> - <ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID> + <ID>ReturnCount:Addons.kt$Addons$ fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean</ID> + <ID>ReturnCount:Addons.kt$Addons$ fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean</ID> + <ID>ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$ fun ModuleException.sanitize(vararg sanitize: String): ModuleException</ID> + <ID>SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException</ID> + <ID>SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>TooGenericExceptionCaught:CryptoPrices.kt$CryptoPrices$e: Exception</ID> - <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID> - <ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID> <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> - <ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID> <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> </CurrentIssues> </SmellBaseline> diff --git a/properties/log4j2.xml b/properties/log4j2.xml index 2842592..0aa95bf 100644 --- a/properties/log4j2.xml +++ b/properties/log4j2.xml @@ -1,16 +1,38 @@ <?xml version="1.0" encoding="UTF-8"?> -<Configuration status="WARN"> +<Configuration status="warn"> <Appenders> <Console name="stderr" target="SYSTEM_ERR"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> + <Console name="input" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{UNIX_MILLIS} %msg%n"/> + <Filters> + <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/> + <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/> + </Filters> + </Console> + <Console name="output" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{UNIX_MILLIS} >>>%msg%n"/> + <Filters> + <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/> + <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/> + </Filters> + </Console> </Appenders> <Loggers> - <Logger name="net.thauvin.erik.mobibot" level="warn" additivity="false"> - <AppenderRef ref="stderr"/> - </Logger> - <Root level="error"> + <Root level="warn" additivity="false"> <AppenderRef ref="stderr"/> </Root> + <logger level="debug" name="org.pircbotx.InputParser" additivity="false"> + <appender-ref ref="input"/> + <appender-ref ref="stderr" level="warn"/> + </logger> + <logger level="debug" name="org.pircbotx.output.OutputRaw" additivity="false"> + <appender-ref ref="output"/> + <appender-ref ref="stderr" level="warn"/> + </logger> + <logger level="warn" name="net.thauvin.erik.mobibot" additivity="false"> + <appender-ref ref="stderr"/> + </logger> </Loggers> </Configuration> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 8089d08..67326f3 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -3,9 +3,13 @@ server=irc.freenode.net #port=6667 login=mobibot nick=mobibot +#realname=mobibot + +# Die command password, if any +#die=changeme # NickServ password -ident=changepwd +ident=changeme #ident-nick=nickserv #ident-msg=IDENTIFY changepwd @@ -14,7 +18,6 @@ ignore=chanserv,nickserv tags=mobile mobitopia tags-keywords=android ios apple google -weblog=http://www.mobitopia.org/ feed=http://www.mobitopia.org/rss.xml backlogs=http://www.mobitopia.org/mobibot/logs diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 2e83533..c286091 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -32,9 +32,9 @@ package net.thauvin.erik.mobibot.modules; -import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; import org.jetbrains.annotations.NotNull; +import org.pircbotx.hooks.types.GenericMessageEvent; import java.security.SecureRandom; @@ -66,8 +66,8 @@ public final class War extends AbstractModule { /** * The default constructor. */ - public War(final Mobibot bot) { - super(bot); + public War() { + super(); commands.add(WAR_CMD); @@ -79,10 +79,8 @@ public final class War extends AbstractModule { * {@inheritDoc} */ @Override - public void commandResponse(@NotNull final String sender, - @NotNull final String cmd, - @NotNull final String args, - final boolean isPrivate) { + public void commandResponse(@NotNull final String channel, @NotNull final String cmd, @NotNull final String args, + @NotNull final GenericMessageEvent event) { int i; int y; @@ -90,20 +88,20 @@ public final class War extends AbstractModule { i = RANDOM.nextInt(HEARTS.length); y = RANDOM.nextInt(HEARTS.length); - getBot().send(sender + " drew: " + DECK[RANDOM.nextInt(DECK.length)][i]); - getBot().action("drew: " + DECK[RANDOM.nextInt(DECK.length)][y]); + event.respond("you drew " + DECK[RANDOM.nextInt(DECK.length)][i]); + event.getBot().sendIRC().action(channel, "drew " + DECK[RANDOM.nextInt(DECK.length)][y]); if (i != y) { break; } - getBot().send("This means " + bold("WAR") + '!'); + event.respond("This means " + bold("WAR") + '!'); } if (i < y) { - getBot().action("lost."); + event.getBot().sendIRC().action(channel, "lost."); } else { - getBot().action("wins."); + event.getBot().sendIRC().action(channel, "wins."); } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 1a0e8dd..bc527f0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -33,6 +33,8 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.modules.AbstractModule +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent import java.util.Properties /** @@ -77,7 +79,7 @@ class Addons { if (isEnabled()) { commands.add(this) if (isVisible) { - if (isOp) { + if (isOpOnly) { ops.add(name) } else { names.add(name) @@ -90,17 +92,18 @@ class Addons { /** * Execute a command or module. */ - fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean { - for (command in commands) { + fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean { + val cmds = if (event is PrivateMessageEvent) commands else commands.filter { it.isPublic } + for (command in cmds) { if (command.name.startsWith(cmd)) { - command.commandResponse(sender, login, args, isOp, isPrivate) + command.commandResponse(channel, args, event) return true } } - val mods = if (isPrivate) modules.filter { it.isPrivateMsgEnabled } else modules + val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules for (module in mods) { if (module.commands.contains(cmd)) { - module.commandResponse(sender, cmd, args, isPrivate) + module.commandResponse(channel, cmd, args, event) return true } } @@ -110,10 +113,10 @@ class Addons { /** * Match a command. */ - fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean): Boolean { + fun match(channel: String, event: GenericMessageEvent): Boolean { for (command in commands) { - if (command.matches(message)) { - command.commandResponse(sender, login, message, isOp, isPrivate) + if (command.matches(event.message)) { + command.commandResponse(channel, event.message, event) return true } } @@ -123,15 +126,15 @@ class Addons { /** * Commands and Modules help. */ - fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean { + fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean { for (command in commands) { if (command.isVisible && command.name.startsWith(topic)) { - return command.helpResponse(topic, sender, isOp, isPrivate) + return command.helpResponse(channel, topic, event) } } for (module in modules) { if (module.commands.contains(topic)) { - return module.helpResponse(sender, isPrivate) + return module.helpResponse(event) } } return false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 2ae420d..dfe25dc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -45,11 +45,6 @@ object Constants { */ const val DEBUG_ARG = "debug" - /** - * The debug command. - */ - const val DEBUG_CMD = "debug" - /** * Default IRC Port. */ @@ -58,12 +53,7 @@ object Constants { /** * Default IRC Server. */ - const val DEFAULT_SERVER = "irc.freenode.net" - - /** - * The die command. - */ - const val DIE_CMD = "die" + const val DEFAULT_SERVER = "irc.libera.chat" /** * Help command line argument. @@ -75,11 +65,6 @@ object Constants { */ const val HELP_CMD = "help" - /** - * The kill command. - */ - const val KILL_CMD = "kill" - /** * The link command. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 3ee5aad..16c8e6b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -36,38 +36,36 @@ import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.XmlReader import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.entries.FeedsMgr import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage +import net.thauvin.erik.mobibot.msg.NoticeMessage +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL /** * Reads an RSS feed. */ -class FeedReader( - // Bot - private val bot: Mobibot, - // Nick of the person who sent the message - private val sender: String, - // URL to fetch - private val url: String -) : Runnable { +class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable { + private val logger: Logger = LoggerFactory.getLogger(FeedsMgr::class.java) + /** * Fetches the Feed's items. */ override fun run() { - with(bot) { - try { - readFeed(url).forEach { - send(sender, it) - } - } catch (e: FeedException) { - if (logger.isDebugEnabled) logger.debug("Unable to parse the feed at $url", e) - send(sender, "An error has occurred while parsing the feed: ${e.message}", false) - } catch (e: IOException) { - if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed at $url", e) - send(sender, "An error has occurred while fetching the feed: ${e.message}", false) + try { + readFeed(url).forEach { + event.sendMessage("", it) } + } catch (e: FeedException) { + if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e) + event.sendMessage("An error has occurred while parsing the feed: ${e.message}") + } catch (e: IOException) { + if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e) + event.sendMessage("An error has occurred while fetching the feed: ${e.message}") } } @@ -81,11 +79,11 @@ class FeedReader( val feed = input.build(reader) val items = feed.entries if (items.isEmpty()) { - messages.add(PublicMessage("There is currently nothing to view.")) + messages.add(NoticeMessage("There is currently nothing to view.")) } else { items.take(maxItems).forEach { - messages.add(PublicMessage(it.title)) - messages.add(PublicMessage(helpFormat(green(it.link), false))) + messages.add(NoticeMessage(it.title)) + messages.add(NoticeMessage(helpFormat(green(it.link), false))) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 6bfbe8a..574366a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -29,23 +29,21 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.appendIfMissing -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax -import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.getIntProperty -import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import net.thauvin.erik.mobibot.Utils.today -import net.thauvin.erik.mobibot.commands.AddLog import net.thauvin.erik.mobibot.commands.ChannelFeed import net.thauvin.erik.mobibot.commands.Cycle -import net.thauvin.erik.mobibot.commands.Debug import net.thauvin.erik.mobibot.commands.Die import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.Info -import net.thauvin.erik.mobibot.commands.Kill import net.thauvin.erik.mobibot.commands.Me import net.thauvin.erik.mobibot.commands.Modules import net.thauvin.erik.mobibot.commands.Msg @@ -61,8 +59,6 @@ import net.thauvin.erik.mobibot.commands.links.Posting import net.thauvin.erik.mobibot.commands.links.Tags import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.commands.tell.Tell -import net.thauvin.erik.mobibot.entries.EntriesMgr -import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.mobibot.modules.Calc import net.thauvin.erik.mobibot.modules.CryptoPrices import net.thauvin.erik.mobibot.modules.CurrencyConverter @@ -73,12 +69,9 @@ import net.thauvin.erik.mobibot.modules.Lookup import net.thauvin.erik.mobibot.modules.Ping import net.thauvin.erik.mobibot.modules.RockPaperScissors import net.thauvin.erik.mobibot.modules.StockQuote -import net.thauvin.erik.mobibot.modules.Twitter import net.thauvin.erik.mobibot.modules.War import net.thauvin.erik.mobibot.modules.Weather2 import net.thauvin.erik.mobibot.modules.WorldTime -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.pinboard.PinboardPoster import net.thauvin.erik.semver.Version import org.apache.commons.cli.CommandLine import org.apache.commons.cli.CommandLineParser @@ -87,10 +80,19 @@ import org.apache.commons.cli.HelpFormatter import org.apache.commons.cli.Option import org.apache.commons.cli.Options import org.apache.commons.cli.ParseException -import org.apache.logging.log4j.Level -import org.apache.logging.log4j.LogManager -import org.apache.logging.log4j.Logger -import org.jibble.pircbot.PircBot +import org.pircbotx.Configuration +import org.pircbotx.PircBotX +import org.pircbotx.hooks.ListenerAdapter +import org.pircbotx.hooks.events.ActionEvent +import org.pircbotx.hooks.events.DisconnectEvent +import org.pircbotx.hooks.events.JoinEvent +import org.pircbotx.hooks.events.MessageEvent +import org.pircbotx.hooks.events.NickChangeEvent +import org.pircbotx.hooks.events.PartEvent +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.BufferedOutputStream import java.io.File import java.io.FileNotFoundException @@ -100,427 +102,139 @@ import java.io.PrintStream import java.nio.file.Files import java.nio.file.Paths import java.util.Properties -import java.util.Timer -import java.util.logging.ConsoleHandler import java.util.regex.Pattern import kotlin.system.exitProcess -/** - * Implements the #mobitopia bot. - */ @Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") -class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : PircBot() { +class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { + // The bot configuration. + private val config: Configuration + // Commands and Modules private val addons = Addons() + // Tell module + private val tell: Tell + /** Main channel. */ val channel: String - // IRC port - private val ircPort: Int - - /** IRC server. */ - val ircServer: String - /** Logger. */ - val logger: Logger = LogManager.getLogger(Mobibot::class.java) - - /** Logger default level. */ - val loggerLevel: Level - - /** Log directory. */ - val logsDir: String - - // Pinboard posts handler - private val pinboard: PinboardPoster = PinboardPoster() - - /** Tell command. */ - val tell: Tell - - /** Today's date. */ - val today = today() - - /** Twitter module. */ - val twitter: Twitter - - /** The backlogs URL. */ - val backlogsUrl: String - - // Ident message - private val identMsg: String - - // Ident nick - private val identNick: String - - // NickServ ident password - private val identPwd: String - - // Is pinboard enabled? - private var isPinboardEnabled = false - - /** Timer. */ - val timer = Timer(true) - - /** Weblog URL */ - val weblogUrl: String - - /** The current channel name. */ - private val channelName: String - get() = channel.substring(1) - - /** The enabled modules names. */ - val modulesNames: List<String> - get() = addons.modulesNames - - /** - * Sends an action to the current channel. - */ - fun action(action: String) { - action(channel, action) - } - - /** - * Sends an action to the channel. - */ - private fun action(channel: String, action: String) { - if (channel.isNotBlank() && action.isNotBlank()) { - sendAction(channel, action) - } - } - - /** - * Adds pin on pinboard. - */ - fun addPin(entry: EntryLink) { - if (isPinboardEnabled) { - PinboardUtils.addPin(pinboard, ircServer, entry) - } - } + val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java) /** * Connects to the server and joins the channel. */ fun connect() { - try { - connect(ircServer, ircPort) - } catch (e: Exception) { - if (logger.isDebugEnabled) { - logger.debug("Unable to connect to $ircServer, will try again.", e) - } - var retries = 0 - while (retries++ < MAX_RECONNECT && !isConnected) { - @Suppress("MagicNumber") - sleep(10) - try { - connect(ircServer, ircPort) - } catch (ex: Exception) { - if (retries == MAX_RECONNECT) { - if (logger.isDebugEnabled) { - logger.debug("Unable to reconnect to $ircServer, after $MAX_RECONNECT retries.", ex) - } - System.err.println("An error has occurred while reconnecting; ${ex.message}") - exitProcess(1) - } - } - } - } - identify() - joinChannel() - } - - /** - * Deletes pin on pinboard. - */ - fun deletePin(index: Int, entry: EntryLink) { - if (isPinboardEnabled) { - PinboardUtils.deletePin(pinboard, entry) - } - if (twitter.isAutoPost) { - twitter.removeEntry(index) - } + PircBotX(config).startBot() } /** * Responds with the default help. */ - @Suppress("MagicNumber") - fun helpDefault(sender: String, isOp: Boolean, isPrivate: Boolean) { - send(sender, "Type a URL on $channel to post it.", isPrivate) - send(sender, "For more information on a specific command, type:", isPrivate) - send( - sender, - helpFormat(buildCmdSyntax("%c ${Constants.HELP_CMD} <command>", nick, isPrivate)), - isPrivate + private fun helpDefault(event: GenericMessageEvent) { + event.sendMessage("Type a URL on $channel to post it.") + event.sendMessage("For more information on a specific command, type:") + event.sendMessage( + Utils.helpFormat( + Utils.buildCmdSyntax( + "%c ${Constants.HELP_CMD} <command>", + event.bot().nick, + event is PrivateMessageEvent + ) + ), ) - send(sender, "The commands are:", isPrivate) - sendList(sender, addons.names, 8, isPrivate = isPrivate, isBold = true, isIndent = true) - if (isOp) { - send(sender, "The op commands are:", isPrivate) - sendList(sender, addons.ops, 8, isPrivate = isPrivate, isBold = true, isIndent = true) + event.sendMessage("The commands are:") + event.sendList(addons.names, 8, isBold = true, isIndent = true) + if (isChannelOp(channel, event)) { + event.sendMessage("The op commands are:") + event.sendList(addons.ops, 8, isBold = true, isIndent = true) } } /** * Responds with the default, commands or modules help. */ - private fun helpResponse(sender: String, topic: String, isPrivate: Boolean) { - val isOp = isOp(sender) - if (topic.isBlank() || !addons.help(sender, topic.lowercase().trim(), isOp, isPrivate)) { - helpDefault(sender, isOp, isPrivate) + private fun helpResponse(event: GenericMessageEvent, topic: String) { + if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) { + helpDefault(event) } } - /** - * Identifies the bot. - */ - private fun identify() { - // Identify with NickServ - if (identPwd.isNotBlank()) { - identify(identPwd) - } - // Identify with a specified nick - if (identNick.isNotBlank() && identMsg.isNotBlank()) { - sendMessage(identNick, identMsg) + override fun onAction(event: ActionEvent?) { + if (channel == event?.channel?.name) { + storeRecap(event.user!!.nick, event.action, true) } } - /** - * Returns {@code true} if the specified sender is an Op on the [channel][.ircChannel]. - */ - fun isOp(sender: String): Boolean { - for (user in getUsers(channel)) { - if (user.nick == sender) { - return user.isOp + override fun onDisconnect(event: DisconnectEvent?) { + with(event!!.getBot<PircBotX>()) { + LinksMgr.twitter.notification("$nick disconnected from irc://$serverHostname") + } + LinksMgr.twitter.shutdown() + } + + override fun onPrivateMessage(event: PrivateMessageEvent?) { + if (logger.isTraceEnabled) logger.trace("<<< ${event!!.user!!.nick}: ${event.message}") + val cmds = event!!.message.trim().split(" ".toRegex(), 2) + val cmd = cmds[0].lowercase() + val args = if (cmds.size > 1) { + cmds[1].trim() + } else "" + if (cmd.startsWith(Constants.HELP_CMD)) { // help + helpResponse(event, args) + } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module + helpDefault(event) + } + } + + override fun onJoin(event: JoinEvent?) { + with(event!!.getBot<PircBotX>()) { + if (event.user!!.nick == nick) { + LinksMgr.twitter.notification("$nick has joined ${event.channel.name} on irc://$serverHostname") + } else { + tell.send(event) } } - return false } - /** - * Joins the bot's channel. - */ - private fun joinChannel() { - joinChannel(channel) - twitter.notification("$name ${ReleaseInfo.VERSION} has joined $channel") - } - - override fun onDisconnect() { - if (weblogUrl.isNotBlank()) { - version = weblogUrl - } - @Suppress("MagicNumber") - sleep(5) - connect() - } - - override fun onMessage( - channel: String, - sender: String, - login: String, - hostname: String, - message: String - ) { - if (logger.isDebugEnabled) logger.debug(">>> $sender: $message") - tell.send(sender, true) - if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command> + override fun onMessage(event: MessageEvent?) { + val sender = event!!.user!!.nick + val message = event.message + tell.send(event) + if (message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command> + if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) val cmd = cmds[0].lowercase() val args = if (cmds.size > 1) { cmds[1].trim() } else "" if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help - helpResponse(sender, args, false) + helpResponse(event, args) } else { // Execute module or command - addons.exec(sender, login, cmd, args, isOp(sender), false) + addons.exec(channel, cmd, args, event) } - } else { - // Links, e.g.: https://www.example.com/ or L1: , etc. - addons.match(sender, login, message, isOp(sender), false) + } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. + if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") } storeRecap(sender, message, false) } - override fun onPrivateMessage( - sender: String, - login: String, - hostname: String, - message: String - ) { - if (logger.isDebugEnabled) logger.debug(">>> $sender : $message") - val cmds = message.trim().split(" ".toRegex(), 2) - val cmd = cmds[0].lowercase() - val args = if (cmds.size > 1) { - cmds[1].trim() - } else "" - val isOp = isOp(sender) - if (cmd.startsWith(Constants.HELP_CMD)) { // help - helpResponse(sender, args, true) - } else if (isOp && Constants.KILL_CMD == cmd) { // kill - twitter.notification("$name killed by $sender on $channel") - sendRawLine("QUIT :Poof!") - exitProcess(0) - } else if (isOp && Constants.DIE_CMD == cmd) { // die - send("$sender has just signed my death sentence.") - timer.cancel() - twitter.shutdown() - twitter.notification("$name stopped by $sender on $channel") - @Suppress("MagicNumber") - sleep(3) - quitServer("The Bot Is Out There!") - exitProcess(0) - } else if (!addons.exec(sender, login, cmd, args, isOp, true)) { // Execute command or module - helpDefault(sender, isOp, true) - } + override fun onNickChange(event: NickChangeEvent?) { + tell.send(event!!) } - override fun onAction(sender: String, login: String, hostname: String, target: String, action: String) { - if (channel == target) { - storeRecap(sender, action, true) - } - } - - override fun onJoin(channel: String, sender: String, login: String, hostname: String) { - tell.send(sender) - } - - override fun onNickChange(oldNick: String, login: String, hostname: String, newNick: String) { - tell.send(newNick) - } - - /** - * Sends a private message or notice. - */ - fun send(sender: String, message: String?, isPrivate: Boolean) { - if (message != null && sender.isNotBlank()) { - if (isPrivate) { - if (logger.isDebugEnabled) logger.debug("Sending message to $sender : $message") - sendMessage(sender, message) - } else { - if (logger.isDebugEnabled) logger.debug("Sending notice to $sender: $message") - sendNotice(sender, message) + override fun onPart(event: PartEvent?) { + with(event!!.getBot<PircBotX>()) { + if (event.user!!.nick == nick) { + LinksMgr.twitter.notification("$nick has left ${event.channel.name} on irc://$serverHostname") } } } - /** - * Sends a notice to the channel. - */ - fun send(notice: String?) { - notice?.let { send(channel, it, false) } - } - - /** - * Sends a message. - */ - fun send(who: String, message: Message) { - send(if (message.isNotice) who else channel, message.msg, message.color, message.isPrivate) - } - - /** - * Sends a message. - */ - fun send(who: String, message: String, color: String, isPrivate: Boolean) { - send(who, colorize(message, color), isPrivate) - } - - /** - * Send a formatted commands/modules, etc. list. - */ - @JvmOverloads - fun sendList( - nick: String, - list: List<String>, - maxPerLine: Int, - separator: String = " ", - isPrivate: Boolean, - isBold: Boolean = false, - isIndent: Boolean = false - ) { - var i = 0 - while (i < list.size) { - send( - nick, - helpFormat( - list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), - isBold, - isIndent - ), - isPrivate - ) - i += maxPerLine - } - } - - /** - * Sets the pinboard authentication. - */ - private fun setPinboardAuth(apiToken: String) { - if (apiToken.isNotBlank()) { - pinboard.apiToken = apiToken - isPinboardEnabled = true - if (logger.isDebugEnabled) { - val consoleHandler = ConsoleHandler() - consoleHandler.level = java.util.logging.Level.FINE - pinboard.logger.addHandler(consoleHandler) - pinboard.logger.level = java.util.logging.Level.FINE - } - } - } - - /** - * Shutdown the bot. - */ - fun shutdown(sender: String, now: Boolean = false) { - if (now) { // kill - twitter.notification("$name killed by $sender on $channel") - sendRawLine("QUIT :Poof!") - } else { - timer.cancel() - twitter.shutdown() - twitter.notification("$name stopped by $sender on $channel") - @Suppress("MagicNumber") - sleep(3) - quitServer("The Bot Is Out There!") - } - exitProcess(0) - } - - /** - * Sleeps for the specified number of seconds. - */ - fun sleep(secs: Int) { - try { - @Suppress("MagicNumber") - Thread.sleep(secs * 1000L) - } catch (ignore: InterruptedException) { - // Do nothing - } - } - - /** - * Updates pin on pinboard. - */ - fun updatePin(oldUrl: String, entry: EntryLink) { - if (isPinboardEnabled) { - PinboardUtils.updatePin(pinboard, ircServer, oldUrl, entry) - } - } - - /** - * Returns the bot's version. - */ - override fun onVersion(sourceNick: String, sourceLogin: String, sourceHostname: String, target: String) { - sendRawLine("NOTICE $sourceNick :\u0001VERSION ${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}\u0001") - } - companion object { - // Maximum number of times the bot will try to reconnect, if disconnected - private const val MAX_RECONNECT = 10 - - /** - * The Truth is Out There! - */ + @Throws(Exception::class) @JvmStatic fun main(args: Array<String>) { // Set up the command line options @@ -593,7 +307,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert val stdout = PrintStream( BufferedOutputStream( FileOutputStream( - logsDir + channel.substring(1) + '.' + today() + ".log", true + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true ) ), true ) @@ -615,11 +329,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } } - // Create the bot - val bot = Mobibot(nickname, channel, logsDir, p) - - // Connect - bot.connect() + // Start the bot + Mobibot(nickname, channel, logsDir, p).connect() } } } @@ -629,97 +340,93 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert * Initialize the bot. */ init { - System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", Constants.CONNECT_TIMEOUT.toString()) - System.getProperties().setProperty("sun.net.client.defaultReadTimeout", Constants.CONNECT_TIMEOUT.toString()) - - name = nickname - ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) - ircPort = p.getIntProperty("port", Constants.DEFAULT_PORT) this.channel = channel - logsDir = logsDirPath + val ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) + config = Configuration.Builder().apply { + name = nickname + login = p.getProperty("login", nickname) + realName = p.getProperty("realname", nickname) + addServer( + ircServer, + p.getIntProperty("port", Constants.DEFAULT_PORT) + ) + addAutoJoinChannel(channel) + addListener(this@Mobibot) + version = "${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}" + isAutoNickChange = true + val identPwd = p.getProperty("ident") + if (!identPwd.isNullOrBlank()) { + nickservPassword = identPwd + } + val identNick = p.getProperty("ident-nick") + if (!identNick.isNullOrBlank()) { + nickservNick = identNick + } + val identMsg = p.getProperty("ident-msg") + if (!identMsg.isNullOrBlank()) { + nickservCustomMessage = identMsg + } + isAutoReconnect = true + //socketConnectTimeout = Constants.CONNECT_TIMEOUT + //socketTimeout = Constants.CONNECT_TIMEOUT + //messageDelay = StaticDelay(500) + }.buildConfiguration() - // Store the default logger level - loggerLevel = logger.level + // Load the current entries + with(LinksMgr) { + entries.channel = channel + entries.ircServer = ircServer + entries.logsDir = logsDirPath + entries.backlogs = p.getProperty("backlogs", "") + entries.load() - setVerbose(true) - setAutoNickChange(true) - login = p.getProperty("login", name) - // Set the real name - version = ReleaseInfo.PROJECT - // setMessageDelay(1000); - - // Set NICKSERV identification - identPwd = p.getProperty("ident", "") - identNick = p.getProperty("ident-nick", "") - identMsg = p.getProperty("ident-msg", "") - - // Set the URLs - weblogUrl = p.getProperty("weblog", "") - backlogsUrl = p.getProperty("backlogs", weblogUrl).appendIfMissing('/') - - // Load the current entries and backlogs, if any - try { - LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) - if (logger.isDebugEnabled) logger.debug("Last feed: ${LinksMgr.startDate}") - } catch (e: Exception) { - if (logger.isErrorEnabled) logger.error("An error occurred while loading the logs.", e) + // Set up pinboard + pinboard.setApiToken(p.getProperty("pinboard-api-token", "")) } - // Set the pinboard authentication - setPinboardAuth(p.getProperty("pinboard-api-token", "")) - // Load the commands - addons.add(AddLog(this), p) - addons.add(ChannelFeed(this, channelName), p) - addons.add(Cycle(this), p) - addons.add(Die(this), p) - addons.add(Debug(this), p) - addons.add(Ignore(this), p) - addons.add(Info(this), p) - addons.add(Kill(this), p) - addons.add(Me(this), p) - addons.add(Modules(this), p) - addons.add(Msg(this), p) - addons.add(Nick(this), p) - addons.add(Recap(this), p) - addons.add(Say(this), p) - addons.add(Users(this), p) - addons.add(Versions(this), p) + addons.add(ChannelFeed(channel.removePrefix("#")), p) + addons.add(Comment(), p) + addons.add(Cycle(), p) + addons.add(Die(), p) + addons.add(Ignore(), p) + addons.add(LinksMgr(), p) + addons.add(Me(), p) + addons.add(Msg(), p) + addons.add(Nick(), p) + addons.add(Posting(), p) + addons.add(Recap(), p) + addons.add(Say(), p) + addons.add(Tags(), p) // Tell command - tell = Tell(this) + tell = Tell("${logsDirPath}${nickname}.ser") addons.add(tell, p) - // Load the links commands - addons.add(Comment(this), p) - addons.add(Posting(this), p) - addons.add(Tags(this), p) - addons.add(LinksMgr(this), p) - addons.add(View(this), p) + addons.add(LinksMgr.twitter, p) + addons.add(Users(), p) + addons.add(Versions(), p) + addons.add(View(), p) // Load the modules - addons.add(Calc(this), p) - addons.add(CryptoPrices(this), p) - addons.add(CurrencyConverter(this), p) - addons.add(Dice(this), p) - addons.add(GoogleSearch(this), p) - addons.add(Joke(this), p) - addons.add(Lookup(this), p) - addons.add(Ping(this), p) - addons.add(RockPaperScissors(this), p) - addons.add(StockQuote(this), p) - addons.add(War(this), p) - addons.add(Weather2(this), p) - addons.add(WorldTime(this), p) - - // Twitter module - twitter = Twitter(this) - addons.add(twitter, p) + addons.add(Calc(), p) + addons.add(CryptoPrices(), p) + addons.add(CurrencyConverter(), p) + addons.add(Dice(), p) + addons.add(GoogleSearch(), p) + addons.add(Info(tell), p) + addons.add(Joke(), p) + addons.add(Lookup(), p) + addons.add(Modules(addons.modulesNames), p) + addons.add(Ping(), p) + addons.add(RockPaperScissors(), p) + addons.add(StockQuote(), p) + addons.add(Weather2(), p) + addons.add(WorldTime(), p) + addons.add(War(), p) // Sort the addons addons.sort() - - // Save the entries - LinksMgr.saveEntries(this, true) } } + diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt similarity index 63% rename from src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 2d91b64..8083c74 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/PinboardUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,5 +1,5 @@ /* - * PinboardUtils.kt + * Pinboard.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -45,33 +45,44 @@ import java.util.Date /** * Handles posts to pinboard.in. */ -object PinboardUtils { +class Pinboard { + private val poster = PinboardPoster() + /** * Adds a pin. */ - @JvmStatic - fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) { - runBlocking { - launch { - poster.addPin( - entry.link, - entry.title, - entry.postedBy(ircServer), - entry.pinboardTags, - entry.date.toTimestamp() - ) + fun addPin(ircServer: String, entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + runBlocking { + launch { + poster.addPin( + entry.link, + entry.title, + entry.postedBy(ircServer), + entry.pinboardTags, + entry.date.toTimestamp() + ) + } } } } + /** + * Sets the pinboard API token. + */ + fun setApiToken(apiToken: String) { + poster.apiToken = apiToken + } + /** * Deletes a pin. */ - @JvmStatic - fun deletePin(poster: PinboardPoster, entry: EntryLink) { - runBlocking { - launch { - poster.deletePin(entry.link) + fun deletePin(entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + runBlocking { + launch { + poster.deletePin(entry.link) + } } } } @@ -79,30 +90,15 @@ object PinboardUtils { /** * Updates a pin. */ - @JvmStatic - fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) { - runBlocking { - launch { - with(entry) { - if (oldUrl != link) { - poster.deletePin(oldUrl) - poster.addPin( - link, - title, - entry.postedBy(ircServer), - pinboardTags, - date.toTimestamp() - ) - } else { - poster.addPin( - link, - title, - entry.postedBy(ircServer), - pinboardTags, - date.toTimestamp(), - replace = true, - shared = true - ) + fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + runBlocking { + launch { + with(entry) { + if (oldUrl != link) { + poster.deletePin(oldUrl) + } + poster.addPin(link, title, entry.postedBy(ircServer), pinboardTags, date.toTimestamp()) } } } @@ -112,8 +108,7 @@ object PinboardUtils { /** * Format a date to a UTC timestamp. */ - @JvmStatic - fun Date.toTimestamp(): String { + private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( this.toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt index 31bde39..a96b491 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt @@ -95,7 +95,6 @@ object TwitterOAuth { """.trimIndent() ) } catch (te: TwitterException) { - @Suppress("MagicNumber") if (401 == te.statusCode) { println("Unable to get the access token.") } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt index 6b3a4b3..374f301 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt @@ -32,10 +32,11 @@ package net.thauvin.erik.mobibot +import net.thauvin.erik.mobibot.modules.Twitter import java.util.TimerTask -class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() { +class TwitterTimer(private var twitter: Twitter, private var index: Int) : TimerTask() { override fun run() { - bot.twitter.postEntry(index) + twitter.postEntry(index) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index a16c01b..6f75788 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -31,9 +31,13 @@ */ package net.thauvin.erik.mobibot +import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import org.jibble.pircbot.Colors import org.jsoup.Jsoup +import org.pircbotx.Colors +import org.pircbotx.PircBotX +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent import java.io.BufferedReader import java.io.IOException import java.io.InputStreamReader @@ -85,6 +89,13 @@ object Utils { @JvmStatic fun bold(s: String?): String = colorize(s, Colors.BOLD) + /** + * Returns the [PircBotX] instance. + */ + fun GenericMessageEvent.bot(): PircBotX { + return getBot() as PircBotX + } + /** * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's * nick. @@ -159,6 +170,14 @@ object Utils { return if (isIndent) s.prependIndent() else s } + /** + * Returns {@code true} if the specified user is an operator on the [channel]. + */ + @JvmStatic + fun isChannelOp(channel: String, event: GenericMessageEvent): Boolean { + return event.bot().userChannelDao.getChannel(channel).isOp(event.user) + } + /** * Obfuscates the given string. */ @@ -203,6 +222,56 @@ object Utils { @JvmStatic fun reverseColor(s: String?): String = colorize(s, Colors.REVERSE) + /** + * Send a formatted commands/modules, etc. list. + */ + @JvmStatic + fun GenericMessageEvent.sendList( + list: List<String>, + maxPerLine: Int, + separator: String = " ", + isBold: Boolean = false, + isIndent: Boolean = false + ) { + var i = 0 + while (i < list.size) { + sendMessage( + helpFormat( + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), + isBold, + isIndent + ), + ) + i += maxPerLine + } + } + + /** + * Sends a [message]. + */ + @JvmStatic + fun GenericMessageEvent.sendMessage(channel: String, message: Message) { + if (message.isNotice) { + bot().sendIRC().notice(user.nick, colorize(message.msg, message.color)) + } else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) { + respondPrivateMessage(colorize(message.msg, message.color)) + } else { + bot().sendIRC().message(channel, colorize(message.msg, message.color)) + } + } + + /** + * Sends a response as a private message or notice. + */ + @JvmStatic + fun GenericMessageEvent.sendMessage(message: String) { + if (this is PrivateMessageEvent) { + respondPrivateMessage(message) + } else { + bot().sendIRC().notice(user.nick, message) + } + } + /** * Returns today's date. */ @@ -258,7 +327,6 @@ object Utils { /** * Converts milliseconds to year month week day hour and minutes. */ - @Suppress("MagicNumber") @JvmStatic fun uptime(uptime: Long): String { val info = StringBuilder() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 7675fe5..425f309 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -32,31 +32,31 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent import java.util.concurrent.ConcurrentHashMap -abstract class AbstractCommand(val bot: Mobibot) { +abstract class AbstractCommand { abstract val name: String abstract val help: List<String> - abstract val isOp: Boolean + abstract val isOpOnly: Boolean abstract val isPublic: Boolean abstract val isVisible: Boolean val properties: MutableMap<String, String> = ConcurrentHashMap() - abstract fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) + abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent) - open fun helpResponse(command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean { - if (!this.isOp || this.isOp == isOp) { + open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + if (!isOpOnly || isOpOnly == isChannelOp(channel, event)) { for (h in help) { - bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) + event.sendMessage( + buildCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic), + ) } return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt deleted file mode 100644 index f13aa4d..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AddLog.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * AddLog.kt - * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Mobibot - -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.history -import net.thauvin.erik.mobibot.entries.EntriesMgr -import java.io.File - -class AddLog(bot: Mobibot) : AbstractCommand(bot) { - override val name = "addlog" - override val help = emptyList<String>() - override val isOp = true - override val isPublic = false - override val isVisible = false - - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - if (args.isNotBlank()) { - // e.g: 2014-04-01 - val backlog = File("${bot.logsDir}$args${EntriesMgr.XML_EXT}") - if (backlog.exists()) { - history.add(0, args) - } else { - bot.send(sender, "The specified log could not be found.", isPrivate) - return - } - } - @Suppress("MagicNumber") - bot.sendList(sender, history, 4, isPrivate = isPrivate, isIndent = true) - } - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 8dbe942..5ac8d7f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -35,13 +35,14 @@ package net.thauvin.erik.mobibot.commands import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.FeedReader -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.types.GenericMessageEvent -class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { +class ChannelFeed(channel: String) : AbstractCommand() { override val name = channel override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel")) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -53,22 +54,16 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) { initProperties(FEED_PROP) } - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { with(properties[FEED_PROP]) { if (!isNullOrBlank()) { runBlocking { launch { - FeedReader(bot, sender, this@with).run() + FeedReader(this@with, event).run() } } } else { - bot.send(sender, "There is no feed setup for this channel.", false) + event.sendMessage("There is no feed setup for this channel.") } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 88de89b..e5b019d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -32,35 +32,33 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent -class Cycle(bot: Mobibot) : AbstractCommand(bot) { - @Suppress("MagicNumber") +class Cycle : AbstractCommand() { private val wait = 10 override val name = "cycle" override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name")) - override val isOp = true + override val isOpOnly = true override val isPublic = false override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - with(bot) { - if (isOp) { - send("$sender has just asked me to leave. I'll be back!") - sleep(wait) - partChannel(channel) - sleep(wait) - joinChannel(channel) + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + if (isChannelOp(channel, event)) { + runBlocking { + sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!") + userChannelDao.getChannel(channel).send().part() + delay(wait * 1000L) + sendIRC().joinChannel(channel) + } } else { - helpDefault(sender, isOp, isPrivate) + helpResponse(channel, args, event) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Debug.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Debug.kt deleted file mode 100644 index 8c3c97b..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Debug.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Debug.kt - * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Mobibot -import org.apache.logging.log4j.Level -import org.apache.logging.log4j.core.config.Configurator - -class Debug(bot: Mobibot) : AbstractCommand(bot) { - override val name = Constants.DEBUG_CMD - override val help = emptyList<String>() - override val isOp = true - override val isPublic = false - override val isVisible = false - - override fun commandResponse(sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean) { - if (isOp) { - with(bot) { - if (logger.isDebugEnabled) { - Configurator.setLevel(logger.name, loggerLevel) - } else { - Configurator.setLevel(logger.name, Level.DEBUG) - } - send(sender, "Debug logging is " + if (logger.isDebugEnabled) "enabled." else "disabled.", true) - } - } - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index c9c6299..df715c4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -32,25 +32,35 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent -class Die(bot: Mobibot) : AbstractCommand(bot) { +class Die : AbstractCommand() { override val name = "die" override val help = emptyList<String>() - override val isOp = true + override val isOpOnly = true override val isPublic = false override val isVisible = false - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - bot.send("$sender has just signed my death sentence.") - bot.shutdown(sender) + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + if (isChannelOp(channel, event) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) { + sendIRC().message(channel, "${event.user?.nick} has just signed my death sentence.") + stopBotReconnect() + sendIRC().quitServer("The Bot is Out There!") + } } } + + companion object { + /** + * Max days property. + */ + const val DIE_PROP = "die" + } + + init { + initProperties(DIE_PROP) + } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 7ae45a2..d1e38ce 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -32,13 +32,17 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.links.LinksMgr +import org.pircbotx.hooks.types.GenericMessageEvent -class Ignore(bot: Mobibot) : AbstractCommand(bot) { +class Ignore : AbstractCommand() { private val me = "me" init { @@ -58,7 +62,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) ) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -73,56 +77,45 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { } } - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { val isMe = args.trim().equals(me, true) - if (isMe || !isOp) { - val nick = sender.lowercase() - ignoreNick(bot, nick, isMe, isPrivate) + if (isMe || !isChannelOp(channel, event)) { + val nick = event.user.nick.lowercase() + ignoreNick(nick, isMe, event) } else { - ignoreOp(bot, sender, args, isPrivate) + ignoreOp(args, event) } } - override fun helpResponse( - command: String, - sender: String, - isOp: Boolean, - isPrivate: Boolean - ): Boolean { - return if (isOp) { + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + return if (isChannelOp(channel, event)) { for (h in helpOp) { - bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivate), isPrivate) + event.sendMessage(buildCmdSyntax(h, event.bot().nick, true)) } true } else { - super.helpResponse(command, sender, isOp, isPrivate) + super.helpResponse(channel, topic, event) } } - private fun ignoreNick(bot: Mobibot, sender: String, isMe: Boolean, isPrivate: Boolean) { + private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) { if (isMe) { if (ignored.remove(sender)) { - bot.send(sender, "You are no longer ignored.", isPrivate) + event.sendMessage("You are no longer ignored.") } else { ignored.add(sender) - bot.send(sender, "You are now ignored.", isPrivate) + event.sendMessage("You are now ignored.") } } else { if (ignored.contains(sender)) { - bot.send(sender, "You are currently ignored.", isPrivate) + event.sendMessage("You are currently ignored.") } else { - bot.send(sender, "You are not currently ignored.", isPrivate) + event.sendMessage("You are not currently ignored.") } } } - private fun ignoreOp(bot: Mobibot, sender: String, args: String, isPrivate: Boolean) { + private fun ignoreOp(args: String, event: GenericMessageEvent) { if (args.isNotEmpty()) { val nicks = args.lowercase().split(" ") for (nick in nicks) { @@ -138,11 +131,10 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) { } if (ignored.size > 0) { - bot.send(sender, "The following nicks are ignored:", isPrivate) - @Suppress("MagicNumber") - bot.sendList(sender, ignored.sorted(), 8, isPrivate = isPrivate, isIndent = true) + event.sendMessage("The following nicks are ignored:") + event.sendList(ignored.sorted(), 8, isIndent = true) } else { - bot.send(sender, "No one is currently ${bold("ignored")}.", isPrivate) + event.sendMessage("No one is currently ${bold("ignored")}.") } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index bffd1f2..a383756 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -31,50 +31,46 @@ */ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.ReleaseInfo import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.commands.links.LinksMgr +import net.thauvin.erik.mobibot.commands.tell.Tell +import org.pircbotx.hooks.types.GenericMessageEvent import java.lang.management.ManagementFactory -class Info(bot: Mobibot?) : AbstractCommand(bot!!) { +class Info(private val tell: Tell) : AbstractCommand() { private val allVersions = listOf( "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${green(ReleaseInfo.WEBSITE)})", "Written by ${ReleaseInfo.AUTHOR} (${green(ReleaseInfo.AUTHOR_URL)})" ) override val name = "info" override val help = listOf("To view information about the bot:", helpFormat("%c $name")) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - with(bot) { - sendList(sender, allVersions, 1, isPrivate = isPrivate) - val info = StringBuilder() - info.append("Uptime: ") - .append(uptime(ManagementFactory.getRuntimeMXBean().uptime)) - .append(" [Entries: ") - .append(LinksMgr.entries.size) - if (isOp) { - if (tell.isEnabled()) { - info.append(", Messages: ").append(tell.size()) - } - if (twitter.isAutoPost) { - info.append(", Twitter: ").append(twitter.entriesCount()) - } + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + event.sendList(allVersions, 1) + val info = StringBuilder() + info.append("Uptime: ") + .append(uptime(ManagementFactory.getRuntimeMXBean().uptime)) + .append(" [Entries: ") + .append(LinksMgr.entries.links.size) + if (isChannelOp(channel, event)) { + if (tell.isEnabled()) { + info.append(", Messages: ").append(tell.size()) + } + if (LinksMgr.twitter.isAutoPost) { + info.append(", Twitter: ").append(LinksMgr.twitter.entriesCount()) } - info.append(", Recap: ").append(Recap.recaps.size).append(']') - send(sender, info.toString(), isPrivate) } + info.append(", Recap: ").append(Recap.recaps.size).append(']') + event.sendMessage(info.toString()) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index 4b996f1..7a85f4f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -32,27 +32,21 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent -class Me(bot: Mobibot) : AbstractCommand(bot) { +class Me : AbstractCommand() { override val name = "me" override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>")) - override val isOp = true + override val isOpOnly = true override val isPublic = false override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - bot.action(args) - } else { - bot.helpDefault(sender, isOp, isPrivate) + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isChannelOp(channel, event)) { + event.bot().sendIRC().action(channel, args) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 3bd2db1..36a4baf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -32,35 +32,29 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import org.pircbotx.hooks.types.GenericMessageEvent -class Modules(bot: Mobibot) : AbstractCommand(bot) { +class Modules(private val modulesNames: List<String>) : AbstractCommand() { override val name = "modules" override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name")) - override val isOp = true + override val isOpOnly = true override val isPublic = false override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - with(bot) { - if (isOp) { - if (modulesNames.isEmpty()) { - send(sender, "There are no enabled modules.", isPrivate) - } else { - send(sender, "The enabled modules are: ", isPrivate) - @Suppress("MagicNumber") - sendList(sender, modulesNames, 7, isPrivate = isPrivate, isIndent = true) - } + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isChannelOp(channel, event)) { + if (modulesNames.isEmpty()) { + event.respondPrivateMessage("There are no enabled modules.") } else { - helpDefault(sender, isOp, isPrivate) + event.respondPrivateMessage("The enabled modules are: ") + event.sendList(modulesNames, 7, isIndent = true) } + } else { + helpResponse(channel, args, event) } } } + diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 5e9b5ae..edd9cc9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -32,35 +32,30 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent -class Msg(bot: Mobibot) : AbstractCommand(bot) { +class Msg : AbstractCommand() { override val name = "msg" override val help = listOf( "To have the bot send a private message to someone:", helpFormat("%c $name <nick> <text>") ) - override val isOp = true - override val isPublic = true + override val isOpOnly = true + override val isPublic = false override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isChannelOp(channel, event)) { val msg = args.split(" ", limit = 2) if (args.length > 2) { - bot.send(msg[0], msg[1], isPrivate) + event.bot().sendIRC().message(msg[0], msg[1]) + event.respondPrivateMessage("A message was sent to ${msg[0]}") } else { - helpResponse(name, sender, isOp, isPrivate) + helpResponse(channel, args, event) } - } else { - bot.helpDefault(sender, isOp, isPrivate) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 2f956e2..c8275dd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -32,27 +32,21 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent -class Nick(bot: Mobibot) : AbstractCommand(bot) { +class Nick : AbstractCommand() { override val name = "nick" override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>")) - override val isOp = true + override val isOpOnly = true override val isPublic = true override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - bot.changeNick(args) - } else { - bot.helpDefault(sender, isOp, isPrivate) + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isChannelOp(channel, event)) { + event.bot().sendIRC().changeNick(args) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 5912ff4..03b349c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -32,19 +32,20 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.toUtcDateTime +import org.pircbotx.hooks.types.GenericMessageEvent import java.time.Clock import java.time.LocalDateTime -class Recap(bot: Mobibot) : AbstractCommand(bot) { +class Recap : AbstractCommand() { override val name = "recap" override val help = listOf( "To list the last 10 public channel messages:", helpFormat("%c $name") ) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -61,26 +62,19 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) { LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + " - $sender" + (if (isAction) " " else ": ") + message ) - @Suppress("MagicNumber") if (recaps.size > 10) { recaps.removeFirst() } } } - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (recaps.isNotEmpty()) { for (r in recaps) { - bot.send(sender, r, isPrivate) + event.sendMessage(r) } } else { - bot.send(sender, "Sorry, nothing to recap.", isPrivate) + event.sendMessage("Sorry, nothing to recap.") } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 198ea8c..a89c64e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -32,28 +32,22 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent -class Say(bot: Mobibot) : AbstractCommand(bot) { +class Say : AbstractCommand() { override val name = "say" override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name <text>")) - override val isOp = true + override val isOpOnly = true override val isPublic = false override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - bot.send(args) - } else { - bot.helpDefault(sender, isOp, isPrivate) + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isChannelOp(channel, event)) { + event.bot().sendIRC().message(channel, args) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 82f5277..5133146 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -32,36 +32,29 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendList +import org.pircbotx.hooks.types.GenericMessageEvent -class Users(bot: Mobibot) : AbstractCommand(bot) { +class Users : AbstractCommand() { override val name = "users" override val help = listOf("To list the users present on the channel:", helpFormat("%c $name")) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { val nicks = mutableListOf<String>() - with(bot) { - getUsers(channel).forEach { user -> - if (isOp(user.nick)) { - nicks.add("@${user.nick}") - } else { - nicks.add(user.nick) - } + val ch = event.bot().userChannelDao.getChannel(channel) + ch.users.forEach { + if (it.channelsOpIn.contains(ch)) { + nicks.add("@${it.nick}") + } else { + nicks.add(it.nick) } - - @Suppress("MagicNumber") - sendList(sender, nicks.sorted(), 8, isPrivate = isPrivate, isIndent = true) } + event.sendList(nicks, 8) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 1712add..f7fa866 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -31,12 +31,14 @@ */ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.ReleaseInfo import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import org.pircbotx.hooks.types.GenericMessageEvent -class Versions(bot: Mobibot) : AbstractCommand(bot) { +class Versions : AbstractCommand() { private val allVersions = listOf( "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") @@ -45,21 +47,13 @@ class Versions(bot: Mobibot) : AbstractCommand(bot) { ) override val name = "versions" override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) - override val isOp = true + override val isOpOnly = true override val isPublic = false override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - bot.sendList(sender, allVersions, 1, isPrivate = isPrivate) - } else { - bot.helpDefault(sender, false, isPrivate) + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isChannelOp(channel, event)) { + event.sendList(allVersions, 1) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 2bfc57b..61143fb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -33,14 +33,16 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent -class Comment(bot: Mobibot) : AbstractCommand(bot) { +class Comment : AbstractCommand() { override val name = COMMAND override val help = listOf( "To add a comment:", @@ -51,7 +53,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { "To delete a comment, use its label and a minus sign: ", helpFormat("${Constants.LINK_CMD}1.1:-") ) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -59,29 +61,22 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { const val COMMAND = "comment" } - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - @Suppress("MagicNumber") + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { val cmds = args.substring(1).split("[.:]".toRegex(), 3) - val index = cmds[0].toInt() - 1 + val entryIndex = cmds[0].toInt() - 1 - if (index < LinksMgr.entries.size) { - val entry: EntryLink = LinksMgr.entries[index] + if (entryIndex < LinksMgr.entries.links.size && LinksMgr.isUpToDate(event)) { + val entry: EntryLink = LinksMgr.entries.links[entryIndex] val commentIndex = cmds[1].toInt() - 1 if (commentIndex < entry.comments.size) { when (val cmd = cmds[2].trim()) { - "" -> showComment(bot, entry, index, commentIndex) // L1.1: - "-" -> deleteComment(bot, sender, isOp, entry, index, commentIndex) // L11:- + "" -> showComment(entry, entryIndex, commentIndex, event) // L1.1: + "-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:- else -> { if (cmd.startsWith('?')) { // L1.1:?<author> - changeAuthor(bot, cmd, sender, isOp, entry, index, commentIndex) + changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event) } else { // L1.1:<comment> - setComment(bot, cmd, sender, entry, index, commentIndex) + setComment(cmd, entry, entryIndex, commentIndex, event) } } } @@ -89,20 +84,11 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { } } - override fun helpResponse( - command: String, - sender: String, - isOp: Boolean, - isPrivate: Boolean - ): Boolean { - if (super.helpResponse(command, sender, isOp, isPrivate)) { - if (isOp) { - bot.send(sender, "To change a comment's author:", isPrivate) - bot.send( - sender, - helpFormat("${Constants.LINK_CMD}1.1:?<nick>"), - isPrivate - ) + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + if (super.helpResponse(channel, topic, event)) { + if (isChannelOp(channel, event)) { + event.sendMessage("To change a comment's author:") + event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?<nick>")) } return true } @@ -114,49 +100,52 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) { } private fun changeAuthor( - bot: Mobibot, + channel: String, cmd: String, - sender: String, - isOp: Boolean, entry: EntryLink, - index: Int, - commentIndex: Int + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { - if (isOp && cmd.length > 1) { + if (isChannelOp(channel, event) && cmd.length > 1) { val comment = entry.getComment(commentIndex) comment.nick = cmd.substring(1) - bot.send(EntriesUtils.buildComment(index, commentIndex, comment)) - LinksMgr.saveEntries(bot, false) + event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment)) + LinksMgr.entries.save() } else { - bot.send(sender, "Please ask a channel op to change the author of this comment for you.", false) + event.sendMessage("Please ask a channel op to change the author of this comment for you.") } } private fun deleteComment( - bot: Mobibot, - sender: String, - isOp: Boolean, + channel: String, entry: EntryLink, - index: Int, - commentIndex: Int + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { - if (isOp || sender == entry.getComment(commentIndex).nick) { + if (isChannelOp(channel, event) || event.user.nick == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) - bot.send("Comment ${EntriesUtils.buildLinkCmd(index)}.${commentIndex + 1} removed.") - LinksMgr.saveEntries(bot, false) + event.sendMessage("Comment ${EntriesUtils.buildLinkLabel(entryIndex)}.${commentIndex + 1} removed.") + LinksMgr.entries.save() } else { - bot.send(sender, "Please ask a channel op to delete this comment for you.", false) + event.sendMessage("Please ask a channel op to delete this comment for you.") } } - private fun setComment(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int) { - entry.setComment(commentIndex, cmd, sender) - val comment = entry.getComment(commentIndex) - bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false) - LinksMgr.saveEntries(bot, false) + private fun setComment( + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent + ) { + entry.setComment(commentIndex, cmd, event.user.nick) + event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + LinksMgr.entries.save() } - private fun showComment(bot: Mobibot, entry: EntryLink, index: Int, commentIndex: Int) { - bot.send(EntriesUtils.buildComment(index, commentIndex, entry.getComment(commentIndex))) + private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) { + event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index fd7eb27..7bd95b8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -33,25 +33,29 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Pinboard import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.Ignore -import net.thauvin.erik.mobibot.entries.EntriesMgr +import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored +import net.thauvin.erik.mobibot.entries.Entries import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.modules.Twitter import org.jsoup.Jsoup +import org.pircbotx.hooks.types.GenericMessageEvent import java.io.IOException -class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { - private val keywords: MutableList<String> = mutableListOf() +class LinksMgr : AbstractCommand() { private val defaultTags: MutableList<String> = mutableListOf() + private val keywords: MutableList<String> = mutableListOf() override val name = Constants.LINK_CMD override val help = emptyList<String>() - override val isOp = false + override val isOpOnly = false override val isPublic = false override val isVisible = false @@ -65,52 +69,35 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { const val TAGS_PROP = "tags" const val TAG_MATCH = ", *| +" - // Entries array - @JvmField - val entries = mutableListOf<EntryLink>() + /** Entries array **/ + val entries = Entries() - // History/backlogs array - @JvmField - val history = mutableListOf<String>() + /** Pinboard handler. **/ + val pinboard = Pinboard() + /** Twitter handler. **/ + val twitter = Twitter() + + /** Let the user know if the entries are too old to be modified. **/ @JvmStatic - var startDate: String = today() - private set - - /** - * Saves the entries. - * - * @param isDayBackup Set the `true` if the daily backup file should also be created. - */ - @JvmStatic - fun saveEntries(bot: Mobibot, isDayBackup: Boolean) { - EntriesMgr.saveEntries(bot, entries, history, isDayBackup) - } - - @JvmStatic - fun startup(current: String, backlogs: String, channel: String) { - startDate = EntriesMgr.loadEntries(current, channel, entries) - if (today() != startDate) { - entries.clear() - startDate = today() + fun isUpToDate(event: GenericMessageEvent): Boolean { + if (entries.lastPubDate != today()) { + event.sendMessage("The links are too old to be updated.") + return false } - EntriesMgr.loadBacklogs(backlogs, history) + return true } } - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { val cmds = args.split(" ".toRegex(), 2) + val sender = event.user.nick + val botNick = event.bot().nick + val login = event.user.login - if (Ignore.isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(bot.nick))) { + if (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) { val link = cmds[0].trim() - if (!isDupEntry(bot, sender, link, isPrivate)) { - val isBackup = saveDayBackup(bot) + if (!isDupEntry(link, event)) { var title = "" val tags = ArrayList<String>(defaultTags) if (cmds.size == 2) { @@ -129,37 +116,32 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { matchTagKeywords(title, tags) } - entries.add(EntryLink(link, title, sender, login, bot.channel, tags)) - val index: Int = entries.size - 1 - val entry: EntryLink = entries[index] - bot.send(EntriesUtils.buildLink(index, entry)) + // Links are old, clear them + if (entries.lastPubDate != today()) { + entries.links.clear() + } - // Add Entry to pinboard. - bot.addPin(entry) + val entry = EntryLink(link, title, sender, login, channel, tags) + entries.links.add(entry) + val index = entries.links.lastIndexOf(entry) + event.sendMessage(EntriesUtils.buildLink(index, entry)) + + pinboard.addPin(event.bot().serverHostname, entry) // Queue link for posting to Twitter. - bot.twitter.queueEntry(index) + twitter.queueEntry(index) - saveEntries(bot, isBackup) + entries.save() if (Constants.NO_TITLE == entry.title) { - bot.send(sender, "Please specify a title, by typing:", isPrivate) - bot.send( - sender, - helpFormat("${EntriesUtils.buildLinkCmd(index)}:|This is the title"), - isPrivate - ) + event.sendMessage("Please specify a title, by typing:") + event.sendMessage(helpFormat("${EntriesUtils.buildLinkLabel(index)}:|This is the title")) } } } } - override fun helpResponse( - command: String, - sender: String, - isOp: Boolean, - isPrivate: Boolean - ): Boolean = false + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false override fun matches(message: String): Boolean { return message.matches(LINK_MATCH.toRegex()) @@ -180,12 +162,12 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { return Constants.NO_TITLE } - private fun isDupEntry(bot: Mobibot, sender: String, link: String, isPrivate: Boolean): Boolean { + private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean { synchronized(entries) { - for (i in entries.indices) { - if (link == entries[i].link) { - val entry: EntryLink = entries[i] - bot.send(sender, bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry), isPrivate) + for (i in entries.links.indices) { + if (link == entries.links[i].link) { + val entry: EntryLink = entries.links[i] + event.sendMessage(bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry)) return true } } @@ -202,17 +184,6 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) { } } - private fun saveDayBackup(bot: Mobibot): Boolean { - if (today() != startDate) { - saveEntries(bot, true) - entries.clear() - startDate = today() - return true - } - - return false - } - override fun setProperty(key: String, value: String) { super.setProperty(key, value) if (KEYWORDS_PROP == key) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 140f70c..2e7d9ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -33,15 +33,18 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent -class Posting(bot: Mobibot) : AbstractCommand(bot) { +class Posting : AbstractCommand() { override val name = "posting" override val help = listOf( "Post a URL, by saying it on a line on its own:", @@ -55,30 +58,27 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { "To edit a comment, see: ", helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { val cmds = args.substring(1).split(":", limit = 2) - val index = cmds[0].toInt() - 1 + val entryIndex = cmds[0].toInt() - 1 - if (index < entries.size) { - when (val cmd = cmds[1].trim()) { - "" -> showEntry(index) - "-" -> removeEntry(sender, login, isOp, index) // L1:- - else -> { + if (entryIndex < entries.links.size) { + val cmd = cmds[1].trim() + if (cmd.isBlank()) { + showEntry(entryIndex, event) // L1: + } else if (LinksMgr.isUpToDate(event)) { + if (cmd == "-") { + removeEntry(channel, entryIndex, event) // L1:- + } else { when (cmd[0]) { - '|' -> changeTitle(cmd, index) // L1:|<title> - '=' -> changeUrl(cmd, login, isOp, index) // L1:=<url> - '?' -> changeAuthor(cmd, sender, isOp, index) // L1:?<author> - else -> addComment(cmd, sender, index) // L1:<comment> + '|' -> changeTitle(cmd, entryIndex, event) // L1:|<title> + '=' -> changeUrl(channel, cmd, entryIndex, event) // L1:=<url> + '?' -> changeAuthor(channel, cmd, entryIndex, event) // L1:?<author> + else -> addComment(cmd, entryIndex, event) // L1:<comment> } } } @@ -89,73 +89,75 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) { return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex()) } - private fun addComment(cmd: String, sender: String, index: Int) { - val entry: EntryLink = entries[index] - val commentIndex = entry.addComment(cmd, sender) + private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[entryIndex] + val commentIndex = entry.addComment(cmd, event.user.nick) val comment = entry.getComment(commentIndex) - bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false) - LinksMgr.saveEntries(bot, false) + event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment)) + entries.save() } - private fun changeTitle(cmd: String, index: Int) { + private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) { if (cmd.length > 1) { - val entry: EntryLink = entries[index] + val entry: EntryLink = entries.links[entryIndex] entry.title = cmd.substring(1).trim() - bot.updatePin(entry.link, entry) - bot.send(EntriesUtils.buildLink(index, entry)) - LinksMgr.saveEntries(bot, false) + LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.buildLink(entryIndex, entry)) + entries.save() } } - private fun changeUrl(cmd: String, login: String, isOp: Boolean, index: Int) { - val entry: EntryLink = entries[index] - if (entry.login == login || isOp) { + private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[entryIndex] + if (entry.login == event.user.login || isChannelOp(channel, event)) { val link = cmd.substring(1) if (link.matches(LinksMgr.LINK_MATCH.toRegex())) { val oldLink = entry.link entry.link = link - bot.updatePin(oldLink, entry) - bot.send(EntriesUtils.buildLink(index, entry)) - LinksMgr.saveEntries(bot, false) + LinksMgr.pinboard.updatePin(event.bot().serverHostname, oldLink, entry) + event.sendMessage(EntriesUtils.buildLink(entryIndex, entry)) + entries.save() } } } - private fun changeAuthor(cmd: String, sender: String, isOp: Boolean, index: Int) { - if (isOp) { + private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) { + if (isChannelOp(channel, event)) { if (cmd.length > 1) { - val entry: EntryLink = entries[index] + val entry: EntryLink = entries.links[index] entry.nick = cmd.substring(1) - bot.send(EntriesUtils.buildLink(index, entry)) - LinksMgr.saveEntries(bot, false) + LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.buildLink(index, entry)) + entries.save() } } else { - bot.send(sender, "Please ask a channel op to change the author of this link for you.", false) + event.sendMessage("Please ask a channel op to change the author of this link for you.") } } - private fun removeEntry(sender: String, login: String, isOp: Boolean, index: Int) { - val entry: EntryLink = entries[index] - if (entry.login == login || isOp) { - bot.deletePin(index, entry) - entries.removeAt(index) - bot.send("Entry ${EntriesUtils.buildLinkCmd(index)} removed.") - LinksMgr.saveEntries(bot, false) + private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[index] + if (entry.login == event.user.login || isChannelOp(channel, event)) { + LinksMgr.pinboard.deletePin(entry) + LinksMgr.twitter.removeEntry(index) + entries.links.removeAt(index) + event.sendMessage("Entry ${EntriesUtils.buildLinkLabel(index)} removed.") + entries.save() } else { - bot.send(sender, "Please ask a channel op to remove this entry for you.", false) + event.sendMessage("Please ask a channel op to remove this entry for you.") } } - private fun showEntry(index: Int) { - val entry: EntryLink = entries[index] - bot.send(EntriesUtils.buildLink(index, entry)) + private fun showEntry(index: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[index] + event.sendMessage(EntriesUtils.buildLink(index, entry)) if (entry.tags.isNotEmpty()) { - bot.send(EntriesUtils.buildTags(index, entry)) + event.sendMessage(EntriesUtils.buildTags(index, entry)) } if (entry.comments.isNotEmpty()) { val comments = entry.comments for (i in comments.indices) { - bot.send(EntriesUtils.buildComment(index, i, comments[i])) + event.sendMessage(EntriesUtils.buildComment(index, i, comments[i])) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index ab7a983..2970033 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -33,19 +33,22 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent -class Tags(bot: Mobibot) : AbstractCommand(bot) { +class Tags : AbstractCommand() { override val name = COMMAND override val help = listOf( "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") ) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -53,33 +56,27 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) { const val COMMAND = "tags" } - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2) val index = cmds[0].toInt() - 1 - if (index < LinksMgr.entries.size) { + if (index < LinksMgr.entries.links.size && LinksMgr.isUpToDate(event)) { val cmd = cmds[1].trim() - val entry: EntryLink = LinksMgr.entries[index] + val entry: EntryLink = LinksMgr.entries.links[index] if (cmd.isNotEmpty()) { - if (entry.login == login || isOp) { + if (entry.login == event.user.login || isChannelOp(channel, event)) { entry.setTags(cmd) - bot.updatePin(entry.link, entry) - bot.send(EntriesUtils.buildTags(index, entry)) - LinksMgr.saveEntries(bot, false) + LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.buildTags(index, entry)) + LinksMgr.entries.save() } else { - bot.send(sender, "Please ask a channel op to change the tags for you.", isPrivate) + event.sendMessage("Please ask a channel op to change the tags for you.") } } else { if (entry.tags.isNotEmpty()) { - bot.send(EntriesUtils.buildTags(index, entry)) + event.sendMessage(EntriesUtils.buildTags(index, entry)) } else { - bot.send(sender, "The entry has no tags. Why don't add some?", isPrivate) + event.sendMessage("The entry has no tags. Why don't add some?") } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index e845a66..5d446d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -32,23 +32,25 @@ package net.thauvin.erik.mobibot.commands.links -import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries import net.thauvin.erik.mobibot.entries.EntriesUtils import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent -class View(bot: Mobibot) : AbstractCommand(bot) { - @Suppress("MagicNumber") - private val maxEntries = 8 +class View : AbstractCommand() { + private val maxEntries = 6 override val name = VIEW_CMD override val help = listOf( "To list or search the current URL posts:", helpFormat("%c $name [<start>] [<query>]") ) - override val isOp = false + override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -56,22 +58,16 @@ class View(bot: Mobibot) : AbstractCommand(bot) { const val VIEW_CMD = "view" } - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (entries.size != 0) { - showPosts(bot, args, sender) + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (entries.links.isNotEmpty()) { + showPosts(args, event) } else { - bot.send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate) + event.sendMessage("There is currently nothing to view. Why don't you post something?") } } - private fun showPosts(bot: Mobibot, args: String, sender: String) { - val max = entries.size + private fun showPosts(args: String, event: GenericMessageEvent) { + val max = entries.links.size var lcArgs = args.lowercase() var i = 0 if (lcArgs.isEmpty() && max > maxEntries) { @@ -97,25 +93,32 @@ class View(bot: Mobibot) : AbstractCommand(bot) { var entry: EntryLink var sent = 0 while (i < max && sent < maxEntries) { - entry = entries[i] + entry = entries.links[i] if (lcArgs.isNotBlank()) { if (entry.matches(lcArgs)) { - bot.send(sender, EntriesUtils.buildLink(i, entry, true), false) + event.sendMessage(EntriesUtils.buildLink(i, entry, true)) sent++ } } else { - bot.send(sender, EntriesUtils.buildLink(i, entry, true), false) + event.sendMessage(EntriesUtils.buildLink(i, entry, true)) sent++ } i++ if (sent == maxEntries && i < max) { - bot.send( - sender, "To view more, try: " + bold("${bot.nick}: $name ${i + 1} $lcArgs"), false + event.sendMessage("To view more, try: ") + event.sendMessage( + helpFormat( + buildCmdSyntax( + "%c $name ${i + 1} $lcArgs", + event.bot().nick, + event is PrivateMessageEvent + ) + ) ) } } if (sent == 0) { - bot.send(sender, "No matches. Please try again.", false) + event.sendMessage("No matches. Please try again.") } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index ddacd50..b063549 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -31,84 +31,88 @@ */ package net.thauvin.erik.mobibot.commands.tell -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.toIntOrDefault import net.thauvin.erik.mobibot.Utils.toUtcDateTime import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.View +import org.pircbotx.PircBotX +import org.pircbotx.hooks.events.MessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent +import org.pircbotx.hooks.types.GenericUserEvent /** * The `Tell` command. */ -class Tell(bot: Mobibot) : AbstractCommand(bot) { +class Tell(private val serialObject: String) : AbstractCommand() { // Messages queue private val messages: MutableList<TellMessage> = mutableListOf() - // Serialized object file - private val serializedObject: String - // Maximum number of days to keep messages - @Suppress("MagicNumber") private var maxDays = 7 // Message maximum queue size - @Suppress("MagicNumber") private var maxSize = 50 /** * Cleans the messages queue. */ private fun clean(): Boolean { - if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.") + // if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.") return TellMessagesMgr.clean(messages, maxDays.toLong()) } // Delete message. - private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) { + private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) { val split = args.split(" ") if (split.size == 2) { val id = split[1] var deleted = false if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { for (message in messages) { - if (message.sender.equals(sender, ignoreCase = true) && message.isReceived) { + if (message.sender.equals(event.user.nick, ignoreCase = true) && message.isReceived) { messages.remove(message) deleted = true } } if (deleted) { save() - bot.send(sender, "Delivered messages have been deleted.", isPrivate) + event.sendMessage("Delivered messages have been deleted.") } else { - bot.send(sender, "No delivered messages were found.", isPrivate) + event.sendMessage("No delivered messages were found.") } } else { var found = false for (message in messages) { found = (message.id == id) - if (found && (message.sender.equals(sender, ignoreCase = true) || bot.isOp(sender))) { + if (found && (message.sender.equals(event.user.nick, ignoreCase = true) || isChannelOp( + channel, + event + )) + ) { messages.remove(message) save() - bot.send(sender, "Your message was deleted from the queue.", isPrivate) + event.sendMessage("Your message was deleted from the queue.") deleted = true break } } if (!deleted) { if (found) { - bot.send(sender, "Only messages that you sent can be deleted.", isPrivate) + event.sendMessage("Only messages that you sent can be deleted.") } else { - bot.send(sender, "The specified message [ID $id] could not be found.", isPrivate) + event.sendMessage("The specified message [ID $id] could not be found.") } } } } else { - helpResponse(args, sender, isOp, isPrivate) + helpResponse(channel, args, event) } } @@ -124,30 +128,24 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { helpFormat("%c $name ${View.VIEW_CMD}"), "Messages are kept for ${bold(maxDays)}" + " day".plural(maxDays.toLong()) + '.' ) - override val isOp: Boolean = false + override val isOpOnly: Boolean = false override val isPublic: Boolean = isEnabled() override val isVisible: Boolean = isEnabled() - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isEnabled()) { if (args.isBlank()) { - helpResponse(args, sender, isOp, isPrivate) + helpResponse(channel, args, event) } else if (args.startsWith(View.VIEW_CMD)) { - if (bot.isOp(sender) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { - viewAll(sender, isPrivate) + if (isChannelOp(channel, event) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { + viewAll(event) } else { - viewMessages(sender, isPrivate) + viewMessages(event) } } else if (args.startsWith("$TELL_DEL_KEYWORD ")) { - deleteMessage(sender, args, isOp, isPrivate) + deleteMessage(channel, args, event) } else { - newMessage(sender, args, isOp, isPrivate) + newMessage(channel, args, event) } if (clean()) { save() @@ -169,21 +167,19 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { } // New message. - private fun newMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) { + private fun newMessage(channel: String, args: String, event: GenericMessageEvent) { val split = args.split(" ".toRegex(), 2) if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) { if (messages.size < maxSize) { - val message = TellMessage(sender, split[0], split[1].trim()) + val message = TellMessage(event.user.nick, split[0], split[1].trim()) messages.add(message) save() - bot.send( - sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate - ) + event.sendMessage("Message [ID ${message.id}] was queued for ${bold(message.recipient)}") } else { - bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate) + event.sendMessage("Sorry, the messages queue is currently full.") } } else { - helpResponse(args, sender, isOp, isPrivate) + helpResponse(channel, args, event) } } @@ -191,34 +187,30 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { * Saves the messages queue. */ private fun save() { - TellMessagesMgr.save(serializedObject, messages, bot.logger) + TellMessagesMgr.save(serialObject, messages) } /** * Checks and sends messages. */ - @JvmOverloads - fun send(nickname: String, isMessage: Boolean = false) { - if (isEnabled() && nickname != bot.nick) { + fun send(event: GenericUserEvent) { + val nickname = event.user.nick + if (isEnabled() && nickname != event.getBot<PircBotX>().nick) { messages.stream().filter { message: TellMessage -> message.isMatch(nickname) } .forEach { message: TellMessage -> if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { if (message.sender == nickname) { - if (!isMessage) { - bot.send( - nickname, - "${bold("You")} wanted me to remind you: ${reverseColor(message.message)}", - true + if (event !is MessageEvent) { + event.user.send().message( + "${bold("You")} wanted me to remind you: ${reverseColor(message.message)}" ) message.isReceived = true message.isNotified = true save() } } else { - bot.send( - nickname, - "${message.sender} wanted me to tell you: ${reverseColor(message.message)}", - true + event.user.send().message( + "${message.sender} wanted me to tell you: ${reverseColor(message.message)}" ) message.isReceived = true save() @@ -226,11 +218,9 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived && !message.isNotified ) { - bot.send( - nickname, - "Your message ${reverseColor("[ID " + message.id + ']')} was sent to " - + "${bold(message.recipient)} on ${message.receptionDate.toUtcDateTime()}", - true + event.user.send().message( + "Your message ${reverseColor("[ID ${message.id}]")} was sent to " + + "${bold(message.recipient)} on ${message.receptionDate}" ) message.isNotified = true save() @@ -247,66 +237,55 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { fun size(): Int = messages.size // View all messages. - private fun viewAll(sender: String, isPrivate: Boolean) { + private fun viewAll(event: GenericMessageEvent) { if (messages.isNotEmpty()) { for (message in messages) { - bot.send( - sender, bold(message.sender) + ARROW + bold(message.recipient) - + " [ID: " + message.id + ", " - + (if (message.isReceived) "DELIVERED" else "QUEUED") + ']', - isPrivate + event.sendMessage( + "${bold(message.sender)}$ARROW${bold(message.recipient)} [ID: ${message.id}, " + + (if (message.isReceived) "DELIVERED]" else "QUEUED]") ) } } else { - bot.send(sender, "There are no messages in the queue.", isPrivate) + event.sendMessage("There are no messages in the queue.") } } // View messages. - private fun viewMessages(sender: String, isPrivate: Boolean) { + private fun viewMessages(event: GenericMessageEvent) { var hasMessage = false for (message in messages) { - if (message.isMatch(sender)) { + if (message.isMatch(event.user.nick)) { if (!hasMessage) { - hasMessage = true - bot.send(sender, "Here are your messages: ", isPrivate) + hasMessage = true; event.sendMessage("Here are your messages: ") } if (message.isReceived) { - bot.send( - sender, - bold(message.sender) + ARROW + bold(message.recipient) - + " [${message.receptionDate.toUtcDateTime()}, ID: " - + bold(message.id) + ", DELIVERED]", - isPrivate + event.sendMessage( + bold(message.sender) + ARROW + bold(message.recipient) + + " [${message.receptionDate.toUtcDateTime()}, ID: ${bold(message.id)}, DELIVERED]" ) } else { - bot.send( - sender, - bold(message.sender) + ARROW + bold(message.recipient) - + " [${message.queued.toUtcDateTime()}, ID: " - + bold(message.id) + ", QUEUED]", - isPrivate + event.sendMessage( + bold(message.sender) + ARROW + bold(message.recipient) + + " [${message.queued.toUtcDateTime()}, ID: ${bold(message.id)}, QUEUED]" ) } - bot.send(sender, helpFormat(message.message), isPrivate) + event.sendMessage(helpFormat(message.message)) } } if (!hasMessage) { - bot.send(sender, "You have no messages in the queue.", isPrivate) + event.sendMessage("You have no messages in the queue.") } else { - bot.send(sender, "To delete one or all delivered messages:", isPrivate) - bot.send( - sender, + event.sendMessage("To delete one or all delivered messages:") + event.sendMessage( helpFormat( buildCmdSyntax( "%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", - bot.nick, - isPrivate + event.user.nick, + true ) - ), - isPrivate + ) ) - bot.send(sender, help.last(), isPrivate) + event.sendMessage(help.last()) } } @@ -324,9 +303,6 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { // Arrow private const val ARROW = " --> " - // Serialized object file extension - private const val SER_EXT = ".ser" - // All keyword private const val TELL_ALL_KEYWORD = "all" @@ -341,8 +317,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP) // Load the message queue - serializedObject = bot.logsDir + bot.name + SER_EXT - messages.addAll(TellMessagesMgr.load(serializedObject, bot.logger)) + messages.addAll(TellMessagesMgr.load(serialObject)) if (clean()) { save() } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 47521d4..eb058b7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -66,12 +66,12 @@ class TellMessage internal constructor( var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) /** - * Returns {@code true) if a notification was sent. + * Returns {@code true} if a notification was sent. */ var isNotified = false /** - * Returns {@code true) if the message was received. + * Returns {@code true} if the message was received. */ var isReceived = false set(value) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index c11e23e..5960b63 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -31,10 +31,10 @@ */ package net.thauvin.erik.mobibot.commands.tell -import org.apache.logging.log4j.Logger +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.BufferedInputStream import java.io.BufferedOutputStream -import java.io.FileNotFoundException import java.io.IOException import java.io.ObjectInputStream import java.io.ObjectOutputStream @@ -42,11 +42,14 @@ import java.nio.file.Files import java.nio.file.Paths import java.time.Clock import java.time.LocalDateTime +import kotlin.io.path.exists /** * The Tell Messages Manager. */ object TellMessagesMgr { + val logger: Logger = LoggerFactory.getLogger(TellMessagesMgr::class.java) + /** * Cleans the messages queue. */ @@ -58,22 +61,22 @@ object TellMessagesMgr { /** * Loads the messages. */ - - fun load(file: String, logger: Logger): List<TellMessage> { - try { - ObjectInputStream( - BufferedInputStream(Files.newInputStream(Paths.get(file))) - ).use { input -> - if (logger.isDebugEnabled) logger.debug("Loading the messages.") - @Suppress("UNCHECKED_CAST") - return input.readObject() as List<TellMessage> + fun load(file: String): List<TellMessage> { + val serialFile = Paths.get(file) + if (serialFile.exists()) { + try { + ObjectInputStream( + BufferedInputStream(Files.newInputStream(serialFile)) + ).use { input -> + if (logger.isDebugEnabled) logger.debug("Loading the messages.") + @Suppress("UNCHECKED_CAST") + return input.readObject() as List<TellMessage> + } + } catch (e: IOException) { + logger.error("An IO error occurred loading the messages queue.", e) + } catch (e: ClassNotFoundException) { + logger.error("An error occurred loading the messages queue.", e) } - } catch (ignore: FileNotFoundException) { - // Do nothing - } catch (e: IOException) { - logger.error("An IO error occurred loading the messages queue.", e) - } catch (e: ClassNotFoundException) { - logger.error("An error occurred loading the messages queue.", e) } return listOf() } @@ -81,7 +84,7 @@ object TellMessagesMgr { /** * Saves the messages. */ - fun save(file: String, messages: List<TellMessage?>?, logger: Logger) { + fun save(file: String, messages: List<TellMessage?>?) { try { BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> ObjectOutputStream(bos).use { output -> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt similarity index 74% rename from src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index 803ab58..78602f6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Kill.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,5 +1,5 @@ /* - * Kill.kt + * Entries.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -30,26 +30,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.thauvin.erik.mobibot.commands +package net.thauvin.erik.mobibot.entries -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.today -class Kill(bot: Mobibot) : AbstractCommand(bot) { - override val name = "kill" - override val help = emptyList<String>() - override val isOp = true - override val isPublic = false - override val isVisible = false +class Entries( + var channel: String = "", + var ircServer: String = "", + var logsDir: String = "", + var backlogs: String = "" +) { + val links = mutableListOf<EntryLink>() - override fun commandResponse( - sender: String, - login: String, - args: String, - isOp: Boolean, - isPrivate: Boolean - ) { - if (isOp) { - bot.shutdown(sender, true) - } + var lastPubDate = today() + + fun load() { + lastPubDate = FeedsMgr.loadFeed(this) + } + + fun save() { + lastPubDate = today() + FeedsMgr.saveFeed(this) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt deleted file mode 100644 index 6fe809b..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ /dev/null @@ -1,262 +0,0 @@ -/* - * EntriesMgr.kt - * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.entries - -import com.rometools.rome.feed.synd.SyndContentImpl -import com.rometools.rome.feed.synd.SyndEntry -import com.rometools.rome.feed.synd.SyndEntryImpl -import com.rometools.rome.feed.synd.SyndFeed -import com.rometools.rome.feed.synd.SyndFeedImpl -import com.rometools.rome.io.FeedException -import com.rometools.rome.io.SyndFeedInput -import com.rometools.rome.io.SyndFeedOutput -import net.thauvin.erik.mobibot.Mobibot -import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import java.io.IOException -import java.io.InputStreamReader -import java.io.OutputStreamWriter -import java.nio.charset.StandardCharsets -import java.nio.file.Files -import java.nio.file.Paths -import java.util.Calendar - -/** - * Manages the feed entries. - */ -object EntriesMgr { - /** - * The name of the file containing the current entries. - */ - const val CURRENT_XML = "current.xml" - - /** - * The name of the file containing the backlog entries. - */ - const val NAV_XML = "nav.xml" - - /** - * The .xml extension - */ - const val XML_EXT = ".xml" - - // Maximum number of backlogs to keep - private const val maxBacklogs = 10 - - // Daily backup - private fun dailyBackup( - bot: Mobibot, - history: MutableList<String> - ) { - if (bot.backlogsUrl.isNotBlank()) { - if (!history.contains(bot.today)) { - history.add(bot.today) - while (history.size > maxBacklogs) { - history.removeFirst() - } - } - OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8 - ).use { fw -> - val output = SyndFeedOutput() - val rss: SyndFeed = SyndFeedImpl() - val items: MutableList<SyndEntry> = mutableListOf() - var item: SyndEntry - with(rss) { - feedType = "rss_2.0" - title = "${bot.channel} IRC Links Backlogs" - description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}" - link = bot.backlogsUrl - publishedDate = Calendar.getInstance().time - } - var date: String - items.clear() - for (i in history.size - 1 downTo 0) { - date = history[i] - item = SyndEntryImpl() - with(item) { - link = bot.backlogsUrl + date + ".xml" - title = date - description = SyndContentImpl().apply { value = "Links for $date" } - } - items.add(item) - } - rss.entries = items - if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.") - output.output(rss, fw) - } - } else { - if (bot.logger.isErrorEnabled) { - bot.logger.warn("Unable to generate the backlogs feed. No property configured.") - } - } - } - - /** - * Loads the backlogs. - */ - @Throws(IOException::class, FeedException::class) - fun loadBacklogs(file: String, history: MutableList<String>) { - history.clear() - val input = SyndFeedInput() - InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader -> - val feed = input.build(reader) - val items = feed.entries - for (i in items.indices.reversed()) { - history.add(items[i].title) - } - } - } - - /** - * Loads the current entries. - */ - @Throws(IOException::class, FeedException::class) - fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String { - entries.clear() - val input = SyndFeedInput() - var today: String - InputStreamReader( - Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8 - ).use { reader -> - val feed = input.build(reader) - today = feed.publishedDate.toIsoLocalDate() - val items = feed.entries - var entry: EntryLink - for (i in items.indices.reversed()) { - with(items[i]) { - entry = EntryLink( - link, - title, - author.substring(author.lastIndexOf('(') + 1, author.length - 1), - channel, - publishedDate, - categories - ) - var split: List<String> - for (comment in description.value.split("<br/>")) { - split = comment.split(": ".toRegex(), 2) - if (split.size == 2) { - entry.addComment(comment = split[1].trim(), nick = split[0].trim()) - } - } - } - entries.add(entry) - } - } - return today - } - - /** - * Saves the entries. - */ - fun saveEntries( - bot: Mobibot, - entries: List<EntryLink>, - history: MutableList<String>, - isDayBackup: Boolean - ) { - if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...") - if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { - try { - val output = SyndFeedOutput() - val rss: SyndFeed = SyndFeedImpl() - val items: MutableList<SyndEntry> = mutableListOf() - var item: SyndEntry - OutputStreamWriter( - Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8 - ).use { fw -> - with(rss) { - feedType = "rss_2.0" - title = bot.channel + " IRC Links" - description = "Links from ${bot.ircServer} on ${bot.channel}" - link = bot.weblogUrl - publishedDate = Calendar.getInstance().time - language = "en" - } - val buff: StringBuilder = StringBuilder() - for (i in entries.size - 1 downTo 0) { - with(entries[i]) { - buff.setLength(0) - buff.append("Posted by <b>") - .append(nick) - .append("</b> on <a href=\"irc://") - .append(bot.ircServer).append('/') - .append(channel) - .append("\"><b>") - .append(channel) - .append("</b></a>") - if (comments.size > 0) { - buff.append(" <br/><br/>") - for (j in comments.indices) { - if (j > 0) { - buff.append(" <br/>") - } - buff.append(comments[j].nick).append(": ").append(comments[j].comment) - } - } - item = SyndEntryImpl() - item.link = link - item.description = SyndContentImpl().apply { value = buff.toString() } - item.title = title - item.publishedDate = date - item.author = "${bot.channel.substring(1)}@${bot.ircServer} ($nick)" - item.categories = tags - items.add(item) - } - } - rss.entries = items - if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the entries feed.") - output.output(rss, fw) - } - OutputStreamWriter( - Files.newOutputStream( - Paths.get( - bot.logsDir + bot.today + XML_EXT - ) - ), StandardCharsets.UTF_8 - ).use { fw -> output.output(rss, fw) } - if (isDayBackup) { - dailyBackup(bot, history) - } - } catch (e: FeedException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) - } catch (e: IOException) { - if (bot.logger.isWarnEnabled) - bot.logger.warn("An IO error occurred while generating the entries feed.", e) - } - } else { - if (bot.logger.isWarnEnabled) { - bot.logger.warn("Unable to generate the entries feed. A required property is missing.") - } - } - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index cf0bf35..b577895 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -40,22 +40,22 @@ import net.thauvin.erik.mobibot.Utils.green */ object EntriesUtils { /** - * Build link cmd based on its index. e.g: L1 + * Build link label based on its index. e.g: L1 */ - fun buildLinkCmd(index: Int): String = Constants.LINK_CMD + (index + 1) + fun buildLinkLabel(index: Int): String = Constants.LINK_CMD + (index + 1) /** * Builds an entry's comment for display on the channel. */ fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${buildLinkCmd(entryIndex)}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + ("${buildLinkLabel(entryIndex)}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** * Builds an entry's link for display on the channel. */ @JvmOverloads fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { - val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ") + val buff = StringBuilder().append(buildLinkLabel(entryIndex)).append(": ") .append('[').append(entry.nick).append(']') if (isView && entry.comments.isNotEmpty()) { buff.append("[+").append(entry.comments.size).append(']') @@ -76,5 +76,5 @@ object EntriesUtils { * Build an entry's tags/categories for display on the channel. */ fun buildTags(entryIndex: Int, entry: EntryLink): String = - buildLinkCmd(entryIndex) + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ") + buildLinkLabel(entryIndex) + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ") } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 1a8468f..d7730a5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -109,16 +109,25 @@ class EntryLink : Serializable { */ fun addComment(comment: String, nick: String): Int { comments.add(EntryComment(comment, nick)) - return comments.size - 1 + return comments.lastIndex } /** * Deletes a specific comment. */ - fun deleteComment(index: Int) { + fun deleteComment(index: Int): Boolean { if (index < comments.size) { comments.removeAt(index) + return true } + return false + } + + /** + * Deletes a comment. + */ + fun deleteComment(entryComment: EntryComment): Boolean { + return comments.remove(entryComment) } /** @@ -154,7 +163,7 @@ class EntryLink : Serializable { * Sets a comment. */ fun setComment(index: Int, comment: String?, nick: String?) { - if (index < comments.size && (comment != null) && !nick.isNullOrBlank()) { + if (index < comments.size && !comment.isNullOrBlank() && !nick.isNullOrBlank()) { comments[index] = EntryComment(comment, nick) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt new file mode 100644 index 0000000..ccbcd46 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt @@ -0,0 +1,190 @@ +/* + * FeedsMgr.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import com.rometools.rome.feed.synd.SyndContentImpl +import com.rometools.rome.feed.synd.SyndEntry +import com.rometools.rome.feed.synd.SyndEntryImpl +import com.rometools.rome.feed.synd.SyndFeed +import com.rometools.rome.feed.synd.SyndFeedImpl +import com.rometools.rome.io.FeedException +import com.rometools.rome.io.SyndFeedInput +import com.rometools.rome.io.SyndFeedOutput +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.Utils.today +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.io.InputStreamReader +import java.io.OutputStreamWriter +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths +import java.util.Calendar +import kotlin.io.path.exists + +/** + * Manages the RSS feeds. + */ +class FeedsMgr private constructor() { + companion object { + private val logger: Logger = LoggerFactory.getLogger(FeedsMgr::class.java) + + // The file containing the current entries. + private const val currentXml = "current.xml" + + // The .xml extension. + private const val dotXml = ".xml" + + /** + * Loads the current feed. + */ + @Throws(IOException::class, FeedException::class) + fun loadFeed(entries: Entries, currentFile: String = currentXml): String { + entries.links.clear() + val xml = Paths.get("${entries.logsDir}${currentFile}") + var pubDate = today() + if (xml.exists()) { + val input = SyndFeedInput() + InputStreamReader( + Files.newInputStream(xml), StandardCharsets.UTF_8 + ).use { reader -> + val feed = input.build(reader) + pubDate = feed.publishedDate.toIsoLocalDate() + val items = feed.entries + var entry: EntryLink + for (i in items.indices.reversed()) { + with(items[i]) { + entry = EntryLink( + link, + title, + author.substring(author.lastIndexOf('(') + 1, author.length - 1), + entries.channel, + publishedDate, + categories + ) + var split: List<String> + for (comment in description.value.split("<br/>")) { + split = comment.split(": ".toRegex(), 2) + if (split.size == 2) { + entry.addComment(comment = split[1].trim(), nick = split[0].trim()) + } + } + } + entries.links.add(entry) + } + } + } else { + // Create an empty feed. + saveFeed(entries) + } + return pubDate + } + + /** + * Saves the feeds. + */ + fun saveFeed(entries: Entries, currentFile: String = currentXml) { + if (logger.isDebugEnabled) logger.debug("Saving the feeds...") + if (entries.logsDir.isNotBlank()) { + try { + val output = SyndFeedOutput() + val rss: SyndFeed = SyndFeedImpl() + val items: MutableList<SyndEntry> = mutableListOf() + var item: SyndEntry + OutputStreamWriter( + Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 + ).use { fw -> + with(rss) { + feedType = "rss_2.0" + title = "${entries.channel} IRC Links" + description = "Links from ${entries.ircServer} on ${entries.channel}" + if (entries.backlogs.isNotBlank()) link = entries.backlogs + publishedDate = Calendar.getInstance().time + language = "en" + } + val buff: StringBuilder = StringBuilder() + for (i in entries.links.indices.reversed()) { + with(entries.links[i]) { + buff.setLength(0) + buff.append("Posted by <b>") + .append(nick) + .append("</b> on <a href=\"irc://") + .append(entries.ircServer).append('/') + .append(channel) + .append("\"><b>") + .append(channel) + .append("</b></a>") + if (comments.size > 0) { + buff.append(" <br/><br/>") + for (j in comments.indices) { + if (j > 0) { + buff.append(" <br/>") + } + buff.append(comments[j].nick).append(": ").append(comments[j].comment) + } + } + item = SyndEntryImpl() + item.link = link + item.description = SyndContentImpl().apply { value = buff.toString() } + item.title = title + item.publishedDate = date + item.author = "${channel.substring(1)}@${entries.ircServer} ($nick)" + item.categories = tags + items.add(item) + } + } + rss.entries = items + if (logger.isDebugEnabled) logger.debug("Writing the entries feed.") + output.output(rss, fw) + } + OutputStreamWriter( + Files.newOutputStream( + Paths.get( + entries.logsDir + today() + dotXml + ) + ), StandardCharsets.UTF_8 + ).use { fw -> output.output(rss, fw) } + } catch (e: FeedException) { + if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e) + } catch (e: IOException) { + if (logger.isWarnEnabled) + logger.warn("An IO error occurred while generating the entries feed.", e) + } + } else { + if (logger.isWarnEnabled) { + logger.warn("Unable to generate the entries feed. A required property is missing.") + } + } + } + } +} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 3c46fb4..372ef2d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -31,13 +31,16 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent /** * The `Module` abstract class. */ -abstract class AbstractModule(val bot: Mobibot) { +abstract class AbstractModule { /** * The module's commands, if any. */ @@ -51,12 +54,7 @@ abstract class AbstractModule(val bot: Mobibot) { /** * Responds to a command. */ - abstract fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) + abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) /** * Returns the module's property keys. @@ -74,9 +72,9 @@ abstract class AbstractModule(val bot: Mobibot) { /** * Responds with the module's help. */ - open fun helpResponse(sender: String, isPrivate: Boolean): Boolean { + open fun helpResponse(event: GenericMessageEvent): Boolean { for (h in help) { - bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate) + event.sendMessage(buildCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent)) } return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 5ff5c73..efe87e5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -33,35 +33,32 @@ package net.thauvin.erik.mobibot.modules import net.objecthunter.exp4j.ExpressionBuilder import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.text.DecimalFormat /** * The Calc module. */ -class Calc(bot: Mobibot) : AbstractModule(bot) { - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { +class Calc : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Calc::class.java) + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { - with(bot) { - try { - send(calculate(args)) - } catch (e: IllegalArgumentException) { - if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e) - send("No idea. This is the kind of math I don't get.") - } catch (e: UnknownFunctionOrVariableException) { - if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e) - send("No idea. I must've some form of Dyscalculia.") - } + try { + event.respond(calculate(args)) + } catch (e: IllegalArgumentException) { + if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e) + event.respond("No idea. This is the kind of math I don't get.") + } catch (e: UnknownFunctionOrVariableException) { + if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e) + event.respond("No idea. I must've some form of Dyscalculia.") } } else { - helpResponse(sender, isPrivate) + helpResponse(event) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index db28550..6cd8906 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -34,40 +34,44 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.crypto.CryptoException import net.thauvin.erik.crypto.CryptoPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.msg.PublicMessage +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException /** * The Cryptocurrency Prices module. */ -class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) { +class CryptoPrices : ThreadedModule() { + private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) + /** * Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price). */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val debugMessage = "crypto($cmd $args)" - with(bot) { - if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { - try { - val price = currentPrice(args.split(' ')) - val amount = try { - price.toCurrency() - } catch (ignore: IllegalArgumentException) { - price.amount - } - send(sender, PublicMessage("${price.base}: $amount [${price.currency}]")) - } catch (e: CryptoException) { - if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) - send(e.message) - } catch (e: Exception) { - if (logger.isErrorEnabled) logger.error(debugMessage, e) - send("An error has occurred while retrieving the cryptocurrency market price.") + if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { + try { + val price = currentPrice(args.split(' ')) + val amount = try { + price.toCurrency() + } catch (ignore: IllegalArgumentException) { + price.amount } - } else { - helpResponse(sender, isPrivate) + event.respond("${price.base} current price is $amount [${price.currency}]") + } catch (e: CryptoException) { + if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) + event.sendMessage(e.message!!) + } catch (e: IOException) { + if (logger.isErrorEnabled) logger.error(debugMessage, e) + event.sendMessage("An IO error has occurred while retrieving the cryptocurrency market price.") } + } else { + helpResponse(event) } + } companion object { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index e0b7088..cd4bb67 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -31,16 +31,21 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import org.jdom2.JDOMException import org.jdom2.input.SAXBuilder +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL import java.text.NumberFormat @@ -51,84 +56,68 @@ import javax.xml.XMLConstants /** * The CurrencyConverter module. */ -class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { +class CurrencyConverter : ThreadedModule() { + private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { synchronized(this) { if (pubDate != today()) { EXCHANGE_RATES.clear() } } - super.commandResponse(sender, cmd, args, isPrivate) + super.commandResponse(channel, cmd, args, event) } /** * Converts the specified currencies. */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - bot.apply { - if (EXCHANGE_RATES.isEmpty()) { - try { - loadRates() - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - } + override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (EXCHANGE_RATES.isEmpty()) { + try { + loadRates() + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) } + } - if (EXCHANGE_RATES.isEmpty()) { - send(sender, EMPTY_RATE_TABLE, true) - } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { - val msg = convertCurrency(args) - send(sender, msg) - if (msg.isError) { - helpResponse(sender, isPrivate) - } - } else if (args.contains(CURRENCY_RATES_KEYWORD)) { - send(sender, "The reference rates for ${bold(pubDate)} are:", isPrivate) - @Suppress("MagicNumber") - sendList(sender, currencyRates(), 3, " ", isPrivate, isIndent = true) - } else { - helpResponse(sender, isPrivate) + if (EXCHANGE_RATES.isEmpty()) { + event.respond(EMPTY_RATE_TABLE) + } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { + val msg = convertCurrency(args) + event.respond(msg.msg) + if (msg.isError) { + helpResponse(event) } + } else if (args.contains(CURRENCY_RATES_KEYWORD)) { + event.sendMessage("The reference rates for ${bold(pubDate)} are:") + event.sendList(currencyRates(), 3, " ", isIndent = true) + } else { + helpResponse(event) } } - override fun helpResponse(sender: String, isPrivate: Boolean): Boolean { - with(bot) { - if (EXCHANGE_RATES.isEmpty()) { - try { - loadRates() - } catch (e: ModuleException) { - if (logger.isDebugEnabled) logger.debug(e.debugMessage, e) - } + override fun helpResponse(event: GenericMessageEvent): Boolean { + if (EXCHANGE_RATES.isEmpty()) { + try { + loadRates() + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) } - if (EXCHANGE_RATES.isEmpty()) { - send(sender, EMPTY_RATE_TABLE, isPrivate) - } else { - send(sender, "To convert from one currency to another:", isPrivate) - send( - sender, - helpFormat( - buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled) - ), - isPrivate + } + if (EXCHANGE_RATES.isEmpty()) { + event.sendMessage(EMPTY_RATE_TABLE) + } else { + val nick = event.bot().nick + event.sendMessage("To convert from one currency to another:") + event.sendMessage(helpFormat(buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) + event.sendMessage("For a listing of current reference rates:") + event.sendMessage( + helpFormat( + buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) ) - send(sender, "For a listing of current reference rates:", isPrivate) - send( - sender, - helpFormat( - buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) - ), - isPrivate - ) - send(sender, "The supported currencies are: ", isPrivate) - @Suppress("MagicNumber") - sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate = isPrivate, isIndent = true) - } + ) + event.sendMessage("The supported currencies are: ") + event.sendList(ArrayList(EXCHANGE_RATES.keys), 11, isIndent = true) } return true } @@ -161,7 +150,6 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { /** * Converts from a currency to another. */ - @Suppress("MagicNumber") @JvmStatic fun convertCurrency(query: String): Message { val cmds = query.split(" ") @@ -194,7 +182,6 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { fun currencyRates(): List<String> { val rates = mutableListOf<String>() for ((key, value) in EXCHANGE_RATES.toSortedMap()) { - @Suppress("MagicNumber") rates.add("$key: ${value.padStart(8)}") } return rates diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 57e78ca..4b7257e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -31,37 +31,33 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent import kotlin.random.Random /** * The Dice module. */ -class Dice(bot: Mobibot) : AbstractModule(bot) { - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { +class Dice : AbstractModule() { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val botRoll = roll() val roll = roll() val botTotal = botRoll.first + botRoll.second val total = roll.first + roll.second - with(bot) { - send( - channel, - "$sender rolled ${total}: ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]}", - isPrivate + with(event.bot()) { + event.respond( + "you rolled ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]} for a total of ${bold(total)}" ) - action( - "rolled ${botTotal}: ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]}" + sendIRC().action( + channel, + "rolled ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]} for a total of ${bold(botTotal)}" ) when (winLoseOrTie(botTotal, total)) { - Result.WIN -> action("wins.") - Result.LOSE -> action("lost.") - else -> action("tied.") + Result.WIN -> sendIRC().action(channel, "wins.") + Result.LOSE -> sendIRC().action(channel, "lost.") + else -> sendIRC().action(channel, "tied.") } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 5c1a268..9571556 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -31,45 +31,49 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.Utils.urlReader +import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage -import org.jibble.pircbot.Colors import org.json.JSONException import org.json.JSONObject +import org.pircbotx.Colors +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL /** * The GoogleSearch module. */ -class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { +class GoogleSearch : ThreadedModule() { + private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) + /** * Searches Google. */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { - if (args.isNotBlank()) { - try { - val results = searchGoogle( - args, properties[GOOGLE_API_KEY_PROP], - properties[GOOGLE_CSE_KEY_PROP] - ) - for (msg in results) { - send(sender, msg) - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - send(sender, e.message, isPrivate) + override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val results = searchGoogle( + args, properties[GOOGLE_API_KEY_PROP], + properties[GOOGLE_CSE_KEY_PROP] + ) + for (msg in results) { + event.sendMessage(channel, msg) } - } else { - helpResponse(sender, isPrivate) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + event.sendMessage(e.message!!) } + } else { + helpResponse(event) } } @@ -92,29 +96,33 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { throw ModuleException("${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing.") } - return if (query.isNotBlank()) { - val results = mutableListOf<Message>() + val results = mutableListOf<Message>() + if (query.isNotBlank()) { try { val url = URL( "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + "&q=${encodeUrl(query)}&filter=1&num=5&alt=json" ) val json = JSONObject(urlReader(url)) - val ja = json.getJSONArray("items") - for (i in 0 until ja.length()) { - val j = ja.getJSONObject(i) - results.add(NoticeMessage(unescapeXml(j.getString("title")))) - results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) + if (json.has("items")) { + val ja = json.getJSONArray("items") + for (i in 0 until ja.length()) { + val j = ja.getJSONObject(i) + results.add(NoticeMessage(unescapeXml(j.getString("title")))) + results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) + } + } else { + results.add(ErrorMessage("No results found.", Colors.RED)) } } catch (e: IOException) { throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e) } catch (e: JSONException) { throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e) } - results } else { - throw ModuleException("Invalid query. Please try again.") + results.add(ErrorMessage("Invalid query. Please try again.")) } + return results } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index d3e0ca8..2d5af61 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -33,42 +33,43 @@ package net.thauvin.erik.mobibot.modules import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import org.json.JSONException import org.json.JSONObject +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL /** * The Joke module. */ -class Joke(bot: Mobibot) : ThreadedModule(bot) { - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { +class Joke : ThreadedModule() { + private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { runBlocking { - launch { run(sender, cmd, args, isPrivate) } + launch { run(channel, cmd, args, event) } } } /** * Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/). */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { + override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { try { - send(cyan(randomJoke().msg)) + sendIRC().notice(channel, cyan(randomJoke().msg)) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - send(sender, e.message, isPrivate) + event.sendMessage(e.message!!) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index e1557d9..703f138 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -32,9 +32,12 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat import org.apache.commons.net.whois.WhoisClient +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.IOException import java.net.InetAddress import java.net.UnknownHostException @@ -42,52 +45,51 @@ import java.net.UnknownHostException /** * The Lookup module. */ -class Lookup(bot: Mobibot) : AbstractModule(bot) { - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { +class Lookup : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java) + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.matches("(\\S.)+(\\S)+".toRegex())) { - with(bot) { - try { - nslookup(args).split(',').forEach { - send(it.trim()) - } - } catch (ignore: UnknownHostException) { - if (args.matches( - ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex() - ) - ) { - try { - val lines = whois(args) - if (lines.isNotEmpty()) { - var line: String - for (rawLine in lines) { - line = rawLine.trim() - if (line.isNotEmpty() && line[0] != '#') { - send(line) + try { + event.respondWith(nslookup(args).prependIndent()) + } catch (ignore: UnknownHostException) { + if (args.matches( + ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex() + ) + ) { + try { + val lines = whois(args) + if (lines.isNotEmpty()) { + var line: String + var hasData = false + for (rawLine in lines) { + line = rawLine.trim() + if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) { + if (!hasData) { + event.respondWith(line) + hasData = true + } else { + event.bot().sendIRC().notice(event.user.nick, line) } } - } else { - send("Unknown host.") } - } catch (ioe: IOException) { - if (logger.isDebugEnabled) { - logger.debug("Unable to perform whois IP lookup: $args", ioe) - } - send("Unable to perform whois IP lookup: ${ioe.message}") + } else { + event.respond("Unknown host.") } - } else { - send("Unknown host.") + } catch (ioe: IOException) { + if (logger.isWarnEnabled) { + logger.warn("Unable to perform whois IP lookup: $args", ioe) + } + event.respond("Unable to perform whois IP lookup: ${ioe.message}") } + } else { + event.respond("Unknown host.") } } } else { - helpResponse(sender, true) + helpResponse(event) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 0a97d7b..9d49118 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -31,24 +31,20 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent import kotlin.random.Random /** * The Ping module. */ -class Ping(bot: Mobibot) : AbstractModule(bot) { +class Ping : AbstractModule() { /** * {@inheritDoc} */ - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { - bot.action(randomPing()) + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + event.bot().sendIRC().action(channel, randomPing()) } companion object { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 33b1fce..20c5c29 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -32,16 +32,17 @@ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent import kotlin.random.Random /** * Simple module example in Kotlin. */ -class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { +class RockPaperScissors : AbstractModule() { init { with(commands) { add(Hands.ROCK.name.lowercase()) @@ -101,20 +102,26 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) { } } - override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val hand = Hands.valueOf(cmd.uppercase()) val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] - with(bot) { - send("${hand.emoji} vs. ${botHand.emoji}") + with(event.bot()) { + sendIRC().message(channel, "${hand.emoji} vs. ${botHand.emoji}") when { hand == botHand -> { - action("tied.") + sendIRC().action(channel, "tied.") } hand.beats(botHand) -> { - action("lost. ${hand.name.capitalise()} ${hand.action} ${botHand.name.lowercase()}.") + sendIRC().action( + channel, + "lost. ${hand.name.capitalise()} ${hand.action} ${botHand.name.lowercase()}." + ) } else -> { - action("wins. ${botHand.name.capitalise()} ${botHand.action} ${hand.name.lowercase()}.") + sendIRC().action( + channel, + "wins. ${botHand.name.capitalise()} ${botHand.action} ${hand.name.lowercase()}." + ) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index f418185..230e2af 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -31,10 +31,10 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.ErrorMessage @@ -43,31 +43,34 @@ import net.thauvin.erik.mobibot.msg.NoticeMessage import net.thauvin.erik.mobibot.msg.PublicMessage import org.json.JSONException import org.json.JSONObject +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL /** * The StockQuote module. */ -class StockQuote(bot: Mobibot) : ThreadedModule(bot) { +class StockQuote : ThreadedModule() { + private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) + /** * Returns the specified stock quote from Alpha Vantage. */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { - if (args.isNotBlank()) { - try { - val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) - for (msg in messages) { - send(sender, msg) - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - send(e.message) + override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) + for (msg in messages) { + event.sendMessage(channel, msg) } - } else { - helpResponse(sender, isPrivate) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + event.sendMessage(e.message!!) } + } else { + helpResponse(event) } } @@ -129,9 +132,9 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { "${STOCK_CMD.capitalise()} is disabled. The API key is missing." ) } - return if (symbol.isNotBlank()) { + val messages = mutableListOf<Message>() + if (symbol.isNotBlank()) { val debugMessage = "getQuote($symbol)" - val messages = mutableListOf<Message>() var response: String try { with(messages) { @@ -161,56 +164,55 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { val quote = json.getJSONObject("Global Quote") if (quote.isEmpty) { add(ErrorMessage(INVALID_SYMBOL)) - return messages - } + } else { - add( - PublicMessage( - "Symbol: " + unescapeXml(quote.getString("01. symbol")) - + " [" + unescapeXml(symbolInfo.getString("2. name")) + ']' + add( + PublicMessage( + "Symbol: " + unescapeXml(quote.getString("01. symbol")) + + " [" + unescapeXml(symbolInfo.getString("2. name")) + ']' + ) ) - ) - @Suppress("MagicNumber") - val pad = 10 + val pad = 10 - add( - PublicMessage( - "Price:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString("05. price")) + add( + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString("05. price")) + ) ) - ) - add( - PublicMessage( - "Previous:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString("08. previous close")) + add( + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString("08. previous close")) + ) ) - ) - val data = arrayOf( - "Open" to "02. open", - "High" to "03. high", - "Low" to "04. low", - "Volume" to "06. volume", - "Latest" to "07. latest trading day" - ) + val data = arrayOf( + "Open" to "02. open", + "High" to "03. high", + "Low" to "04. low", + "Volume" to "06. volume", + "Latest" to "07. latest trading day" + ) + + data.forEach { + add( + NoticeMessage( + "${it.first}:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString(it.second)) + ) + ) + } - data.forEach { add( NoticeMessage( - "${it.first}:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString(it.second)) + "Change:".padEnd(pad).prependIndent() + + unescapeXml(quote.getString("09. change")) + + " [" + unescapeXml(quote.getString("10. change percent")) + ']' ) ) } - - add( - NoticeMessage( - "Change:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString("09. change")) - + " [" + unescapeXml(quote.getString("10. change percent")) + ']' - ) - ) } } } catch (e: IOException) { @@ -218,10 +220,10 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) { } catch (e: NullPointerException) { throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e) } - messages } else { - throw ModuleException(INVALID_SYMBOL) + messages.add(ErrorMessage(INVALID_SYMBOL)) } + return messages } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt index d1d58d0..b44aab1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt @@ -33,36 +33,26 @@ package net.thauvin.erik.mobibot.modules import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import net.thauvin.erik.mobibot.Mobibot +import org.pircbotx.hooks.types.GenericMessageEvent /** * The `ThreadedModule` class. */ -abstract class ThreadedModule(bot: Mobibot) : AbstractModule(bot) { - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { - if (isEnabled && args.isNotEmpty()) { +abstract class ThreadedModule : AbstractModule() { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (isEnabled && event.message.isNotEmpty()) { runBlocking { launch { - run(sender, cmd, args, isPrivate) + run(channel, cmd, args, event) } } } else { - helpResponse(sender, isPrivate) + helpResponse(event) } } /** * Runs the thread. */ - abstract fun run( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) + abstract fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 17f2557..f6020e3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -34,21 +34,26 @@ package net.thauvin.erik.mobibot.modules import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.TwitterTimer import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.links.LinksMgr import net.thauvin.erik.mobibot.entries.EntriesUtils -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import twitter4j.TwitterException import twitter4j.TwitterFactory import twitter4j.conf.ConfigurationBuilder +import java.util.Timer /** * The Twitter module. */ -class Twitter(bot: Mobibot) : ThreadedModule(bot) { +class Twitter : ThreadedModule() { + private val logger: Logger = LoggerFactory.getLogger(Twitter::class.java) + + private val timer = Timer(true) + // Twitter auto-posts. private val entries: MutableSet<Int> = HashSet() @@ -87,16 +92,14 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Send a notification to the registered Twitter handle. */ fun notification(msg: String) { - with(bot) { - if (isEnabled && !handle.isNullOrBlank()) { - runBlocking { - launch { - try { - post(message = msg, isDm = true) - if (logger.isDebugEnabled) logger.debug("Notified @$handle: $msg") - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn("Failed to notify @$handle: $msg", e) - } + if (isEnabled && !handle.isNullOrBlank()) { + runBlocking { + launch { + try { + post(message = msg, isDm = true) + if (logger.isDebugEnabled) logger.debug("Notified @$handle: $msg") + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn("Failed to notify @$handle: $msg", e) } } } @@ -107,7 +110,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Posts on Twitter. */ @Throws(ModuleException::class) - fun post(handle: String = "${properties[HANDLE_PROP]}", message: String, isDm: Boolean): Message { + fun post(handle: String = "${properties[HANDLE_PROP]}", message: String, isDm: Boolean): String { return twitterPost( properties[CONSUMER_KEY_PROP], properties[CONSUMER_SECRET_PROP], @@ -123,35 +126,32 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Post an entry to twitter. */ fun postEntry(index: Int) { - with(bot) { - if (isAutoPost && hasEntry(index) && LinksMgr.entries.size >= index) { - val entry = LinksMgr.entries[index] - val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel" - runBlocking { - launch { - try { - if (logger.isDebugEnabled) { - logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkCmd(index)) - } - post(message = msg, isDm = false) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e) + if (isAutoPost && hasEntry(index) && LinksMgr.entries.links.size >= index) { + val entry = LinksMgr.entries.links[index] + val msg = "${entry.title} ${entry.link} via ${entry.nick} on ${entry.channel}" + runBlocking { + launch { + try { + if (logger.isDebugEnabled) { + logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkLabel(index)) } + post(message = msg, isDm = false) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e) } } - removeEntry(index) } + removeEntry(index) } } fun queueEntry(index: Int) { if (isAutoPost) { addEntry(index) - if (bot.logger.isDebugEnabled) { - bot.logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkCmd(index)) + if (logger.isDebugEnabled) { + logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkLabel(index)) } - @Suppress("MagicNumber") - bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L) + timer.schedule(TwitterTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L) } } @@ -162,18 +162,12 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { /** * Posts to twitter. */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { - with(bot) { - try { - send( - sender, - post(sender, "$args (by $sender on $channel)", false).msg, - isPrivate - ) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - send(sender, e.message, isPrivate) - } + override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + try { + event.respond(post(event.user.nick, "$args (by ${event.user.nick} on $channel)", false)) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + event.respond(e.message) } } @@ -181,6 +175,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { * Post all the entries to Twitter on shutdown. */ fun shutdown() { + timer.cancel() if (isAutoPost) { for (index in entries) { postEntry(index) @@ -213,23 +208,23 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) { handle: String?, message: String, isDm: Boolean - ): Message { + ): String { return try { - val cb = ConfigurationBuilder() - cb.setDebugEnabled(true) - .setOAuthConsumerKey(consumerKey) - .setOAuthConsumerSecret(consumerSecret) - .setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret) + val cb = ConfigurationBuilder().apply { + setDebugEnabled(true) + setOAuthConsumerKey(consumerKey) + setOAuthConsumerSecret(consumerSecret) + setOAuthAccessToken(token) + setOAuthAccessTokenSecret(tokenSecret) + } val tf = TwitterFactory(cb.build()) val twitter = tf.instance if (!isDm) { val status = twitter.updateStatus(message) - NoticeMessage( - "You message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}" - ) + "Your message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}" } else { val dm = twitter.sendDirectMessage(handle, message) - NoticeMessage(dm.text) + dm.text } } catch (e: TwitterException) { throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index ef6cb1f..1b2c525 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -35,45 +35,48 @@ import net.aksingh.owmjapis.api.APIException import net.aksingh.owmjapis.core.OWM import net.aksingh.owmjapis.core.OWM.Country import net.aksingh.owmjapis.model.CurrentWeather -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.capitalizeWords import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage import net.thauvin.erik.mobibot.msg.PublicMessage -import org.jibble.pircbot.Colors +import org.pircbotx.Colors +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory import kotlin.math.roundToInt /** * The `Weather2` module. */ -class Weather2(bot: Mobibot) : ThreadedModule(bot) { +class Weather2 : ThreadedModule() { + private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) + /** * Fetches the weather data from a specific city. */ - override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) { + override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { - with(bot) { - try { - val messages = getWeather(args, properties[OWM_API_KEY_PROP]) - if (messages[0].isError) { - helpResponse(sender, isPrivate) - } else { - for (msg in messages) { - send(sender, msg) - } + try { + val messages = getWeather(args, properties[OWM_API_KEY_PROP]) + if (messages[0].isError) { + helpResponse(event) + } else { + for (msg in messages) { + event.sendMessage(channel, msg) } - } catch (e: ModuleException) { - if (logger.isDebugEnabled) logger.debug(e.debugMessage, e) - send(e.message) } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + event.respond(e.message) } } else { - helpResponse(sender, isPrivate) + helpResponse(event) } } @@ -90,7 +93,6 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { * Converts and rounds temperature from °F to °C. */ fun ftoC(d: Double?): Pair<Int, Int> { - @Suppress("MagicNumber") val c = (d!! - 32) * 5 / 9 return d.roundToInt() to c.roundToInt() } @@ -208,7 +210,6 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) { * Converts and rounds temperature from mph to km/h. */ fun mphToKmh(w: Double): Pair<Int, Int> { - @Suppress("MagicNumber") val kmh = w * 1.60934 return w.roundToInt() to kmh.roundToInt() } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index e4deddb..3105d97 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -31,12 +31,11 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Mobibot import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.types.GenericMessageEvent import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter @@ -46,7 +45,7 @@ import java.util.Collections /** * The WorldTime module. */ -class WorldTime(bot: Mobibot) : AbstractModule(bot) { +class WorldTime : AbstractModule() { companion object { // Beats (Internet Time) keyword const val BEATS_KEYWORD = ".beats" @@ -57,6 +56,12 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { // The Time command private const val TIME_CMD = "time" + // The zones arguments + private const val ZONES_ARGS = "zones" + + // The default zone + private const val DEFAULT_ZONE = "PST" + // Date/Time Format private var dtf = DateTimeFormatter.ofPattern("'The time is ${bold("'HH:mm'")} on ${bold("'EEEE, d MMMM yyyy'")} in '") @@ -64,342 +69,316 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { /** * Returns the current Internet (beat) Time. */ - @Suppress("MagicNumber", "ImplicitDefaultLocale") private fun internetTime(): String { val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() - return String.format("%c%03d", '@', beats) + return "%c%03d".format('@', beats) } /** * Returns the time for the given timezone/city. */ @JvmStatic - fun time(query: String): Message { - val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).uppercase()] - val response: String = if (tz != null) { + fun time(query: String = DEFAULT_ZONE): String { + val tz = COUNTRIES_MAP[(if (query.isNotBlank()) query.trim().uppercase() else DEFAULT_ZONE)] + return if (tz != null) { if (BEATS_KEYWORD == tz) { - "The current Internet Time is: ${bold(internetTime())} $BEATS_KEYWORD" + "The current Internet Time is ${bold(internetTime())} $BEATS_KEYWORD" } else { (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) + bold(tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' '))) } } else { - return ErrorMessage("Unsupported country/zone. Please try again.") + "Unsupported country/zone. Please try again." } - return PublicMessage(response) } init { - // Initialize the countries map - val countries = mutableMapOf<String, String>() - countries["AD"] = "Europe/Andorra" - countries["AE"] = "Asia/Dubai" - countries["AF"] = "Asia/Kabul" - countries["AG"] = "America/Antigua" - countries["AI"] = "America/Anguilla" - countries["AKDT"] = "America/Anchorage" - countries["AKST"] = "America/Anchorage" - countries["AL"] = "Europe/Tirane" - countries["AM"] = "Asia/Yerevan" - countries["AO"] = "Africa/Luanda" - countries["AQ"] = "Antarctica/South_Pole" - countries["AR"] = "America/Argentina/Buenos_Aires" - countries["AS"] = "Pacific/Pago_Pago" - countries["AT"] = "Europe/Vienna" - countries["AU"] = "Australia/Sydney" - countries["AW"] = "America/Aruba" - countries["AX"] = "Europe/Mariehamn" - countries["AZ"] = "Asia/Baku" - countries["BA"] = "Europe/Sarajevo" - countries["BB"] = "America/Barbados" - countries["BD"] = "Asia/Dhaka" - countries["BE"] = "Europe/Brussels" - countries["BEAT"] = BEATS_KEYWORD - countries["BF"] = "Africa/Ouagadougou" - countries["BG"] = "Europe/Sofia" - countries["BH"] = "Asia/Bahrain" - countries["BI"] = "Africa/Bujumbura" - countries["BJ"] = "Africa/Porto-Novo" - countries["BL"] = "America/St_Barthelemy" - countries["BM"] = "Atlantic/Bermuda" - countries["BMT"] = BEATS_KEYWORD - countries["BN"] = "Asia/Brunei" - countries["BO"] = "America/La_Paz" - countries["BQ"] = "America/Kralendijk" - countries["BR"] = "America/Sao_Paulo" - countries["BS"] = "America/Nassau" - countries["BT"] = "Asia/Thimphu" - countries["BW"] = "Africa/Gaborone" - countries["BY"] = "Europe/Minsk" - countries["BZ"] = "America/Belize" - countries["CA"] = "America/Montreal" - countries["CC"] = "Indian/Cocos" - countries["CD"] = "Africa/Kinshasa" - countries["CDT"] = "America/Chicago" - countries["CET"] = "CET" - countries["CF"] = "Africa/Bangui" - countries["CG"] = "Africa/Brazzaville" - countries["CH"] = "Europe/Zurich" - countries["CI"] = "Africa/Abidjan" - countries["CK"] = "Pacific/Rarotonga" - countries["CL"] = "America/Santiago" - countries["CM"] = "Africa/Douala" - countries["CN"] = "Asia/Shanghai" - countries["CO"] = "America/Bogota" - countries["CR"] = "America/Costa_Rica" - countries["CST"] = "America/Chicago" - countries["CU"] = "Cuba" - countries["CV"] = "Atlantic/Cape_Verde" - countries["CW"] = "America/Curacao" - countries["CX"] = "Indian/Christmas" - countries["CY"] = "Asia/Nicosia" - countries["CZ"] = "Europe/Prague" - countries["DE"] = "Europe/Berlin" - countries["DJ"] = "Africa/Djibouti" - countries["DK"] = "Europe/Copenhagen" - countries["DM"] = "America/Dominica" - countries["DO"] = "America/Santo_Domingo" - countries["DZ"] = "Africa/Algiers" - countries["EC"] = "Pacific/Galapagos" - countries["EDT"] = "America/New_York" - countries["EE"] = "Europe/Tallinn" - countries["EG"] = "Africa/Cairo" - countries["EH"] = "Africa/El_Aaiun" - countries["ER"] = "Africa/Asmara" - countries["ES"] = "Europe/Madrid" - countries["EST"] = "America/New_York" - countries["ET"] = "Africa/Addis_Ababa" - countries["FI"] = "Europe/Helsinki" - countries["FJ"] = "Pacific/Fiji" - countries["FK"] = "Atlantic/Stanley" - countries["FM"] = "Pacific/Yap" - countries["FO"] = "Atlantic/Faroe" - countries["FR"] = "Europe/Paris" - countries["GA"] = "Africa/Libreville" - countries["GB"] = "Europe/London" - countries["GD"] = "America/Grenada" - countries["GE"] = "Asia/Tbilisi" - countries["GF"] = "America/Cayenne" - countries["GG"] = "Europe/Guernsey" - countries["GH"] = "Africa/Accra" - countries["GI"] = "Europe/Gibraltar" - countries["GL"] = "America/Thule" - countries["GM"] = "Africa/Banjul" - countries["GMT"] = "GMT" - countries["GN"] = "Africa/Conakry" - countries["GP"] = "America/Guadeloupe" - countries["GQ"] = "Africa/Malabo" - countries["GR"] = "Europe/Athens" - countries["GS"] = "Atlantic/South_Georgia" - countries["GT"] = "America/Guatemala" - countries["GU"] = "Pacific/Guam" - countries["GW"] = "Africa/Bissau" - countries["GY"] = "America/Guyana" - countries["HK"] = "Asia/Hong_Kong" - countries["HN"] = "America/Tegucigalpa" - countries["HR"] = "Europe/Zagreb" - countries["HST"] = "Pacific/Honolulu" - countries["HT"] = "America/Port-au-Prince" - countries["HU"] = "Europe/Budapest" - countries["ID"] = "Asia/Jakarta" - countries["IE"] = "Europe/Dublin" - countries["IL"] = "Asia/Tel_Aviv" - countries["IM"] = "Europe/Isle_of_Man" - countries["IN"] = "Asia/Kolkata" - countries["IO"] = "Indian/Chagos" - countries["IQ"] = "Asia/Baghdad" - countries["IR"] = "Asia/Tehran" - countries["IS"] = "Atlantic/Reykjavik" - countries["IT"] = "Europe/Rome" - countries["JE"] = "Europe/Jersey" - countries["JM"] = "Jamaica" - countries["JO"] = "Asia/Amman" - countries["JP"] = "Asia/Tokyo" - countries["KE"] = "Africa/Nairobi" - countries["KG"] = "Asia/Bishkek" - countries["KH"] = "Asia/Phnom_Penh" - countries["KI"] = "Pacific/Tarawa" - countries["KM"] = "Indian/Comoro" - countries["KN"] = "America/St_Kitts" - countries["KP"] = "Asia/Pyongyang" - countries["KR"] = "Asia/Seoul" - countries["KW"] = "Asia/Riyadh" - countries["KY"] = "America/Cayman" - countries["KZ"] = "Asia/Oral" - countries["LA"] = "Asia/Vientiane" - countries["LB"] = "Asia/Beirut" - countries["LC"] = "America/St_Lucia" - countries["LI"] = "Europe/Vaduz" - countries["LK"] = "Asia/Colombo" - countries["LR"] = "Africa/Monrovia" - countries["LS"] = "Africa/Maseru" - countries["LT"] = "Europe/Vilnius" - countries["LU"] = "Europe/Luxembourg" - countries["LV"] = "Europe/Riga" - countries["LY"] = "Africa/Tripoli" - countries["MA"] = "Africa/Casablanca" - countries["MC"] = "Europe/Monaco" - countries["MD"] = "Europe/Chisinau" - countries["MDT"] = "America/Denver" - countries["ME"] = "Europe/Podgorica" - countries["MF"] = "America/Marigot" - countries["MG"] = "Indian/Antananarivo" - countries["MH"] = "Pacific/Majuro" - countries["MK"] = "Europe/Skopje" - countries["ML"] = "Africa/Timbuktu" - countries["MM"] = "Asia/Yangon" - countries["MN"] = "Asia/Ulaanbaatar" - countries["MO"] = "Asia/Macau" - countries["MP"] = "Pacific/Saipan" - countries["MQ"] = "America/Martinique" - countries["MR"] = "Africa/Nouakchott" - countries["MS"] = "America/Montserrat" - countries["MST"] = "America/Denver" - countries["MT"] = "Europe/Malta" - countries["MU"] = "Indian/Mauritius" - countries["MV"] = "Indian/Maldives" - countries["MW"] = "Africa/Blantyre" - countries["MX"] = "America/Mexico_City" - countries["MY"] = "Asia/Kuala_Lumpur" - countries["MZ"] = "Africa/Maputo" - countries["NA"] = "Africa/Windhoek" - countries["NC"] = "Pacific/Noumea" - countries["NE"] = "Africa/Niamey" - countries["NF"] = "Pacific/Norfolk" - countries["NG"] = "Africa/Lagos" - countries["NI"] = "America/Managua" - countries["NL"] = "Europe/Amsterdam" - countries["NO"] = "Europe/Oslo" - countries["NP"] = "Asia/Kathmandu" - countries["NR"] = "Pacific/Nauru" - countries["NU"] = "Pacific/Niue" - countries["NZ"] = "Pacific/Auckland" - countries["OM"] = "Asia/Muscat" - countries["PA"] = "America/Panama" - countries["PDT"] = "America/Los_Angeles" - countries["PE"] = "America/Lima" - countries["PF"] = "Pacific/Tahiti" - countries["PG"] = "Pacific/Port_Moresby" - countries["PH"] = "Asia/Manila" - countries["PK"] = "Asia/Karachi" - countries["PL"] = "Europe/Warsaw" - countries["PM"] = "America/Miquelon" - countries["PN"] = "Pacific/Pitcairn" - countries["PR"] = "America/Puerto_Rico" - countries["PS"] = "Asia/Gaza" - countries["PST"] = "America/Los_Angeles" - countries["PT"] = "Europe/Lisbon" - countries["PW"] = "Pacific/Palau" - countries["PY"] = "America/Asuncion" - countries["QA"] = "Asia/Qatar" - countries["RE"] = "Indian/Reunion" - countries["RO"] = "Europe/Bucharest" - countries["RS"] = "Europe/Belgrade" - countries["RU"] = "Europe/Moscow" - countries["RW"] = "Africa/Kigali" - countries["SA"] = "Asia/Riyadh" - countries["SB"] = "Pacific/Guadalcanal" - countries["SC"] = "Indian/Mahe" - countries["SD"] = "Africa/Khartoum" - countries["SE"] = "Europe/Stockholm" - countries["SG"] = "Asia/Singapore" - countries["SH"] = "Atlantic/St_Helena" - countries["SI"] = "Europe/Ljubljana" - countries["SJ"] = "Atlantic/Jan_Mayen" - countries["SK"] = "Europe/Bratislava" - countries["SL"] = "Africa/Freetown" - countries["SM"] = "Europe/San_Marino" - countries["SN"] = "Africa/Dakar" - countries["SO"] = "Africa/Mogadishu" - countries["SR"] = "America/Paramaribo" - countries["SS"] = "Africa/Juba" - countries["ST"] = "Africa/Sao_Tome" - countries["SV"] = "America/El_Salvador" - countries["SX"] = "America/Lower_Princes" - countries["SY"] = "Asia/Damascus" - countries["SZ"] = "Africa/Mbabane" - countries["TC"] = "America/Grand_Turk" - countries["TD"] = "Africa/Ndjamena" - countries["TF"] = "Indian/Kerguelen" - countries["TG"] = "Africa/Lome" - countries["TH"] = "Asia/Bangkok" - countries["TJ"] = "Asia/Dushanbe" - countries["TK"] = "Pacific/Fakaofo" - countries["TL"] = "Asia/Dili" - countries["TM"] = "Asia/Ashgabat" - countries["TN"] = "Africa/Tunis" - countries["TO"] = "Pacific/Tongatapu" - countries["TR"] = "Europe/Istanbul" - countries["TT"] = "America/Port_of_Spain" - countries["TV"] = "Pacific/Funafuti" - countries["TW"] = "Asia/Taipei" - countries["TZ"] = "Africa/Dar_es_Salaam" - countries["UA"] = "Europe/Kiev" - countries["UG"] = "Africa/Kampala" - countries["UK"] = "Europe/London" - countries["UM"] = "Pacific/Wake" - countries["US"] = "America/New_York" - countries["UTC"] = "UTC" - countries["UY"] = "America/Montevideo" - countries["UZ"] = "Asia/Tashkent" - countries["VA"] = "Europe/Vatican" - countries["VC"] = "America/St_Vincent" - countries["VE"] = "America/Caracas" - countries["VG"] = "America/Tortola" - countries["VI"] = "America/St_Thomas" - countries["VN"] = "Asia/Ho_Chi_Minh" - countries["VU"] = "Pacific/Efate" - countries["WF"] = "Pacific/Wallis" - countries["WS"] = "Pacific/Apia" - countries["YE"] = "Asia/Aden" - countries["YT"] = "Indian/Mayotte" - countries["ZA"] = "Africa/Johannesburg" - countries["ZM"] = "Africa/Lusaka" - countries["ZULU"] = "Zulu" - countries["ZW"] = "Africa/Harare" - @Suppress("MagicNumber") + // Initialize the zones map + val zones = mutableMapOf<String, String>() + zones["AD"] = "Europe/Andorra" + zones["AE"] = "Asia/Dubai" + zones["AF"] = "Asia/Kabul" + zones["AG"] = "America/Antigua" + zones["AI"] = "America/Anguilla" + zones["AKDT"] = "America/Anchorage" + zones["AKST"] = "America/Anchorage" + zones["AL"] = "Europe/Tirane" + zones["AM"] = "Asia/Yerevan" + zones["AO"] = "Africa/Luanda" + zones["AQ"] = "Antarctica/South_Pole" + zones["AR"] = "America/Argentina/Buenos_Aires" + zones["AS"] = "Pacific/Pago_Pago" + zones["AT"] = "Europe/Vienna" + zones["AU"] = "Australia/Sydney" + zones["AW"] = "America/Aruba" + zones["AX"] = "Europe/Mariehamn" + zones["AZ"] = "Asia/Baku" + zones["BA"] = "Europe/Sarajevo" + zones["BB"] = "America/Barbados" + zones["BD"] = "Asia/Dhaka" + zones["BE"] = "Europe/Brussels" + zones["BEAT"] = BEATS_KEYWORD + zones["BF"] = "Africa/Ouagadougou" + zones["BG"] = "Europe/Sofia" + zones["BH"] = "Asia/Bahrain" + zones["BI"] = "Africa/Bujumbura" + zones["BJ"] = "Africa/Porto-Novo" + zones["BL"] = "America/St_Barthelemy" + zones["BM"] = "Atlantic/Bermuda" + zones["BMT"] = BEATS_KEYWORD + zones["BN"] = "Asia/Brunei" + zones["BO"] = "America/La_Paz" + zones["BQ"] = "America/Kralendijk" + zones["BR"] = "America/Sao_Paulo" + zones["BS"] = "America/Nassau" + zones["BT"] = "Asia/Thimphu" + zones["BW"] = "Africa/Gaborone" + zones["BY"] = "Europe/Minsk" + zones["BZ"] = "America/Belize" + zones["CA"] = "America/Montreal" + zones["CC"] = "Indian/Cocos" + zones["CD"] = "Africa/Kinshasa" + zones["CDT"] = "America/Chicago" + zones["CET"] = "CET" + zones["CF"] = "Africa/Bangui" + zones["CG"] = "Africa/Brazzaville" + zones["CH"] = "Europe/Zurich" + zones["CI"] = "Africa/Abidjan" + zones["CK"] = "Pacific/Rarotonga" + zones["CL"] = "America/Santiago" + zones["CM"] = "Africa/Douala" + zones["CN"] = "Asia/Shanghai" + zones["CO"] = "America/Bogota" + zones["CR"] = "America/Costa_Rica" + zones["CST"] = "America/Chicago" + zones["CU"] = "Cuba" + zones["CV"] = "Atlantic/Cape_Verde" + zones["CW"] = "America/Curacao" + zones["CX"] = "Indian/Christmas" + zones["CY"] = "Asia/Nicosia" + zones["CZ"] = "Europe/Prague" + zones["DE"] = "Europe/Berlin" + zones["DJ"] = "Africa/Djibouti" + zones["DK"] = "Europe/Copenhagen" + zones["DM"] = "America/Dominica" + zones["DO"] = "America/Santo_Domingo" + zones["DZ"] = "Africa/Algiers" + zones["EC"] = "Pacific/Galapagos" + zones["EDT"] = "America/New_York" + zones["EE"] = "Europe/Tallinn" + zones["EG"] = "Africa/Cairo" + zones["EH"] = "Africa/El_Aaiun" + zones["ER"] = "Africa/Asmara" + zones["ES"] = "Europe/Madrid" + zones["EST"] = "America/New_York" + zones["ET"] = "Africa/Addis_Ababa" + zones["FI"] = "Europe/Helsinki" + zones["FJ"] = "Pacific/Fiji" + zones["FK"] = "Atlantic/Stanley" + zones["FM"] = "Pacific/Yap" + zones["FO"] = "Atlantic/Faroe" + zones["FR"] = "Europe/Paris" + zones["GA"] = "Africa/Libreville" + zones["GB"] = "Europe/London" + zones["GD"] = "America/Grenada" + zones["GE"] = "Asia/Tbilisi" + zones["GF"] = "America/Cayenne" + zones["GG"] = "Europe/Guernsey" + zones["GH"] = "Africa/Accra" + zones["GI"] = "Europe/Gibraltar" + zones["GL"] = "America/Thule" + zones["GM"] = "Africa/Banjul" + zones["GMT"] = "GMT" + zones["GN"] = "Africa/Conakry" + zones["GP"] = "America/Guadeloupe" + zones["GQ"] = "Africa/Malabo" + zones["GR"] = "Europe/Athens" + zones["GS"] = "Atlantic/South_Georgia" + zones["GT"] = "America/Guatemala" + zones["GU"] = "Pacific/Guam" + zones["GW"] = "Africa/Bissau" + zones["GY"] = "America/Guyana" + zones["HK"] = "Asia/Hong_Kong" + zones["HN"] = "America/Tegucigalpa" + zones["HR"] = "Europe/Zagreb" + zones["HST"] = "Pacific/Honolulu" + zones["HT"] = "America/Port-au-Prince" + zones["HU"] = "Europe/Budapest" + zones["ID"] = "Asia/Jakarta" + zones["IE"] = "Europe/Dublin" + zones["IL"] = "Asia/Tel_Aviv" + zones["IM"] = "Europe/Isle_of_Man" + zones["IN"] = "Asia/Kolkata" + zones["IO"] = "Indian/Chagos" + zones["IQ"] = "Asia/Baghdad" + zones["IR"] = "Asia/Tehran" + zones["IS"] = "Atlantic/Reykjavik" + zones["IT"] = "Europe/Rome" + zones["JE"] = "Europe/Jersey" + zones["JM"] = "Jamaica" + zones["JO"] = "Asia/Amman" + zones["JP"] = "Asia/Tokyo" + zones["KE"] = "Africa/Nairobi" + zones["KG"] = "Asia/Bishkek" + zones["KH"] = "Asia/Phnom_Penh" + zones["KI"] = "Pacific/Tarawa" + zones["KM"] = "Indian/Comoro" + zones["KN"] = "America/St_Kitts" + zones["KP"] = "Asia/Pyongyang" + zones["KR"] = "Asia/Seoul" + zones["KW"] = "Asia/Riyadh" + zones["KY"] = "America/Cayman" + zones["KZ"] = "Asia/Oral" + zones["LA"] = "Asia/Vientiane" + zones["LB"] = "Asia/Beirut" + zones["LC"] = "America/St_Lucia" + zones["LI"] = "Europe/Vaduz" + zones["LK"] = "Asia/Colombo" + zones["LR"] = "Africa/Monrovia" + zones["LS"] = "Africa/Maseru" + zones["LT"] = "Europe/Vilnius" + zones["LU"] = "Europe/Luxembourg" + zones["LV"] = "Europe/Riga" + zones["LY"] = "Africa/Tripoli" + zones["MA"] = "Africa/Casablanca" + zones["MC"] = "Europe/Monaco" + zones["MD"] = "Europe/Chisinau" + zones["MDT"] = "America/Denver" + zones["ME"] = "Europe/Podgorica" + zones["MF"] = "America/Marigot" + zones["MG"] = "Indian/Antananarivo" + zones["MH"] = "Pacific/Majuro" + zones["MK"] = "Europe/Skopje" + zones["ML"] = "Africa/Timbuktu" + zones["MM"] = "Asia/Yangon" + zones["MN"] = "Asia/Ulaanbaatar" + zones["MO"] = "Asia/Macau" + zones["MP"] = "Pacific/Saipan" + zones["MQ"] = "America/Martinique" + zones["MR"] = "Africa/Nouakchott" + zones["MS"] = "America/Montserrat" + zones["MST"] = "America/Denver" + zones["MT"] = "Europe/Malta" + zones["MU"] = "Indian/Mauritius" + zones["MV"] = "Indian/Maldives" + zones["MW"] = "Africa/Blantyre" + zones["MX"] = "America/Mexico_City" + zones["MY"] = "Asia/Kuala_Lumpur" + zones["MZ"] = "Africa/Maputo" + zones["NA"] = "Africa/Windhoek" + zones["NC"] = "Pacific/Noumea" + zones["NE"] = "Africa/Niamey" + zones["NF"] = "Pacific/Norfolk" + zones["NG"] = "Africa/Lagos" + zones["NI"] = "America/Managua" + zones["NL"] = "Europe/Amsterdam" + zones["NO"] = "Europe/Oslo" + zones["NP"] = "Asia/Kathmandu" + zones["NR"] = "Pacific/Nauru" + zones["NU"] = "Pacific/Niue" + zones["NZ"] = "Pacific/Auckland" + zones["OM"] = "Asia/Muscat" + zones["PA"] = "America/Panama" + zones["PDT"] = "America/Los_Angeles" + zones["PE"] = "America/Lima" + zones["PF"] = "Pacific/Tahiti" + zones["PG"] = "Pacific/Port_Moresby" + zones["PH"] = "Asia/Manila" + zones["PK"] = "Asia/Karachi" + zones["PL"] = "Europe/Warsaw" + zones["PM"] = "America/Miquelon" + zones["PN"] = "Pacific/Pitcairn" + zones["PR"] = "America/Puerto_Rico" + zones["PS"] = "Asia/Gaza" + zones["PST"] = "America/Los_Angeles" + zones["PT"] = "Europe/Lisbon" + zones["PW"] = "Pacific/Palau" + zones["PY"] = "America/Asuncion" + zones["QA"] = "Asia/Qatar" + zones["RE"] = "Indian/Reunion" + zones["RO"] = "Europe/Bucharest" + zones["RS"] = "Europe/Belgrade" + zones["RU"] = "Europe/Moscow" + zones["RW"] = "Africa/Kigali" + zones["SA"] = "Asia/Riyadh" + zones["SB"] = "Pacific/Guadalcanal" + zones["SC"] = "Indian/Mahe" + zones["SD"] = "Africa/Khartoum" + zones["SE"] = "Europe/Stockholm" + zones["SG"] = "Asia/Singapore" + zones["SH"] = "Atlantic/St_Helena" + zones["SI"] = "Europe/Ljubljana" + zones["SJ"] = "Atlantic/Jan_Mayen" + zones["SK"] = "Europe/Bratislava" + zones["SL"] = "Africa/Freetown" + zones["SM"] = "Europe/San_Marino" + zones["SN"] = "Africa/Dakar" + zones["SO"] = "Africa/Mogadishu" + zones["SR"] = "America/Paramaribo" + zones["SS"] = "Africa/Juba" + zones["ST"] = "Africa/Sao_Tome" + zones["SV"] = "America/El_Salvador" + zones["SX"] = "America/Lower_Princes" + zones["SY"] = "Asia/Damascus" + zones["SZ"] = "Africa/Mbabane" + zones["TC"] = "America/Grand_Turk" + zones["TD"] = "Africa/Ndjamena" + zones["TF"] = "Indian/Kerguelen" + zones["TG"] = "Africa/Lome" + zones["TH"] = "Asia/Bangkok" + zones["TJ"] = "Asia/Dushanbe" + zones["TK"] = "Pacific/Fakaofo" + zones["TL"] = "Asia/Dili" + zones["TM"] = "Asia/Ashgabat" + zones["TN"] = "Africa/Tunis" + zones["TO"] = "Pacific/Tongatapu" + zones["TR"] = "Europe/Istanbul" + zones["TT"] = "America/Port_of_Spain" + zones["TV"] = "Pacific/Funafuti" + zones["TW"] = "Asia/Taipei" + zones["TZ"] = "Africa/Dar_es_Salaam" + zones["UA"] = "Europe/Kiev" + zones["UG"] = "Africa/Kampala" + zones["UK"] = "Europe/London" + zones["UM"] = "Pacific/Wake" + zones["US"] = "America/New_York" + zones["UTC"] = "UTC" + zones["UY"] = "America/Montevideo" + zones["UZ"] = "Asia/Tashkent" + zones["VA"] = "Europe/Vatican" + zones["VC"] = "America/St_Vincent" + zones["VE"] = "America/Caracas" + zones["VG"] = "America/Tortola" + zones["VI"] = "America/St_Thomas" + zones["VN"] = "Asia/Ho_Chi_Minh" + zones["VU"] = "Pacific/Efate" + zones["WF"] = "Pacific/Wallis" + zones["WS"] = "Pacific/Apia" + zones["YE"] = "Asia/Aden" + zones["YT"] = "Indian/Mayotte" + zones["ZA"] = "Africa/Johannesburg" + zones["ZM"] = "Africa/Lusaka" + zones["ZULU"] = "Zulu" + zones["ZW"] = "Africa/Harare" ZoneId.getAvailableZoneIds().stream() .filter { tz: String -> - tz.length <= 3 && !countries.containsKey(tz) + tz.length <= 3 && !zones.containsKey(tz) } .forEach { tz: String -> - countries[tz] = tz + zones[tz] = tz } - COUNTRIES_MAP = Collections.unmodifiableMap(countries) + COUNTRIES_MAP = Collections.unmodifiableMap(zones) } } - override fun commandResponse( - sender: String, - cmd: String, - args: String, - isPrivate: Boolean - ) { - with(bot) { - if (args.isEmpty()) { - send(sender, "The supported countries/zones are: ", isPrivate) - @Suppress("MagicNumber") - sendList( - sender, - COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, - 14, - isPrivate = false, - isIndent = true - ) - } else { - val msg = time(args) - if (isPrivate) { - send(sender, msg.msg, true) - } else { - if (msg.isError) { - send(sender, msg.msg, false) - } else { - send(msg.msg) - } - } - } + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.equals(ZONES_ARGS, true)) { + event.sendMessage("The supported countries/zones are: ") + event.sendList(COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, 14, isIndent = true) + } else { + event.respond(time(args)) } } @@ -408,9 +387,9 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) { init { with(help) { add("To display a country's current date/time:") - add(helpFormat("%c $TIME_CMD <country code>")) - add("For a listing of the supported countries:") - add(helpFormat("%c $TIME_CMD")) + add(helpFormat("%c $TIME_CMD [<country code or zone>]")) + add("For a listing of the supported countries/zones:") + add(helpFormat("%c $TIME_CMD $ZONES_ARGS")) } commands.add(TIME_CMD) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt similarity index 82% rename from src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index e8cc448..1660df7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/Sanitize.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,5 +1,5 @@ /* - * Sanitize.kt + * ExceptionSanitizer.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -36,27 +36,26 @@ import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.replaceEach import net.thauvin.erik.mobibot.modules.ModuleException -object Sanitize { +object ExceptionSanitizer { /** * Returns a sanitized exception to avoid displaying api keys, etc. in CI logs. */ - fun sanitizeException(e: ModuleException, vararg sanitize: String): ModuleException { - var sanitizedException = e + fun ModuleException.sanitize(vararg sanitize: String): ModuleException { val search = sanitize.filter { it.isNotBlank() }.toTypedArray() if (search.isNotEmpty()) { val obfuscate = search.map { it.obfuscate() }.toTypedArray() - with(e) { - if (cause?.message != null) { - sanitizedException = ModuleException( + with(this) { + if (!cause?.message.isNullOrBlank()) { + return ModuleException( debugMessage, cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(search, obfuscate), this ) - } else if (message != null) { - sanitizedException = ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this) + } else if (!message.isNullOrBlank()) { + return ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this) } } } - return sanitizedException + return this } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 234f84b..dadf542 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -31,12 +31,14 @@ */ package net.thauvin.erik.mobibot +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isFailure +import assertk.assertions.isInstanceOf import com.rometools.rome.io.FeedException import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test - import java.io.FileNotFoundException import java.net.MalformedURLException import java.net.UnknownHostException @@ -48,26 +50,24 @@ class FeedReaderTest { @Test fun readFeedTest() { var messages = readFeed("https://feeds.thauvin.net/ethauvin") - assertThat(messages.size).describedAs("messages = 10").isEqualTo(10) - assertThat(messages[1].msg).describedAs("feed entry url").contains("ethauvin") + assertThat(messages.size, "size = 10").isEqualTo(10) + assertThat(messages[1].msg, "feed entry url").contains("ethauvin") messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") - assertThat(messages[0].msg).describedAs("nothing to view").contains("nothing") + assertThat(messages[0].msg, "nothing to view").contains("nothing") messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=42", 42) - assertThat(messages.size).describedAs("messages = 84").isEqualTo(84) - assertThat(messages.last().msg).describedAs("example entry url").contains("http://example.com/test/") + assertThat(messages.size, "messages = 84").isEqualTo(84) + assertThat(messages.last().msg, "example entry url").contains("http://example.com/test/") - assertThatThrownBy { readFeed("blah") }.describedAs("invalid URL") - .isInstanceOf(MalformedURLException::class.java) + assertThat { readFeed("blah") }.isFailure().isInstanceOf(MalformedURLException::class.java) - assertThatThrownBy { readFeed("https://www.example.com") }.describedAs("not a feed") - .isInstanceOf(FeedException::class.java) + assertThat { readFeed("https://www.example.com") }.isFailure().isInstanceOf(FeedException::class.java) - assertThatThrownBy { readFeed("https://www.examples.com/foo") }.describedAs("404 not found") + assertThat { readFeed("https://www.examples.com/foo") }.isFailure() .isInstanceOf(FileNotFoundException::class.java) - assertThatThrownBy { readFeed("https://www.doesnotexists.com") }.describedAs("unknown host") + assertThat { readFeed("https://www.doesnotexists.com") }.isFailure() .isInstanceOf(UnknownHostException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt similarity index 81% rename from src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 93ff940..76b5b95 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,5 +1,5 @@ /* - * PinboardUtilsTest.kt + * PinboardTest.kt * * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. @@ -32,45 +32,39 @@ package net.thauvin.erik.mobibot -import net.thauvin.erik.mobibot.PinboardUtils.toTimestamp import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.pinboard.PinboardPoster import org.testng.Assert.assertFalse import org.testng.Assert.assertTrue import org.testng.annotations.Test import java.net.URL -import java.util.Date -class PinboardUtilsTest : LocalProperties() { +class PinboardTest : LocalProperties() { + private val pinboard = Pinboard() + @Test - fun pinboardTest() { + fun testPinboard() { val apiToken = getProperty("pinboard-api-token") - val pinboard = PinboardPoster(apiToken) val url = "https://www.example.com/" val ircServer = "irc.test.com" val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) - PinboardUtils.addPin(pinboard, ircServer, entry) + pinboard.setApiToken(apiToken) + + pinboard.addPin(ircServer, entry) assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "validate add") entry.link = "https://www.foo.com/" - PinboardUtils.updatePin(pinboard, ircServer, url, entry) + pinboard.updatePin(ircServer, url, entry) assertTrue(validatePin(apiToken, url = entry.link, ircServer), "validate update") entry.title = "Foo Title" - PinboardUtils.updatePin(pinboard, ircServer, entry.link, entry) + pinboard.updatePin(ircServer, entry.link, entry) assertTrue(validatePin(apiToken, url = entry.link, entry.title), "validate title") - PinboardUtils.deletePin(pinboard, entry) + pinboard.deletePin(entry) assertFalse(validatePin(apiToken, url = entry.link), "validate delete") } - @Test - fun toTimestampTest() { - val d = Date() - assertTrue(d.toTimestamp().matches("[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z".toRegex())) - } - private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { val response = Utils.urlReader( URL( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 2dc0ef8..fc8f117 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -31,6 +31,8 @@ */ package net.thauvin.erik.mobibot +import assertk.assertThat +import assertk.assertions.isEqualTo import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.buildCmdSyntax @@ -55,8 +57,7 @@ import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import org.assertj.core.api.Assertions.assertThat -import org.jibble.pircbot.Colors +import org.pircbotx.Colors import org.testng.annotations.BeforeClass import org.testng.annotations.Test import java.io.File @@ -86,59 +87,59 @@ class UtilsTest { val dir = "dir" val sep = '/' val url = "https://erik.thauvin.net" - assertThat(dir.appendIfMissing(File.separatorChar)).describedAs("appendIfMissing(dir)") + assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") .isEqualTo(dir + File.separatorChar) - assertThat(url.appendIfMissing(sep)).describedAs("appendIfMissing(url)").isEqualTo("$url$sep") - assertThat("$url$sep".appendIfMissing(sep)).describedAs("appendIfMissing($url$sep)").isEqualTo("$url$sep") + assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") + assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") } @Test fun testBold() { - assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - assertThat(bold(2L)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) - assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat(bold("test")).describedAs("bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) + assertThat(bold(1), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(bold(2L), "bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) + assertThat(bold(ascii), "bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(bold("test"), "bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) } @Test fun testBuildCmdSyntax() { val bot = "mobibot" - assertThat(buildCmdSyntax("%c $test %n $test", bot, false)).describedAs("public") + assertThat(buildCmdSyntax("%c $test %n $test", bot, false), "public") .isEqualTo("$bot: $test $bot $test") - assertThat(buildCmdSyntax("%c %n $test %c $test %n", bot, true)).describedAs("public") + assertThat(buildCmdSyntax("%c %n $test %c $test %n", bot, true), "public") .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") } @Test fun testCapitalise() { - assertThat("test".capitalise()).describedAs("capitalize(test)").isEqualTo("Test") - assertThat("Test".capitalise()).describedAs("capitalize(Test)").isEqualTo("Test") - assertThat(test.capitalise()).describedAs("capitalize($test)").isEqualTo(test) - assertThat("".capitalise()).describedAs("capitalize()").isEqualTo("") + assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test") + assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test") + assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test) + assertThat("".capitalise(), "capitalize()").isEqualTo("") } @Test fun textCapitaliseWords() { - assertThat(test.capitalizeWords()).describedAs("captiatlizeWords(test)").isEqualTo("This Is A Test.") - assertThat("Already Capitalized".capitalizeWords()).describedAs("already capitalized") + assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") + assertThat("Already Capitalized".capitalizeWords(), "already capitalized") .isEqualTo("Already Capitalized") - assertThat(" a test ".capitalizeWords()).describedAs("with spaces").isEqualTo(" A Test ") + assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") } @Test fun testColorize() { - assertThat(colorize(ascii, Colors.REVERSE)).describedAs("colorize(reverse)").isEqualTo( + assertThat(colorize(ascii, Colors.REVERSE), "colorize(reverse)").isEqualTo( Colors.REVERSE + ascii + Colors.REVERSE ) - assertThat(colorize(ascii, Colors.RED)).describedAs("colorize(red)") + assertThat(colorize(ascii, Colors.RED), "colorize(red)") .isEqualTo(Colors.RED + ascii + Colors.NORMAL) - assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)") + assertThat(colorize(ascii, Colors.BOLD), "colorized(bold)") .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo("") - assertThat(colorize("", Colors.RED)).describedAs("colorize()").isEqualTo("") - assertThat(colorize(ascii, DEFAULT_COLOR)).describedAs("colorize(none)").isEqualTo(ascii) - assertThat(colorize(" ", Colors.NORMAL)).describedAs("colorize(blank)") + assertThat(colorize(null, Colors.RED), "colorize(null)").isEqualTo("") + assertThat(colorize("", Colors.RED), "colorize()").isEqualTo("") + assertThat(colorize(ascii, DEFAULT_COLOR), "colorize(none)").isEqualTo(ascii) + assertThat(colorize(" ", Colors.NORMAL), "colorize(blank)") .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) } @@ -157,9 +158,9 @@ class UtilsTest { val p = Properties() p["one"] = "1" p["two"] = "two" - assertThat(p.getIntProperty("one", 9)).describedAs("getIntProperty(one)").isEqualTo(1) - assertThat(p.getIntProperty("two", 2)).describedAs("getIntProperty(two)").isEqualTo(2) - assertThat(p.getIntProperty("foo", 3)).describedAs("getIntProperty(foo)").isEqualTo(3) + assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1) + assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2) + assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3) } @Test @@ -169,26 +170,26 @@ class UtilsTest { @Test fun testHelpFormat() { - assertThat(helpFormat(test, isBold = true, isIndent = false)).describedAs("bold") + assertThat(helpFormat(test, isBold = true, isIndent = false), "bold") .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") - assertThat(helpFormat(test, isBold = false, isIndent = true)).describedAs("indent") + assertThat(helpFormat(test, isBold = false, isIndent = true), "indent") .isEqualTo(test.prependIndent()) - assertThat(helpFormat(test, isBold = true, isIndent = true)).describedAs("bold-indent") + assertThat(helpFormat(test, isBold = true, isIndent = true), "bold-indent") .isEqualTo(colorize(test, Colors.BOLD).prependIndent()) } @Test fun testIsoLocalDate() { - assertThat(cal.time.toIsoLocalDate()).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17") - assertThat(localDateTime.toIsoLocalDate()).describedAs("isoLocalDate(localDate)").isEqualTo("1952-02-17") + assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17") + assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17") } @Test fun testObfuscate() { - assertThat(ascii.obfuscate().length).describedAs("obfuscate is right length").isEqualTo(ascii.length) - assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo("x".repeat(ascii.length)) - assertThat(" ".obfuscate()).describedAs("obfuscate(blank)").isEqualTo(" ") + assertThat(ascii.obfuscate().length, "obfuscate is right length").isEqualTo(ascii.length) + assertThat(ascii.obfuscate(), "obfuscate()").isEqualTo("x".repeat(ascii.length)) + assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ") } @Test @@ -197,7 +198,7 @@ class UtilsTest { val weeks = "weeks" for (i in -1..3) { - assertThat(week.plural(i.toLong())).describedAs("plural($i)").isEqualTo(if (i > 1) weeks else week) + assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week) } } @@ -205,15 +206,15 @@ class UtilsTest { fun testReplaceEach() { val search = arrayOf("one", "two", "three") val replace = arrayOf("1", "2", "3") - assertThat(search.joinToString(",").replaceEach(search, replace)).describedAs("replaceEach(1,2,3") + assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") .isEqualTo(replace.joinToString(",")) - assertThat(test.replaceEach(search, replace)).describedAs("replaceEach(nothing)").isEqualTo(test) + assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) - assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E"))).describedAs("replaceEach($test)") + assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") .isEqualTo(test.replace("t", "").replace("e", "E")) - assertThat(test.replaceEach(search, emptyArray())).describedAs("replaceEach(search, empty)") + assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") .isEqualTo(test) } @@ -234,8 +235,8 @@ class UtilsTest { @Test fun testToIntOrDefault() { - assertThat("10".toIntOrDefault(1)).describedAs("toIntOrDefault(10, 1)").isEqualTo(10) - assertThat("a".toIntOrDefault(2)).describedAs("toIntOrDefault(a, 2)").isEqualTo(2) + assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10) + assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2) } @Test @@ -247,26 +248,26 @@ class UtilsTest { @Test fun testUptime() { - assertThat(uptime(547800300076L)).describedAs("full") + assertThat(uptime(547800300076L), "full") .isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes") - assertThat(uptime(2700000L)).describedAs("minutes").isEqualTo("45 minutes") - assertThat(uptime(24300000L)).describedAs("hours minutes").isEqualTo("6 hours 45 minutes") - assertThat(uptime(110700000L)).describedAs("days hours minutes").isEqualTo("1 day 6 hours 45 minutes") - assertThat(uptime(1320300000L)).describedAs("weeks days hours minutes") + assertThat(uptime(2700000L), "minutes").isEqualTo("45 minutes") + assertThat(uptime(24300000L), "hours minutes").isEqualTo("6 hours 45 minutes") + assertThat(uptime(110700000L), "days hours minutes").isEqualTo("1 day 6 hours 45 minutes") + assertThat(uptime(1320300000L), "weeks days hours minutes") .isEqualTo("2 weeks 1 day 6 hours 45 minutes") - assertThat(uptime(0L)).describedAs("0 minutes").isEqualTo("0 minute") + assertThat(uptime(0L), "0 minutes").isEqualTo("0 minute") } @Test @Throws(IOException::class) fun testUrlReader() { - assertThat(urlReader(URL("https://postman-echo.com/status/200"))).describedAs("urlReader()") + assertThat(urlReader(URL("https://postman-echo.com/status/200")), "urlReader()") .isEqualTo("{\"status\":200}") } @Test fun testUtcDateTime() { - assertThat(cal.time.toUtcDateTime()).describedAs("utcDateTime(date)").isEqualTo("1952-02-17 12:30") - assertThat(localDateTime.toUtcDateTime()).describedAs("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") + assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30") + assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 86422ca..808ea97 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -31,7 +31,12 @@ */ package net.thauvin.erik.mobibot.commands.tell -import org.assertj.core.api.Assertions.assertThat +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import assertk.assertions.prop import org.testng.annotations.Test import java.time.Duration import java.time.LocalDateTime @@ -51,18 +56,21 @@ class TellMessageTest { val recipient = "recipient" val sender = "sender" val tellMessage = TellMessage(sender, recipient, message) - assertThat(tellMessage).extracting("sender", "recipient", "message") - .containsExactly(sender, recipient, message) - assertThat(isValidDate(tellMessage.queued)).describedAs("queued is valid date/time").isTrue - assertThat(tellMessage.isMatch(sender)).describedAs("match sender").isTrue - assertThat(tellMessage.isMatch(recipient)).describedAs("match recipient").isTrue - assertThat(tellMessage.isMatch("foo")).describedAs("foo is no match").isFalse + assertThat(tellMessage).all { + prop(TellMessage::sender).isEqualTo(sender) + prop(TellMessage::recipient).isEqualTo(recipient) + prop(TellMessage::message).isEqualTo(message) + } + assertThat(isValidDate(tellMessage.queued), "queued is valid date/time").isTrue() + assertThat(tellMessage.isMatch(sender), "match sender").isTrue() + assertThat(tellMessage.isMatch(recipient), "match recipient").isTrue() + assertThat(tellMessage.isMatch("foo"), "foo is no match").isFalse() tellMessage.isReceived = false - assertThat(tellMessage.receptionDate).describedAs("reception date not set").isEqualTo(LocalDateTime.MIN) + assertThat(tellMessage.receptionDate, "reception date not set").isEqualTo(LocalDateTime.MIN) tellMessage.isReceived = true - assertThat(tellMessage.isReceived).describedAs("is received").isTrue - assertThat(isValidDate(tellMessage.receptionDate)).describedAs("received is valid date/time").isTrue + assertThat(tellMessage.isReceived, "is received").isTrue() + assertThat(isValidDate(tellMessage.receptionDate), "received is valid date/time").isTrue() tellMessage.isNotified = true - assertThat(tellMessage.isNotified).describedAs("is notified").isTrue + assertThat(tellMessage.isNotified, "is notified").isTrue() } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 347b508..94a6b7e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -31,9 +31,16 @@ */ package net.thauvin.erik.mobibot.entries +import assertk.all +import assertk.assertThat +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import assertk.assertions.prop +import assertk.assertions.size import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl -import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test import java.security.SecureRandom import java.util.Date @@ -58,21 +65,30 @@ class EntryLinkTest { entryLink.addComment("c$i", "u$i") i++ } - assertThat(entryLink.comments.size).describedAs("getComments().size() == 5").isEqualTo(i) + assertThat(entryLink.comments.size, "getComments().size() == 5").isEqualTo(i) i = 0 for (comment in entryLink.comments) { - assertThat(comment).extracting("comment", "nick").containsExactly("c$i", "u$i") + assertThat(comment).all { + prop(EntryComment::comment).isEqualTo("c$i") + prop(EntryComment::nick).isEqualTo("u$i") + } i++ } + val r = SecureRandom() while (entryLink.comments.size > 0) { entryLink.deleteComment(r.nextInt(entryLink.comments.size)) } - assertThat(entryLink.comments).describedAs("hasComments()").isEmpty() + assertThat(entryLink.comments, "hasComments()").isEmpty() entryLink.addComment("nothing", "nobody") entryLink.setComment(0, "something", "somebody") - assertThat(entryLink.getComment(0)).describedAs("get first comment").extracting("nick", "comment") - .containsExactly("somebody", "something") + val comment = entryLink.getComment(0) + assertThat(comment, "get first comment").all { + prop(EntryComment::nick).isEqualTo("somebody") + prop(EntryComment::comment).isEqualTo("something") + } + assertThat(entryLink.deleteComment(comment), "delete comment").isTrue() + assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse() } @Test @@ -80,19 +96,19 @@ class EntryLinkTest { val tag = "test" val tags = listOf(SyndCategoryImpl().apply { name = tag }) val link = EntryLink("link", "title", "nick", "channel", Date(), tags) - assertThat(link.tags.size).describedAs("check tag size").isEqualTo(tags.size) - assertThat(link.tags[0].name).describedAs("check tag name").isEqualTo(tag) - assertThat(link.pinboardTags).describedAs("check pinboard tags").isEqualTo("nick,$tag") + assertThat(link.tags.size, "check tag size").isEqualTo(tags.size) + assertThat(link.tags[0].name, "check tag name").isEqualTo(tag) + assertThat(link.pinboardTags, "check pinboard tags").isEqualTo("nick,$tag") } @Test fun testMatches() { - assertThat(entryLink.matches("mobitopia")).describedAs("match mobitopia").isTrue - assertThat(entryLink.matches("skynx")).describedAs("match nick").isTrue - assertThat(entryLink.matches("www.mobitopia.org")).describedAs("match url").isTrue - assertThat(entryLink.matches("foo")).describedAs("match foo").isFalse - assertThat(entryLink.matches("")).describedAs("match empty").isFalse - assertThat(entryLink.matches(null)).describedAs("match null").isFalse + assertThat(entryLink.matches("mobitopia"), "match mobitopia").isTrue() + assertThat(entryLink.matches("skynx"), "match nick").isTrue() + assertThat(entryLink.matches("www.mobitopia.org"), "match url").isTrue() + assertThat(entryLink.matches("foo"), "match foo").isFalse() + assertThat(entryLink.matches("<empty>"), "match empty").isFalse() + assertThat(entryLink.matches(null), "match null").isFalse() } @@ -100,20 +116,19 @@ class EntryLinkTest { fun testTags() { val tags: List<SyndCategory> = entryLink.tags for ((i, tag) in tags.withIndex()) { - assertThat(tag.name).describedAs("tag.getName($i)").isEqualTo("tag" + (i + 1)) + assertThat(tag.name, "tag.getName($i)").isEqualTo("tag" + (i + 1)) } - assertThat(entryLink.tags.size).describedAs("getTags().size() is 5").isEqualTo(5) - assertThat(entryLink.tags).describedAs("hasTags() is true").isNotEmpty + assertThat(entryLink.tags, "size is 5").size().isEqualTo(5) entryLink.setTags("-tag5") entryLink.setTags("+mobitopia") entryLink.setTags("tag4") entryLink.setTags("-mobitopia") - assertThat(entryLink.pinboardTags).describedAs("getPinboardTags()") + assertThat(entryLink.pinboardTags, "getPinboardTags()") .isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia") val size = entryLink.tags.size entryLink.setTags("") - assertThat(entryLink.tags.size).describedAs("empty tag").isEqualTo(size) + assertThat(entryLink.tags.size, "empty tag").isEqualTo(size) entryLink.setTags(" ") - assertThat(entryLink.tags.size).describedAs("blank tag").isEqualTo(size) + assertThat(entryLink.tags.size, "blank tag").isEqualTo(size) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt new file mode 100644 index 0000000..04c36a7 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -0,0 +1,121 @@ +/* + * FeedMgrTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.entries + +import assertk.all +import assertk.assertThat +import assertk.assertions.endsWith +import assertk.assertions.isEqualTo +import assertk.assertions.isTrue +import assertk.assertions.prop +import net.thauvin.erik.mobibot.Utils.today +import org.testng.annotations.BeforeSuite +import org.testng.annotations.Test +import java.nio.file.Paths +import java.util.Date +import kotlin.io.path.absolutePathString +import kotlin.io.path.deleteIfExists +import kotlin.io.path.exists +import kotlin.io.path.fileSize +import kotlin.io.path.name + +class FeedMgrTest { + private val entries = Entries() + private val channel = "mobibot" + + @BeforeSuite(alwaysRun = true) + fun beforeSuite() { + entries.logsDir = "src/test/resources/" + entries.ircServer = "irc.example.com" + entries.channel = channel + entries.backlogs = "https://www.mobitopia.org/mobibot/logs" + } + + @Test + fun testFeedMgr() { + // Load the feed + assertThat(FeedsMgr.loadFeed(entries), "pubDate").isEqualTo("2021-10-31") + + assertThat(entries.links.size, "2 links").isEqualTo(2) + entries.links.forEachIndexed { i, entryLink -> + assertThat(entryLink, "Example $(i + 1)").all { + prop(EntryLink::title).isEqualTo("Example ${i + 1}") + prop(EntryLink::link).isEqualTo("https://www.example.com/${i + 1}") + prop(EntryLink::channel).isEqualTo(channel) + } + entryLink.tags.forEachIndexed { y, tag -> + assertThat(tag.name, "tag${i + 1}-${y + 1}").isEqualTo("tag${i + 1}-${y + 1}") + } + } + + with(entries.links.first()) { + assertThat(nick, "first nick").isEqualTo("ErikT") + assertThat(date, "first date").isEqualTo(Date(1635638400000L)) + comments.forEachIndexed { i, entryComment -> + assertThat(entryComment.comment, "comment ${i + 1}").endsWith("comment ${i + 1}.") + if (i == 0) { + assertThat(entryComment.nick, "comment ${i + 1} nick").isEqualTo("ErikT") + } else { + assertThat(entryComment.nick, "comment ${i + 1} nick").isEqualTo("Skynx") + } + } + } + + assertThat(entries.links[1], "second link").all { + prop(EntryLink::nick).isEqualTo("Skynx") + prop(EntryLink::date).isEqualTo(Date(1635638460000L)) + } + + val currentFile = Paths.get("${entries.logsDir}test.xml") + val backlogFile = Paths.get("${entries.logsDir}${today()}.xml") + + // Save the feed + FeedsMgr.saveFeed(entries, currentFile.name) + + assertThat(currentFile.exists(), "${currentFile.absolutePathString()} exists").isTrue() + assertThat(backlogFile.exists(), "${backlogFile.absolutePathString()} exits").isTrue() + + assertThat(currentFile.fileSize(), "files are identical").isEqualTo(backlogFile.fileSize()) + + // Load the test feed + entries.links.clear() + FeedsMgr.loadFeed(entries, currentFile.name) + + entries.links.forEachIndexed { i, entryLink -> + assertThat(entryLink.title, "${currentFile.name} title ${i + 1}").isEqualTo("Example ${i + 1}") + } + + assertThat(currentFile.deleteIfExists(), "delete ${currentFile.absolutePathString()}").isTrue() + assertThat(backlogFile.deleteIfExists(), "delete ${backlogFile.absolutePathString()}").isTrue() + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index ad0740e..c1b9a4d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -31,11 +31,13 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFailure +import assertk.assertions.isInstanceOf import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test /** @@ -44,11 +46,11 @@ import org.testng.annotations.Test class CalcTest { @Test fun testCalculate() { - assertThat(calculate("1 + 1")).describedAs("calculate(1+1)").isEqualTo("1+1 = %s", bold(2)) - assertThat(calculate("1 -3")).describedAs("calculate(1 -3)").isEqualTo("1-3 = %s", bold(-2)) - assertThat(calculate("pi+π+e+φ")).describedAs("calculate(pi+π+e+φ)") - .isEqualTo("pi+π+e+φ = %s", bold("10.62")) - assertThatThrownBy { calculate("one + one") }.describedAs("calculate(one+one)") - .isInstanceOf(UnknownFunctionOrVariableException::class.java) + assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${bold(2)}") + assertThat(calculate("1 -3"), "calculate(1 -3)").isEqualTo("1-3 = ${bold(-2)}") + assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)") + .isEqualTo("pi+π+e+φ = ${bold("10.62")}") + assertThat { calculate("one + one") } + .isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 8d0c70a..e5f5e52 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -31,8 +31,13 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThan +import assertk.assertions.prop +import net.thauvin.erik.crypto.CryptoPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice -import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -43,11 +48,17 @@ class CryptoPricesTest { @Throws(ModuleException::class) fun testMarketPrice() { var price = currentPrice(listOf("BTC")) - assertThat(price).extracting("base", "currency").containsExactly("BTC", "USD") - assertThat(price.amount.signum()).describedAs("BTC > 0").isGreaterThan(0) + assertThat(price, "BTC in USD").all { + prop(CryptoPrice::base).isEqualTo("BTC") + prop(CryptoPrice::currency).isEqualTo("USD") + prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) + } price = currentPrice(listOf("ETH", "EUR")) - assertThat(price).extracting("base", "currency").containsExactly("ETH", "EUR") - assertThat(price.amount.signum()).describedAs("ETH > 0").isGreaterThan(0) + assertThat(price, "ETH in EUR").all { + prop(CryptoPrice::base).isEqualTo("ETH") + prop(CryptoPrice::currency).isEqualTo("EUR") + prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) + } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 84fa190..962e2c2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -31,10 +31,21 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.all +import assertk.assertThat +import assertk.assertions.any +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isInstanceOf +import assertk.assertions.matches +import assertk.assertions.prop +import assertk.assertions.size import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates -import org.assertj.core.api.Assertions.assertThat +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage import org.testng.annotations.BeforeClass import org.testng.annotations.Test @@ -50,14 +61,28 @@ class CurrencyConverterTest { @Test fun testConvertCurrency() { - assertThat(convertCurrency("100 USD to EUR").msg) - .describedAs("100 USD to EUR").matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}") - assertThat(convertCurrency("100 USD to USD").msg).describedAs("100 USD to USD") - .contains("You're kidding, right?") - assertThat(convertCurrency("100 USD").msg).describedAs("100 USD").contains("Invalid query.") + assertThat( + convertCurrency("100 USD to EUR").msg, + "100 USD to EUR" + ).matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}".toRegex()) + assertThat(convertCurrency("100 USD to USD"), "100 USD to USD").all { + prop(Message::msg).contains("You're kidding, right?") + isInstanceOf(PublicMessage::class.java) + } + assertThat(convertCurrency("100 USD"), "100 USD").all { + prop(Message::msg).contains("Invalid query.") + isInstanceOf(ErrorMessage::class.java) + } + } + + @Test + fun testCurrencyRates() { val rates = currencyRates() - assertThat(rates.size).describedAs("currencyRates.size == 33").isEqualTo(33) - assertThat(rates).describedAs("currencyRates(EUR< USD)").contains("EUR: 1") - .anyMatch { it.matches("USD: .*".toRegex()) } + assertThat(rates).all { + size().isEqualTo(33) + any { it.matches("[A-Z]{3}: +[\\d.]+".toRegex()) } + contains("EUR: 1") + } + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 681dc5b..dfebd9d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -33,14 +33,15 @@ package net.thauvin.erik.mobibot.modules -import org.assertj.core.api.Assertions.assertThat +import assertk.assertThat +import assertk.assertions.isEqualTo import org.testng.annotations.Test class DiceTest { @Test fun testWinLoseOrTie() { - assertThat(Dice.winLoseOrTie(6, 6)).describedAs("6 vs. 6").isEqualTo(Dice.Result.TIE) - assertThat(Dice.winLoseOrTie(6, 5)).describedAs("6 vs. 5").isEqualTo(Dice.Result.WIN) - assertThat(Dice.winLoseOrTie(5, 6)).describedAs("5 vs. 6").isEqualTo(Dice.Result.LOSE) + assertThat(Dice.winLoseOrTie(6, 6), "6 vs. 6").isEqualTo(Dice.Result.TIE) + assertThat(Dice.winLoseOrTie(6, 5), "6 vs. 5").isEqualTo(Dice.Result.WIN) + assertThat(Dice.winLoseOrTie(5, 6), "5 vs. 6").isEqualTo(Dice.Result.LOSE) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 5dfc1f2..fd7b4c7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -31,11 +31,20 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.hasNoCause +import assertk.assertions.isEqualTo +import assertk.assertions.isFailure +import assertk.assertions.isInstanceOf +import assertk.assertions.isNotEmpty +import assertk.assertions.prop +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.Sanitize.sanitizeException import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message import org.testng.annotations.Test /** @@ -49,23 +58,33 @@ class GoogleSearchTest : LocalProperties() { val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) try { var messages = searchGoogle("mobitopia", apiKey, cseKey) - assertThat(messages).describedAs("mobitopia results not empty").isNotEmpty - assertThat(messages[0].msg).describedAs("found mobibtopia").containsIgnoringCase("mobitopia") + assertThat(messages, "mobitopia results not empty").isNotEmpty() + assertThat(messages[0].msg, "found mobibtopia").contains("mobitopia", true) + messages = searchGoogle("aapl", apiKey, cseKey) - assertThat(messages).describedAs("aapl results not empty").isNotEmpty - assertThat(messages[0].msg).describedAs("found apple").containsIgnoringCase("apple") - assertThatThrownBy { searchGoogle("test", "", "apiKey") } - .describedAs("no API key") + assertThat(messages, "aapl results not empty").isNotEmpty() + assertThat(messages[0].msg, "found apple").contains("apple", true) + + messages = searchGoogle("adadflkjl", apiKey, cseKey) + assertThat(messages[0], "not found").all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo("No results found.") + } + + assertThat( + searchGoogle("", "apikey", "cssKey").first(), + "empty query" + ).isInstanceOf(ErrorMessage::class.java) + + assertThat { searchGoogle("test", "", "apiKey") }.isFailure() .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThatThrownBy { searchGoogle("test", "apiKey", "") } - .describedAs("no CSE API key") + + assertThat { searchGoogle("test", "apiKey", "") }.isFailure() .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThatThrownBy { searchGoogle("", "apikey", "apiKey") } - .describedAs("no query").isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI")) { - throw sanitizeException(e, apiKey, cseKey) + throw e.sanitize(apiKey, cseKey) } else { throw e } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 28fd592..865a65f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -31,8 +31,11 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isNotEmpty import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke -import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -42,7 +45,9 @@ class JokeTest { @Test @Throws(ModuleException::class) fun testRandomJoke() { - assertThat(randomJoke().msg).describedAs("randomJoke() > 0").isNotEmpty - assertThat(randomJoke().msg).describedAs("randomJoke()").containsIgnoringCase("chuck") + assertThat(randomJoke().msg, "randomJoke() > 0").all { + isNotEmpty() + contains("chuck", true) + } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 211afdd..b3e3dff 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -31,9 +31,11 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertThat +import assertk.assertions.any +import assertk.assertions.contains import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois -import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -44,14 +46,13 @@ class LookupTest { @Throws(Exception::class) fun testLookup() { val result = nslookup("apple.com") - assertThat(result).describedAs("lookup(apple.com)").contains("17.253.144.10") + assertThat(result, "lookup(apple.com)").contains("17.253.144.10") } @Test @Throws(Exception::class) fun testWhois() { val result = whois("17.178.96.59", Lookup.WHOIS_HOST) - assertThat(result).describedAs("whois(17.178.96.59/Apple Inc.") - .anyMatch { it.contains("Apple Inc.") } + assertThat(result, "whois(17.178.96.59/Apple Inc.").any { it.contains("Apple Inc.") } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 4ad980b..d776f2c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -31,8 +31,16 @@ */ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Sanitize.sanitizeException -import org.assertj.core.api.Assertions.assertThat +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.doesNotContain +import assertk.assertions.endsWith +import assertk.assertions.hasMessage +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import assertk.assertions.isNull +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import org.testng.annotations.DataProvider import org.testng.annotations.Test import java.io.IOException @@ -58,37 +66,41 @@ class ModuleExceptionTest { @Test(dataProvider = "dp") fun testGetDebugMessage(e: ModuleException) { - assertThat(e.debugMessage).describedAs("get debug message").isEqualTo(debugMessage) + assertThat(e.debugMessage, "get debug message").isEqualTo(debugMessage) } @Test(dataProvider = "dp") fun testGetMessage(e: ModuleException) { - assertThat(e).describedAs("get message").hasMessage(message) + assertThat(e, "get message").hasMessage(message) } @Test fun testSanitizeMessage() { val apiKey = "1234567890" var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) - assertThat(sanitizeException(e, apiKey, "", "me")).describedAs("sanitized url") - .hasMessageContainingAll("xxxxxxxxxx", "userID=xx", "java.io.IOException") - .hasMessageNotContainingAny(apiKey, "me") + assertThat(e.sanitize(apiKey, "", "me").message, "sanitized url").isNotNull().all { + contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") + doesNotContain(apiKey, "me") + } e = ModuleException(debugMessage, message, null) - assertThat(sanitizeException(e, apiKey)).describedAs("no cause").hasMessage(message) + assertThat(e.sanitize(apiKey), "no cause").hasMessage(message) e = ModuleException(debugMessage, message, IOException()) - assertThat(sanitizeException(e, apiKey)).describedAs("no cause message").hasMessage(message) + assertThat(e.sanitize(apiKey), "no cause message").hasMessage(message) e = ModuleException(apiKey) - assertThat(sanitizeException(e, apiKey)).describedAs("api key in message").hasMessageNotContaining(apiKey) + assertThat(e.sanitize(apiKey).message, "api key in message").isNotNull().doesNotContain(apiKey) val msg: String? = null e = ModuleException(debugMessage, msg, IOException(msg)) - assertThat(sanitizeException(e, apiKey).message).describedAs("null message").isNull() + assertThat(e.sanitize(apiKey).message, "null message").isNull() e = ModuleException(msg, msg, IOException("foo is $apiKey")) - assertThat(sanitizeException(e, " ", apiKey, "foo").message).describedAs("key in cause") - .doesNotContain(apiKey).endsWith("xxx is xxxxxxxxxx") + assertThat(e.sanitize(" ", apiKey, "foo").message, "key in cause").isNotNull().all { + doesNotContain(apiKey) + endsWith("xxx is xxxxxxxxxx") + } + assertThat(e.sanitize(), "empty").isEqualTo(e) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index e79fbcc..34cbfbb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -31,8 +31,10 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isNotEmpty import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing -import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test /** @@ -41,13 +43,13 @@ import org.testng.annotations.Test class PingTest { @Test fun testPingsArray() { - assertThat(Ping.PINGS).describedAs("Pings array is not empty.").isNotEmpty + assertThat(Ping.PINGS, "Pings array is not empty.").isNotEmpty() } @Test fun testRandomPing() { for (i in 0..9) { - assertThat(randomPing()).describedAs("Random ping $i").isIn(Ping.PINGS) + assertThat(Ping.PINGS, "Random ping $i").contains(randomPing()) } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 3f5352f..499c28c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -32,23 +32,19 @@ package net.thauvin.erik.mobibot.modules -import org.assertj.core.api.Assertions.assertThat +import assertk.assertThat +import assertk.assertions.isEqualTo import org.testng.annotations.Test class RockPaperScissorsTest { @Test fun testWinLoseOrDraw() { - assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper")).describedAs("scissors vs. paper") - .isEqualTo("win") - assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock")).describedAs("paper vs. rock").isEqualTo("win") - assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors")).describedAs("rock vs. scissors") - .isEqualTo("win") - assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors")).describedAs("paper vs. scissors") - .isEqualTo("lose") - assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper")).describedAs("rock vs. paper").isEqualTo("lose") - assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock")).describedAs("scissors vs. rock") - .isEqualTo("lose") - assertThat(RockPaperScissors.winLoseOrDraw("scissors", "scissors")) - .describedAs("scissors vs. scissors").isEqualTo("draw") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win") + assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose") + assertThat(RockPaperScissors.winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 4eb4081..8812c21 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -31,11 +31,20 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.all +import assertk.assertThat +import assertk.assertions.hasNoCause +import assertk.assertions.isEqualTo +import assertk.assertions.isFailure +import assertk.assertions.isInstanceOf +import assertk.assertions.isNotEmpty +import assertk.assertions.matches +import assertk.assertions.prop +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.Sanitize.sanitizeException import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message import org.testng.annotations.Test /** @@ -52,24 +61,25 @@ class StockQuoteTest : LocalProperties() { val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP) try { val messages = getQuote("apple inc", apiKey) - assertThat(messages).describedAs("response not empty").isNotEmpty - assertThat(messages[0].msg).describedAs("same stock symbol").matches("Symbol: AAPL .*") - assertThat(messages[1].msg).describedAs("price label").matches(buildMatch("Price")) - assertThat(messages[2].msg).describedAs("previous label").matches(buildMatch("Previous")) - assertThat(messages[3].msg).describedAs("open label").matches(buildMatch("Open")) - try { - getQuote("blahfoo", apiKey) - } catch (e: ModuleException) { - assertThat(e.message).describedAs("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL) + assertThat(messages, "response not empty").isNotEmpty() + assertThat(messages[0].msg, "same stock symbol").matches("Symbol: AAPL .*".toRegex()) + assertThat(messages[1].msg, "price label").matches(buildMatch("Price").toRegex()) + assertThat(messages[2].msg, "previous label").matches(buildMatch("Previous").toRegex()) + assertThat(messages[3].msg, "open label").matches(buildMatch("Open").toRegex()) + + assertThat(getQuote("blahfoo", apiKey).first(), "invalid symbol").all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) } - assertThatThrownBy { getQuote("test", "") }.describedAs("no API key") - .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThatThrownBy { getQuote("", "apikey") }.describedAs("no symbol") - .isInstanceOf(ModuleException::class.java).hasNoCause() + assertThat(getQuote("", "apikey").first(), "empty symbol").all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) + } + assertThat { getQuote("test", "") }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI")) { - throw sanitizeException(e, apiKey) + throw e.sanitize(apiKey) } else { throw e } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 2257ac4..379e15a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -31,9 +31,11 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isSuccess import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.Twitter.Companion.twitterPost -import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test import java.net.InetAddress import java.net.UnknownHostException @@ -56,7 +58,7 @@ class TwitterTest : LocalProperties() { @Throws(ModuleException::class) fun testPostTwitter() { val msg = "Testing Twitter API from $ci" - assertThat( + assertThat { twitterPost( getProperty(Twitter.CONSUMER_KEY_PROP), getProperty(Twitter.CONSUMER_SECRET_PROP), @@ -65,7 +67,7 @@ class TwitterTest : LocalProperties() { getProperty(Twitter.HANDLE_PROP), msg, true - ).msg - ).describedAs("twitterPost($msg)").isEqualTo(msg) + ) + }.isSuccess().isEqualTo(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 1c503ca..a96e007 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -31,6 +31,16 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.endsWith +import assertk.assertions.hasNoCause +import assertk.assertions.isEqualTo +import assertk.assertions.isFailure +import assertk.assertions.isInstanceOf +import assertk.assertions.isNotNull +import assertk.assertions.isTrue import net.aksingh.owmjapis.api.APIException import net.aksingh.owmjapis.core.OWM import net.thauvin.erik.mobibot.LocalProperties @@ -39,8 +49,6 @@ import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy import org.testng.annotations.Test import kotlin.random.Random @@ -51,50 +59,63 @@ class Weather2Test : LocalProperties() { @Test fun testFtoC() { val t = ftoC(32.0) - assertThat(t.second).describedAs("32 °F is 0 °C").isEqualTo(0) + assertThat(t.second, "32 °F is 0 °C").isEqualTo(0) } @Test fun testGetCountry() { - assertThat(getCountry("foo")).describedAs("not a country").isEqualTo(OWM.Country.UNITED_STATES) - assertThat(getCountry("fr")).describedAs("fr is france").isEqualTo(OWM.Country.FRANCE) + assertThat(getCountry("foo"), "not a country").isEqualTo(OWM.Country.UNITED_STATES) + assertThat(getCountry("fr"), "fr is france").isEqualTo(OWM.Country.FRANCE) val country = OWM.Country.values() repeat(3) { val rand = country[Random.nextInt(0, country.size - 1)] - assertThat(getCountry(rand.value)).describedAs(rand.name).isEqualTo(rand) + assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) } } @Test fun testMphToKmh() { val w = mphToKmh(0.62) - assertThat(w.second).describedAs("0.62 mph is 1 km/h").isEqualTo(1) + assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1) } @Test @Throws(ModuleException::class) fun testWeather() { var messages = getWeather("98204", getProperty(OWM_API_KEY_PROP)) - assertThat(messages[0].msg).describedAs("is Everett").contains("Everett, United States").contains("US") - assertThat(messages[messages.size - 1].msg).describedAs("is Everett zip code").endsWith("98204%2CUS") + assertThat(messages[0].msg, "is Everett").all { + contains("Everett, United States") + contains("US") + } + assertThat(messages[messages.size - 1].msg, "is Everett zip code").endsWith("98204%2CUS") messages = getWeather("San Francisco", getProperty(OWM_API_KEY_PROP)) - assertThat(messages[0].msg).describedAs("is San Francisco").contains("San Francisco").contains("US") - assertThat(messages[messages.size - 1].msg).describedAs("is San Fran city code").endsWith("5391959") + assertThat(messages[0].msg, "is San Francisco").all { + contains("San Francisco") + contains("US") + } + assertThat(messages[messages.size - 1].msg, "is San Fran city code").endsWith("5391959") messages = getWeather("London, GB", getProperty(OWM_API_KEY_PROP)) - assertThat(messages[0].msg).describedAs("is UK").contains("London, United Kingdom").contains("GB") - assertThat(messages[messages.size - 1].msg).describedAs("is London city code").endsWith("2643743") + assertThat(messages[0].msg, "is UK").all { + contains("London, United Kingdom") + contains("GB") + } + assertThat(messages[messages.size - 1].msg, "is London city code").endsWith("2643743") - assertThatThrownBy { getWeather("Foo, US", getProperty(OWM_API_KEY_PROP)) } - .describedAs("foo not found").hasCauseInstanceOf(APIException::class.java) - assertThatThrownBy { getWeather("test", "") } - .describedAs("no API key").isInstanceOf(ModuleException::class.java).hasNoCause() - assertThatThrownBy { getWeather("test", null) } - .describedAs("null API key").isInstanceOf(ModuleException::class.java).hasNoCause() + try { + getWeather("Foo, US", getProperty(OWM_API_KEY_PROP)) + } catch (e: ModuleException) { + assertThat(e.cause, "cause is API exception").isNotNull().isInstanceOf(APIException::class.java) + } + + assertThat { getWeather("test", "") }.isFailure() + .isInstanceOf(ModuleException::class.java).hasNoCause() + assertThat { getWeather("test", null) }.isFailure() + .isInstanceOf(ModuleException::class.java).hasNoCause() messages = getWeather("", "apikey") - assertThat(messages[0].isError).describedAs("no query").isTrue + assertThat(messages[0].isError, "no query").isTrue() } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 49e63c1..ddc6e8f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -31,14 +31,18 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertThat +import assertk.assertions.endsWith +import assertk.assertions.isSuccess +import assertk.assertions.matches +import assertk.assertions.startsWith import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time -import org.assertj.core.api.Assertions.assertThat +import org.pircbotx.Colors import org.testng.annotations.Test import java.time.ZoneId -import java.time.zone.ZoneRulesException /** * The `WordTimeTest` class. @@ -46,16 +50,23 @@ import java.time.zone.ZoneRulesException class WordTimeTest { @Test fun testTime() { - assertThat(time("PST").msg).describedAs("PST").endsWith(bold("Los Angeles")) - assertThat(time("BLAH").isError).describedAs("BLAH").isTrue - assertThat(time("BEAT").msg).describedAs(BEATS_KEYWORD).matches("[\\w ]+: .?@\\d{3}+.? .beats") + assertThat(time(), "no zone").matches( + ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + + "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + + "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() + ) + assertThat(time(""), "empty zone").endsWith(bold("Los Angeles")) + assertThat(time("PST"), "PST").endsWith(bold("Los Angeles")) + assertThat(time("GB"), "GB").endsWith(bold("London")) + assertThat(time("FR"), "FR").endsWith(bold("Paris")) + assertThat(time("BLAH"), "BLAH").startsWith("Unsupported") + assertThat(time("BEAT"), BEATS_KEYWORD).matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) } @Test - @Throws(ZoneRulesException::class) - fun testCountries() { + fun testZones() { COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { - ZoneId.of(it.value) + assertThat { ZoneId.of(it.value) }.isSuccess() } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt index 6ab62c9..94a2de0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt @@ -32,7 +32,8 @@ package net.thauvin.erik.mobibot.msg -import org.assertj.core.api.Assertions.assertThat +import assertk.assertThat +import assertk.assertions.isTrue import org.testng.annotations.Test class TestMessage { @@ -41,15 +42,15 @@ class TestMessage { var msg = Message() msg.isError = true - assertThat(msg.isNotice).describedAs("message is notice").isTrue + assertThat(msg.isNotice, "message is notice").isTrue() msg = Message("foo", isError = true) - assertThat(msg.isNotice).describedAs("message is notice too").isTrue + assertThat(msg.isNotice, "message is notice too").isTrue() } @Test fun testErrorMessage() { val msg = ErrorMessage("foo") - assertThat(msg.isNotice).describedAs("error message is notice").isTrue + assertThat(msg.isNotice, "error message is notice").isTrue() } } diff --git a/src/test/resources/current.xml b/src/test/resources/current.xml new file mode 100644 index 0000000..8552a9a --- /dev/null +++ b/src/test/resources/current.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> + <channel> + <title>#mobibot IRC Links + https://www.mobitopia.org/mobibot/logs + Links from irc.example.com on #mobibot + en + Sun, 31 Oct 2021 21:45:11 GMT + 2021-10-31T21:45:11Z + en + + Example 2 + https://www.example.com/2 + Posted by <b>Skynx</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> + tag2-1 + tag2-2 + Sun, 31 Oct 2021 21:45:11 GMT + https://www.foo.com + mobibot@irc.libera.chat (Skynx) + 2021-10-31T00:01:00Z + + + Example 1 + https://www.example.com/1 + Posted by <b>ErikT</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> + <br/><br/>ErikT: This is comment 1. <br/>Skynx: This is comment 2. + + tag1-1 + tag1-2 + Sun, 31 Oct 2021 21:43:15 GMT + https://www.example.com/ + mobibot@irc.libera.chat (ErikT) + 2021-10-31T00:00:00Z + + + diff --git a/version.properties b/version.properties index 772b346..f5f7c7c 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Sep 15 17:27:25 PDT 2021 -version.buildmeta=1400 +#Mon Nov 08 13:50:12 PST 2021 +version.buildmeta=2220 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+1400 +version.semver=0.8.0-beta+2220 From f53f43082a708deac6ec33f07b5f5f28201be445 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 9 Nov 2021 10:00:35 -0800 Subject: [PATCH 572/842] Fixed incorrect bot nickname. --- src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index b063549..f66ba2b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.commands.tell import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp @@ -280,7 +281,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { helpFormat( buildCmdSyntax( "%c $name $TELL_DEL_KEYWORD ", - event.user.nick, + event.bot().nick, true ) ) From 5dd8b36f92ed152c090f934e83ef82de4053c8f2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 9 Nov 2021 10:03:15 -0800 Subject: [PATCH 573/842] Bumped CIs to Java 11 & 17. --- .circleci/config.yml | 6 +++--- .github/workflows/gradle.yml | 2 +- .gitlab-ci.yml | 2 +- .idea/mobibot.iml | 7 ------- bitbucket-pipelines.yml | 2 +- version.properties | 6 +++--- 6 files changed, 9 insertions(+), 16 deletions(-) delete mode 100644 .idea/mobibot.iml diff --git a/.circleci/config.yml b/.circleci/config.yml index a7df501..592890a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,11 +31,11 @@ defaults_gradle: &defaults_gradle path: build/reports/ jobs: - build_gradle_jdk15: + build_gradle_jdk17: <<: *defaults docker: - - image: cimg/openjdk:15.0 + - image: cimg/openjdk:17.0 <<: *defaults_gradle @@ -52,4 +52,4 @@ workflows: gradle: jobs: - build_gradle_jdk11 - - build_gradle_jdk15 + - build_gradle_jdk17 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 1bb63d7..c814746 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - java-version: [ 11, 15 ] + java-version: [ 11, 17 ] steps: - uses: actions/checkout@v2 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c415a8c..67f2bb4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: gradle:7-jdk11 +image: gradle:7-jdk17 variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" diff --git a/.idea/mobibot.iml b/.idea/mobibot.iml deleted file mode 100644 index b03a311..0000000 --- a/.idea/mobibot.iml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 219c29f..7bd4e82 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,4 +1,4 @@ -image: openjdk:11 +image: openjdk:17 pipelines: default: diff --git a/version.properties b/version.properties index f5f7c7c..c002730 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Nov 08 13:50:12 PST 2021 -version.buildmeta=2220 +#Tue Nov 09 08:47:23 PST 2021 +version.buildmeta=2227 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+2220 +version.semver=0.8.0-beta+2227 From 0a6b84fa2bdf360aad29b50c959cf75cf301fa84 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 12 Nov 2021 09:19:14 -0800 Subject: [PATCH 574/842] Switched to random extension function. --- src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt | 3 +-- src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt | 3 +-- .../net/thauvin/erik/mobibot/modules/RockPaperScissors.kt | 3 +-- .../kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 4b7257e..977a5b6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -35,7 +35,6 @@ import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat import org.pircbotx.hooks.types.GenericMessageEvent -import kotlin.random.Random /** * The Dice module. @@ -67,7 +66,7 @@ class Dice : AbstractModule() { } private fun roll(): Pair { - return Random.nextInt(1, DICE_FACES.size) to Random.nextInt(1, DICE_FACES.size) + return (1..DICE_FACES.size).random() to (1..DICE_FACES.size).random() } companion object { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 9d49118..3be959e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat import org.pircbotx.hooks.types.GenericMessageEvent -import kotlin.random.Random /** * The Ping module. @@ -69,7 +68,7 @@ class Ping : AbstractModule() { @JvmStatic fun randomPing(): String { - return PINGS[Random.nextInt(PINGS.size)] + return PINGS[PINGS.indices.random()] } /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 20c5c29..9dba554 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -36,7 +36,6 @@ import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.helpFormat import org.pircbotx.hooks.types.GenericMessageEvent -import kotlin.random.Random /** @@ -104,7 +103,7 @@ class RockPaperScissors : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val hand = Hands.valueOf(cmd.uppercase()) - val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)] + val botHand = Hands.values()[(0..Hands.values().size).random()] with(event.bot()) { sendIRC().message(channel, "${hand.emoji} vs. ${botHand.emoji}") when { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index a96e007..27b95c2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -50,7 +50,6 @@ import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh import org.testng.annotations.Test -import kotlin.random.Random /** * The `Weather2Test` class. @@ -69,7 +68,7 @@ class Weather2Test : LocalProperties() { val country = OWM.Country.values() repeat(3) { - val rand = country[Random.nextInt(0, country.size - 1)] + val rand = country[(country.indices).random()] assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) } } From 8efb09cea871c62b3b142407ccc09396370fcf64 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 12 Nov 2021 09:19:58 -0800 Subject: [PATCH 575/842] Cleaned up tests. --- .../kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 8 ++++++-- .../kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt | 6 ++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index dadf542..738a658 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -51,7 +51,7 @@ class FeedReaderTest { fun readFeedTest() { var messages = readFeed("https://feeds.thauvin.net/ethauvin") assertThat(messages.size, "size = 10").isEqualTo(10) - assertThat(messages[1].msg, "feed entry url").contains("ethauvin") + assertThat(messages[1].msg, "feed entry url").contains("erik.thauvin.net") messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") assertThat(messages[0].msg, "nothing to view").contains("nothing") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index fc8f117..876bea0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -31,8 +31,10 @@ */ package net.thauvin.erik.mobibot +import assertk.all import assertk.assertThat import assertk.assertions.isEqualTo +import assertk.assertions.length import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.buildCmdSyntax @@ -187,8 +189,10 @@ class UtilsTest { @Test fun testObfuscate() { - assertThat(ascii.obfuscate().length, "obfuscate is right length").isEqualTo(ascii.length) - assertThat(ascii.obfuscate(), "obfuscate()").isEqualTo("x".repeat(ascii.length)) + assertThat(ascii.obfuscate(), "obfuscate()").all { + length().isEqualTo(ascii.length) + isEqualTo(("x".repeat(ascii.length))) + } assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ") } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index c1b9a4d..80a49b5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -48,9 +48,7 @@ class CalcTest { fun testCalculate() { assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${bold(2)}") assertThat(calculate("1 -3"), "calculate(1 -3)").isEqualTo("1-3 = ${bold(-2)}") - assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)") - .isEqualTo("pi+π+e+φ = ${bold("10.62")}") - assertThat { calculate("one + one") } - .isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java) + assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${bold("10.62")}") + assertThat { calculate("one + one") }.isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java) } } From f1b50d741c4ea4b9b40192abc5df9afa6a0ef789 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 14 Nov 2021 15:37:26 -0800 Subject: [PATCH 576/842] Cleaned up messages classes. --- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 9 +--- .../net/thauvin/erik/mobibot/msg/Message.kt | 50 ++++++------------- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 10 ++-- .../erik/mobibot/msg/PrivateMessage.kt | 10 +--- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 7 +-- .../thauvin/erik/mobibot/msg/TestMessage.kt | 31 +++++++++++- 6 files changed, 51 insertions(+), 66 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 35409f7..f071918 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -34,10 +34,5 @@ package net.thauvin.erik.mobibot.msg /** * The `ErrorMessage` class. */ -class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { - init { - this.msg = msg - this.color = color - isError = true - } -} +class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isError = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index b410663..471c310 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -36,53 +36,31 @@ import net.thauvin.erik.semver.Constants /** * The `Message` class. */ -open class Message { +open class Message @JvmOverloads constructor( + var msg: String, + var color: String = DEFAULT_COLOR, + var isNotice: Boolean = false, + isError: Boolean = false, + var isPrivate: Boolean = false +) { companion object { var DEFAULT_COLOR = Constants.EMPTY + } - /** Message color. */ - var color = DEFAULT_COLOR + init { + if (isError) { + isNotice = true + } + } /** Error flag. */ - var isError = false + var isError = isError set(value) { if (value) isNotice = value field = value } - /** Notice flag. */ - var isNotice = false - - /** Private flag. */ - var isPrivate = false - - /** Message text. */ - var msg = "" - - /** Creates a new message. */ - constructor() { - // This constructor is intentionally empty - } - - /** - * Creates a new message. - */ - @JvmOverloads - constructor( - msg: String, - color: String = DEFAULT_COLOR, - isNotice: Boolean = false, - isError: Boolean = false, - isPrivate: Boolean = false - ) { - this.msg = msg - this.color = color - this.isNotice = isNotice - this.isError = isError - this.isPrivate = isPrivate - } - override fun toString(): String { return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, msg='$msg')" } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index c9fa9dc..d2353b8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -34,10 +34,6 @@ package net.thauvin.erik.mobibot.msg /** * The `NoticeMessage` class. */ -class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { - init { - this.msg = msg - this.color = color - isNotice = true - } -} +class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isNotice = true) + diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 2953b7a..7515eae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -34,11 +34,5 @@ package net.thauvin.erik.mobibot.msg /** * The `PrivateMessage` class. */ -@Suppress("unused") -class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { - init { - this.msg = msg - this.color = color - isPrivate = true - } -} +class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isPrivate = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 397b3cf..9df2770 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -34,9 +34,4 @@ package net.thauvin.erik.mobibot.msg /** * The `PublicMessage` class. */ -class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() { - init { - this.msg = msg - this.color = color - } -} +class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt index 94a2de0..3d588c5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt @@ -32,14 +32,17 @@ package net.thauvin.erik.mobibot.msg +import assertk.all import assertk.assertThat +import assertk.assertions.isFalse import assertk.assertions.isTrue +import assertk.assertions.prop import org.testng.annotations.Test class TestMessage { @Test fun testConstructor() { - var msg = Message() + var msg = Message("foo") msg.isError = true assertThat(msg.isNotice, "message is notice").isTrue() @@ -51,6 +54,30 @@ class TestMessage { @Test fun testErrorMessage() { val msg = ErrorMessage("foo") - assertThat(msg.isNotice, "error message is notice").isTrue() + assertThat(msg).all { + prop(Message::isError).isTrue() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testNoticeMessage() { + val msg = NoticeMessage("food") + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testPublicMessage() { + val msg = PublicMessage("foo") + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isFalse() + prop(Message::isPrivate).isFalse() + } } } From 29b8888eca1b4729eadcc554fdc8b2b43908926f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 14 Nov 2021 15:38:38 -0800 Subject: [PATCH 577/842] Avoid if-null Checks. --- .../thauvin/erik/mobibot/modules/Weather2.kt | 53 ++++++++++--------- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 1b2c525..246c52a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -144,31 +144,34 @@ class Weather2 : ThreadedModule() { country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" ) ) - with(cwd.mainData) { - if (this != null) { + cwd.mainData?.let { + with(it) { if (hasTemp()) { val t = ftoC(temp) messages.add(PublicMessage("Temperature: ${t.first}°F, ${t.second}°C")) } - if (hasHumidity() && humidity != null) { - messages.add(NoticeMessage("Humidity: ${(humidity!!).roundToInt()}%")) + if (hasHumidity()) { + humidity?.let { h -> + messages.add(NoticeMessage("Humidity: ${h.roundToInt()}%")) + } } } } if (cwd.hasWindData()) { - with(cwd.windData) { - if (this != null && hasSpeed() && speed != null) { - val w = mphToKmh(speed!!) - messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h")) + cwd.windData?.let { + if (it.hasSpeed()) { + it.speed?.let { s -> + val w = mphToKmh(s) + messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h")) + } } } } if (cwd.hasWeatherList()) { val condition = StringBuilder("Condition:") - val list = cwd.weatherList - if (list != null) { - for (w in list) { - if (w != null) { + cwd.weatherList?.let { + for (w in it) { + w?.let { condition.append(' ') .append(w.getDescription().capitalise()) .append('.') @@ -177,19 +180,21 @@ class Weather2 : ThreadedModule() { messages.add(NoticeMessage(condition.toString())) } } - if (cwd.hasCityId() && cwd.cityId != null) { - if (cwd.cityId!! > 0) { - messages.add( - NoticeMessage("https://openweathermap.org/city/${cwd.cityId}", Colors.GREEN) - ) - } else { - messages.add( - NoticeMessage( - "https://openweathermap.org/find?q=" - + encodeUrl("$city,${code.uppercase()}"), - Colors.GREEN + if (cwd.hasCityId()) { + cwd.cityId?.let { + if (it > 0) { + messages.add( + NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) ) - ) + } else { + messages.add( + NoticeMessage( + "https://openweathermap.org/find?q=" + + encodeUrl("$city,${code.uppercase()}"), + Colors.GREEN + ) + ) + } } } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 70434a4..b764ab6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -61,7 +61,7 @@ open class LocalProperties { localProps.getProperty(key) } else { val env = System.getenv(keyToEnv(key)) - if (env != null) { + env?.let { localProps.setProperty(key, env) } env From 9b8caaa4a8472cbb129d2d405171b16fd8388161 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 14 Nov 2021 15:40:59 -0800 Subject: [PATCH 578/842] Updated to Gradle 7.3. Bumped dependencies. --- build.gradle | 3 +++ gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 020006e..b6edcf7 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,10 @@ dependencies { implementation 'com.rometools:rome:1.16.0' implementation 'com.squareup.okhttp3:okhttp:4.9.2' + implementation 'net.aksingh:owm-japis:2.5.3.0' + implementation 'com.google.code.gson:gson:2.8.9' + implementation 'net.objecthunter:exp4j:0.4.8' implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..e750102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 29eae2e7ee79ecb8aa62233de1d1acf45fb5e1ac Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 14 Nov 2021 15:41:38 -0800 Subject: [PATCH 579/842] Fixed unknown host test. --- src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 738a658..b828d68 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -67,7 +67,7 @@ class FeedReaderTest { assertThat { readFeed("https://www.examples.com/foo") }.isFailure() .isInstanceOf(FileNotFoundException::class.java) - assertThat { readFeed("https://www.doesnotexists.com") }.isFailure() + assertThat { readFeed("https://www.examplesfoo.com/") }.isFailure() .isInstanceOf(UnknownHostException::class.java) } } From 3ea0e4b3ac69e17e3c759359607e47a4bd1e56dd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 14 Nov 2021 21:56:06 -0800 Subject: [PATCH 580/842] Reworked ModuleException. --- config/detekt/baseline.xml | 1 + .../erik/mobibot/modules/CurrencyConverter.kt | 4 +-- .../erik/mobibot/modules/GoogleSearch.kt | 17 +++++++--- .../net/thauvin/erik/mobibot/modules/Joke.kt | 8 +++-- .../erik/mobibot/modules/ModuleException.kt | 32 +++---------------- .../erik/mobibot/modules/StockQuote.kt | 9 ++++-- .../thauvin/erik/mobibot/modules/Twitter.kt | 4 ++- .../thauvin/erik/mobibot/modules/Weather2.kt | 21 +++++++++--- .../erik/mobibot/msg/PrivateMessage.kt | 1 + .../mobibot/modules/ModuleExceptionTest.kt | 4 +-- version.properties | 6 ++-- 11 files changed, 58 insertions(+), 49 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 9bc0b96..3bcc3cb 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -35,6 +35,7 @@ MagicNumber:View.kt$View$6 MagicNumber:Weather2.kt$Weather2.Companion$1.60934 MagicNumber:Weather2.kt$Weather2.Companion$32 + MagicNumber:Weather2.kt$Weather2.Companion$404 MagicNumber:Weather2.kt$Weather2.Companion$5 MagicNumber:Weather2.kt$Weather2.Companion$9 MagicNumber:WorldTime.kt$WorldTime$14 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index cd4bb67..ebaadb8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -210,13 +210,13 @@ class CurrencyConverter : ThreadedModule() { EXCHANGE_RATES["EUR"] = "1" } catch (e: JDOMException) { throw ModuleException( - e.message, + "loadRates(): JDOM", "An JDOM parsing error has occurred while parsing the exchange rates table.", e ) } catch (e: IOException) { throw ModuleException( - e.message, + "loadRates(): IOE", "An IO error has occurred while parsing the exchange rates table.", e ) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 9571556..efe52ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -70,7 +70,9 @@ class GoogleSearch : ThreadedModule() { } } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - event.sendMessage(e.message!!) + e.message?.let { + event.sendMessage(it) + } } } else { helpResponse(event) @@ -94,7 +96,10 @@ class GoogleSearch : ThreadedModule() { @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { - throw ModuleException("${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing.") + throw ModuleException( + "${GoogleSearch::class.java.name} is disabled.", + "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." + ) } val results = mutableListOf() if (query.isNotBlank()) { @@ -115,9 +120,13 @@ class GoogleSearch : ThreadedModule() { results.add(ErrorMessage("No results found.", Colors.RED)) } } catch (e: IOException) { - throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e) + throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) } catch (e: JSONException) { - throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e) + throw ModuleException( + "searchGoogle($query): JSON", + "A JSON error has occurred searching Google.", + e + ) } } else { results.add(ErrorMessage("Invalid query. Please try again.")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 2d5af61..3ba5f00 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -69,7 +69,9 @@ class Joke : ThreadedModule() { sendIRC().notice(channel, cyan(randomJoke().msg)) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - event.sendMessage(e.message!!) + e.message?.let { + event.sendMessage(it) + } } } } @@ -96,9 +98,9 @@ class Joke : ThreadedModule() { .replace("\\\"", "\"") ) } catch (e: IOException) { - throw ModuleException("randomJoke()", "An IO error has occurred retrieving a random joke.", e) + throw ModuleException("randomJoke(): IOE", "An IO error has occurred retrieving a random joke.", e) } catch (e: JSONException) { - throw ModuleException("randomJoke()", "An JSON error has occurred retrieving a random joke.", e) + throw ModuleException("randomJoke(): JSON", "An JSON error has occurred retrieving a random joke.", e) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 4c6867a..cf06b4e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -34,33 +34,11 @@ package net.thauvin.erik.mobibot.modules /** * The `ModuleException` class. */ -class ModuleException : Exception { - /** - * Returns the debug message. - */ - val debugMessage: String? - - /** - * Creates a new exception. - */ - constructor(message: String?) : super(message) { - debugMessage = message - } - - /** - * Creates a new exception. - */ - constructor(debugMessage: String?, message: String?, cause: Throwable?) : super(message, cause) { - this.debugMessage = debugMessage - } - - /** - * Creates a new exception. - */ - constructor(debugMessage: String?, message: String?) : super(message) { - this.debugMessage = debugMessage - } - +class ModuleException @JvmOverloads constructor( + val debugMessage: String, + message: String? = null, + cause: Throwable? = null +) : Exception(message, cause) { companion object { private const val serialVersionUID = 1L } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 230e2af..a1f17c4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -67,7 +67,9 @@ class StockQuote : ThreadedModule() { } } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - event.sendMessage(e.message!!) + e.message?.let { + event.sendMessage(it) + } } } else { helpResponse(event) @@ -129,6 +131,7 @@ class StockQuote : ThreadedModule() { fun getQuote(symbol: String, apiKey: String?): List { if (apiKey.isNullOrBlank()) { throw ModuleException( + "${StockQuote::class.java.name} is disabled.", "${STOCK_CMD.capitalise()} is disabled. The API key is missing." ) } @@ -216,9 +219,9 @@ class StockQuote : ThreadedModule() { } } } catch (e: IOException) { - throw ModuleException(debugMessage, "An IO error has occurred retrieving a stock quote.", e) + throw ModuleException("$debugMessage: IOE", "An IO error has occurred retrieving a stock quote.", e) } catch (e: NullPointerException) { - throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e) + throw ModuleException("$debugMessage: NPE", "An error has occurred retrieving a stock quote.", e) } } else { messages.add(ErrorMessage(INVALID_SYMBOL)) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index f6020e3..c813392 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -167,7 +167,9 @@ class Twitter : ThreadedModule() { event.respond(post(event.user.nick, "$args (by ${event.user.nick} on $channel)", false)) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - event.respond(e.message) + e.message?.let { + event.respond(it) + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 246c52a..273c374 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -73,7 +73,9 @@ class Weather2 : ThreadedModule() { } } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - event.respond(e.message) + e.message?.let { + event.respond(it) + } } } else { helpResponse(event) @@ -116,7 +118,10 @@ class Weather2 : ThreadedModule() { @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List { if (apiKey.isNullOrBlank()) { - throw ModuleException("${WEATHER_CMD.capitalise()} is disabled. The API key is missing.") + throw ModuleException( + "${Weather2::class.java.name} is disabled.", + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." + ) } val owm = OWM(apiKey) val messages = mutableListOf() @@ -199,9 +204,17 @@ class Weather2 : ThreadedModule() { } } } catch (e: APIException) { - throw ModuleException("getWeather($query)", e.message, e) + if (e.code == 404) { + throw ModuleException( + "getWeather($query): API ${e.code}", + "The requested city was not found.", + e + ) + } else { + throw ModuleException("getWeather($query): API ${e.code}", e.message, e) + } } catch (e: NullPointerException) { - throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e) + throw ModuleException("getWeather($query): NPE", "Unable to perform weather lookup.", e) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 7515eae..6edcb96 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -34,5 +34,6 @@ package net.thauvin.erik.mobibot.msg /** * The `PrivateMessage` class. */ +@Suppress("unused") class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color, isPrivate = true) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index d776f2c..5791867 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -89,14 +89,14 @@ class ModuleExceptionTest { e = ModuleException(debugMessage, message, IOException()) assertThat(e.sanitize(apiKey), "no cause message").hasMessage(message) - e = ModuleException(apiKey) + e = ModuleException(debugMessage, apiKey) assertThat(e.sanitize(apiKey).message, "api key in message").isNotNull().doesNotContain(apiKey) val msg: String? = null e = ModuleException(debugMessage, msg, IOException(msg)) assertThat(e.sanitize(apiKey).message, "null message").isNull() - e = ModuleException(msg, msg, IOException("foo is $apiKey")) + e = ModuleException(debugMessage, msg, IOException("foo is $apiKey")) assertThat(e.sanitize(" ", apiKey, "foo").message, "key in cause").isNotNull().all { doesNotContain(apiKey) endsWith("xxx is xxxxxxxxxx") diff --git a/version.properties b/version.properties index c002730..d3947a4 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Nov 09 08:47:23 PST 2021 -version.buildmeta=2227 +#Sun Nov 14 21:44:14 PST 2021 +version.buildmeta=2262 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+2227 +version.semver=0.8.0-beta+2262 From ba9d79ce608dbfa8a084a37acb215291bfe5cb13 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 16 Nov 2021 10:46:22 -0800 Subject: [PATCH 581/842] Converted colorize, bold, etc. to extension functions. --- .../net/thauvin/erik/mobibot/FeedReader.kt | 3 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 32 +++++++++---------- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 5 +-- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../erik/mobibot/commands/links/LinksMgr.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 4 +-- .../erik/mobibot/entries/EntriesUtils.kt | 4 +-- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 4 +-- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 15 +++------ .../net/thauvin/erik/mobibot/UtilsTest.kt | 32 +++++++++---------- .../thauvin/erik/mobibot/modules/CalcTest.kt | 6 ++-- .../erik/mobibot/modules/WordTimeTest.kt | 8 ++--- 17 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 16c8e6b..739f45a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.XmlReader +import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.sendMessage @@ -83,7 +84,7 @@ class FeedReader(private val url: String, val event: GenericMessageEvent) : Runn } else { items.take(maxItems).forEach { messages.add(NoticeMessage(it.title)) - messages.add(NoticeMessage(helpFormat(green(it.link), false))) + messages.add(NoticeMessage(helpFormat(it.link.cyan(), false))) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 6f75788..0d220c8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -75,19 +75,19 @@ object Utils { * Makes the given int bold. */ @JvmStatic - fun bold(i: Int): String = bold(i.toString()) + fun Int.bold(): String = this.toString().bold() /** * Makes the given long bold. */ @JvmStatic - fun bold(i: Long): String = bold(i.toString()) + fun Long.bold(): String = this.toString().bold() /** * Makes the given string bold. */ @JvmStatic - fun bold(s: String?): String = colorize(s, Colors.BOLD) + fun String?.bold(): String = this.colorize(Colors.BOLD) /** * Returns the [PircBotX] instance. @@ -122,15 +122,15 @@ object Utils { * Colorize a string. */ @JvmStatic - fun colorize(s: String?, color: String): String { - return if (s.isNullOrEmpty()) { + fun String?.colorize(color: String): String { + return if (this.isNullOrEmpty()) { "" } else if (color == DEFAULT_COLOR) { - s + this } else if (Colors.BOLD == color || Colors.REVERSE == color) { - color + s + color + color + this + color } else { - color + s + Colors.NORMAL + color + this + Colors.NORMAL } } @@ -138,7 +138,7 @@ object Utils { * Makes the given string cyan. */ @JvmStatic - fun cyan(s: String?): String = colorize(s, Colors.CYAN) + fun String?.cyan(): String = this.colorize(Colors.CYAN) /** * URL encodes the given string. @@ -158,7 +158,7 @@ object Utils { * Makes the given string green. */ @JvmStatic - fun green(s: String?): String = colorize(s, Colors.DARK_GREEN) + fun String?.green(): String = this.colorize(Colors.DARK_GREEN) /** * Returns a formatted help string. @@ -166,7 +166,7 @@ object Utils { @JvmStatic @JvmOverloads fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { - val s = if (isBold) bold(help) else help + val s = if (isBold) help.bold() else help return if (isIndent) s.prependIndent() else s } @@ -200,7 +200,7 @@ object Utils { * Makes the given string red. */ @JvmStatic - fun red(s: String?): String = colorize(s, Colors.RED) + fun String?.red(): String = this.colorize(Colors.RED) /** * Replaces all occurrences of Strings within another String. @@ -220,7 +220,7 @@ object Utils { * Makes the given string reverse color. */ @JvmStatic - fun reverseColor(s: String?): String = colorize(s, Colors.REVERSE) + fun String?.reverseColor(): String = this.colorize(Colors.REVERSE) /** * Send a formatted commands/modules, etc. list. @@ -252,11 +252,11 @@ object Utils { @JvmStatic fun GenericMessageEvent.sendMessage(channel: String, message: Message) { if (message.isNotice) { - bot().sendIRC().notice(user.nick, colorize(message.msg, message.color)) + bot().sendIRC().notice(user.nick, message.msg.colorize(message.color)) } else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) { - respondPrivateMessage(colorize(message.msg, message.color)) + respondPrivateMessage(message.msg.colorize(message.color)) } else { - bot().sendIRC().message(channel, colorize(message.msg, message.color)) + bot().sendIRC().message(channel, message.msg.colorize(message.color)) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index d1e38ce..2dc04ca 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -134,7 +134,7 @@ class Ignore : AbstractCommand() { event.sendMessage("The following nicks are ignored:") event.sendList(ignored.sorted(), 8, isIndent = true) } else { - event.sendMessage("No one is currently ${bold("ignored")}.") + event.sendMessage("No one is currently ${"ignored".bold()}.") } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index a383756..8e54bf0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.ReleaseInfo import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp @@ -46,8 +47,8 @@ import java.lang.management.ManagementFactory class Info(private val tell: Tell) : AbstractCommand() { private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${green(ReleaseInfo.WEBSITE)})", - "Written by ${ReleaseInfo.AUTHOR} (${green(ReleaseInfo.AUTHOR_URL)})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.cyan()})", + "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" ) override val name = "info" override val help = listOf("To view information about the bot:", helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 61143fb..02f0a3c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -47,7 +47,7 @@ class Comment : AbstractCommand() { override val help = listOf( "To add a comment:", helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${bold(Constants.LINK_CMD)}1.1", + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", "To edit a comment, use its label: ", helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), "To delete a comment, use its label and a minus sign: ", diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 7bd95b8..74be69f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -167,7 +167,7 @@ class LinksMgr : AbstractCommand() { for (i in entries.links.indices) { if (link == entries.links[i].link) { val entry: EntryLink = entries.links[i] - event.sendMessage(bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry)) + event.sendMessage("Duplicate".bold() + " >> " + EntriesUtils.buildLink(i, entry)) return true } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 2e7d9ec..1a43c03 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -49,12 +49,12 @@ class Posting : AbstractCommand() { override val help = listOf( "Post a URL, by saying it on a line on its own:", helpFormat(" [] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${bold(Constants.LINK_CMD)}1", + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", "To add a title, use its label and a pipe:", helpFormat("${Constants.LINK_CMD}1:|This is the title"), "To add a comment:", helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${bold(Constants.LINK_CMD)}1.1", + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", "To edit a comment, see: ", helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index b577895..4c4c29c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -65,9 +65,9 @@ object EntriesUtils { if (Constants.NO_TITLE == title) { buff.append(title) } else { - buff.append(bold(title)) + buff.append(title.bold()) } - buff.append(" ( ").append(green(link)).append(" )") + buff.append(" ( ").append(link.green()).append(" )") } return buff.toString() } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index efe87e5..25ae072 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -74,7 +74,7 @@ class Calc : AbstractModule() { fun calculate(query: String): String { val decimalFormat = DecimalFormat("#.##") val calc = ExpressionBuilder(query).build() - return query.replace(" ", "") + " = " + bold(decimalFormat.format(calc.evaluate())) + return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()).bold() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index ebaadb8..915aa49 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -89,7 +89,7 @@ class CurrencyConverter : ThreadedModule() { helpResponse(event) } } else if (args.contains(CURRENCY_RATES_KEYWORD)) { - event.sendMessage("The reference rates for ${bold(pubDate)} are:") + event.sendMessage("The reference rates for ${pubDate.bold()} are:") event.sendList(currencyRates(), 3, " ", isIndent = true) } else { helpResponse(event) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 977a5b6..4dced28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -47,11 +47,11 @@ class Dice : AbstractModule() { val total = roll.first + roll.second with(event.bot()) { event.respond( - "you rolled ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]} for a total of ${bold(total)}" + "you rolled ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]} for a total of ${total.bold()}" ) sendIRC().action( channel, - "rolled ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]} for a total of ${bold(botTotal)}" + "rolled ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]} for a total of ${botTotal.bold()}" ) when (winLoseOrTie(botTotal, total)) { Result.WIN -> sendIRC().action(channel, "wins.") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 3ba5f00..26d7af3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -66,7 +66,7 @@ class Joke : ThreadedModule() { override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { with(event.bot()) { try { - sendIRC().notice(channel, cyan(randomJoke().msg)) + sendIRC().notice(channel, randomJoke().msg.cyan()) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) e.message?.let { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 273c374..f5f1e16 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -240,7 +240,7 @@ class Weather2 : ThreadedModule() { add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) add("For example:") add(helpFormat("%c $WEATHER_CMD paris, fr")) - add("The default ISO 3166 country code is ${bold("US")}. Zip codes supported in most countries.") + add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") } initProperties(OWM_API_KEY_PROP) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 3105d97..5d9fcde 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -64,7 +64,7 @@ class WorldTime : AbstractModule() { // Date/Time Format private var dtf = - DateTimeFormatter.ofPattern("'The time is ${bold("'HH:mm'")} on ${bold("'EEEE, d MMMM yyyy'")} in '") + DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") /** * Returns the current Internet (beat) Time. @@ -84,10 +84,10 @@ class WorldTime : AbstractModule() { val tz = COUNTRIES_MAP[(if (query.isNotBlank()) query.trim().uppercase() else DEFAULT_ZONE)] return if (tz != null) { if (BEATS_KEYWORD == tz) { - "The current Internet Time is ${bold(internetTime())} $BEATS_KEYWORD" + "The current Internet Time is ${internetTime().bold()} $BEATS_KEYWORD" } else { (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) - + bold(tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' '))) + + tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ').bold()) } } else { "Unsupported country/zone. Please try again." @@ -362,13 +362,8 @@ class WorldTime : AbstractModule() { zones["ZM"] = "Africa/Lusaka" zones["ZULU"] = "Zulu" zones["ZW"] = "Africa/Harare" - ZoneId.getAvailableZoneIds().stream() - .filter { tz: String -> - tz.length <= 3 && !zones.containsKey(tz) - } - .forEach { tz: String -> - zones[tz] = tz - } + ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !zones.containsKey(it) } + .forEach { tz -> zones[tz] = tz } COUNTRIES_MAP = Collections.unmodifiableMap(zones) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 876bea0..92ac36f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -97,10 +97,10 @@ class UtilsTest { @Test fun testBold() { - assertThat(bold(1), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - assertThat(bold(2L), "bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) - assertThat(bold(ascii), "bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat(bold("test"), "bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) + assertThat(1.bold(), "1.bold()").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(2L.bold(), "1.bold()").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) + assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) } @Test @@ -131,23 +131,23 @@ class UtilsTest { @Test fun testColorize() { - assertThat(colorize(ascii, Colors.REVERSE), "colorize(reverse)").isEqualTo( + assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( Colors.REVERSE + ascii + Colors.REVERSE ) - assertThat(colorize(ascii, Colors.RED), "colorize(red)") + assertThat(ascii.colorize(Colors.RED), "red.colorize()") .isEqualTo(Colors.RED + ascii + Colors.NORMAL) - assertThat(colorize(ascii, Colors.BOLD), "colorized(bold)") + assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat(colorize(null, Colors.RED), "colorize(null)").isEqualTo("") - assertThat(colorize("", Colors.RED), "colorize()").isEqualTo("") - assertThat(colorize(ascii, DEFAULT_COLOR), "colorize(none)").isEqualTo(ascii) - assertThat(colorize(" ", Colors.NORMAL), "colorize(blank)") + assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") + assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") + assertThat(ascii.colorize(DEFAULT_COLOR), "none.colorize()").isEqualTo(ascii) + assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) } @Test fun testCyan() { - assertThat(cyan(ascii)).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) + assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) } @Test @@ -167,7 +167,7 @@ class UtilsTest { @Test fun testGreen() { - assertThat(green(ascii)).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) + assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) } @Test @@ -177,7 +177,7 @@ class UtilsTest { assertThat(helpFormat(test, isBold = false, isIndent = true), "indent") .isEqualTo(test.prependIndent()) assertThat(helpFormat(test, isBold = true, isIndent = true), "bold-indent") - .isEqualTo(colorize(test, Colors.BOLD).prependIndent()) + .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) } @@ -224,12 +224,12 @@ class UtilsTest { @Test fun testRed() { - assertThat(red(ascii)).isEqualTo(colorize(ascii, Colors.RED)) + assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED)) } @Test fun testReverseColor() { - assertThat(reverseColor(ascii)).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) + assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) } @Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 80a49b5..b37a09c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -46,9 +46,9 @@ import org.testng.annotations.Test class CalcTest { @Test fun testCalculate() { - assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${bold(2)}") - assertThat(calculate("1 -3"), "calculate(1 -3)").isEqualTo("1-3 = ${bold(-2)}") - assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${bold("10.62")}") + assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") + assertThat(calculate("1 -3"), "calculate(1 -3)").isEqualTo("1-3 = ${(-2).bold()}") + assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}") assertThat { calculate("one + one") }.isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index ddc6e8f..abe9416 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -55,10 +55,10 @@ class WordTimeTest { "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() ) - assertThat(time(""), "empty zone").endsWith(bold("Los Angeles")) - assertThat(time("PST"), "PST").endsWith(bold("Los Angeles")) - assertThat(time("GB"), "GB").endsWith(bold("London")) - assertThat(time("FR"), "FR").endsWith(bold("Paris")) + assertThat(time(""), "empty zone").endsWith("Los Angeles".bold()) + assertThat(time("PST"), "PST").endsWith("Los Angeles".bold()) + assertThat(time("GB"), "GB").endsWith("London".bold()) + assertThat(time("FR"), "FR").endsWith("Paris".bold()) assertThat(time("BLAH"), "BLAH").startsWith("Unsupported") assertThat(time("BEAT"), BEATS_KEYWORD).matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) } From bc75d1eb7376150db6879a7028775bcf7443a0c9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Nov 2021 19:38:14 -0800 Subject: [PATCH 582/842] Improved event null-checks. --- .../net/thauvin/erik/mobibot/Mobibot.kt | 96 +++++++++++-------- .../erik/mobibot/modules/CryptoPrices.kt | 4 +- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 574366a..ec571f7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -162,73 +162,87 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } override fun onAction(event: ActionEvent?) { - if (channel == event?.channel?.name) { - storeRecap(event.user!!.nick, event.action, true) + event?.channel?.let { + if (channel == it.name) { + event.user?.let { user -> + storeRecap(user.nick, event.action, true) + } + } } } override fun onDisconnect(event: DisconnectEvent?) { - with(event!!.getBot<PircBotX>()) { - LinksMgr.twitter.notification("$nick disconnected from irc://$serverHostname") + event?.let { + with(event.getBot<PircBotX>()) { + LinksMgr.twitter.notification("$nick disconnected from irc://$serverHostname") + } } LinksMgr.twitter.shutdown() } override fun onPrivateMessage(event: PrivateMessageEvent?) { - if (logger.isTraceEnabled) logger.trace("<<< ${event!!.user!!.nick}: ${event.message}") - val cmds = event!!.message.trim().split(" ".toRegex(), 2) - val cmd = cmds[0].lowercase() - val args = if (cmds.size > 1) { - cmds[1].trim() - } else "" - if (cmd.startsWith(Constants.HELP_CMD)) { // help - helpResponse(event, args) - } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module - helpDefault(event) + event?.user?.let { user -> + if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}") + val cmds = event.message.trim().split(" ".toRegex(), 2) + val cmd = cmds[0].lowercase() + val args = if (cmds.size > 1) { + cmds[1].trim() + } else "" + if (cmd.startsWith(Constants.HELP_CMD)) { // help + helpResponse(event, args) + } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module + helpDefault(event) + } } } override fun onJoin(event: JoinEvent?) { - with(event!!.getBot<PircBotX>()) { - if (event.user!!.nick == nick) { - LinksMgr.twitter.notification("$nick has joined ${event.channel.name} on irc://$serverHostname") - } else { - tell.send(event) + event?.user?.let { user -> + with(event.getBot<PircBotX>()) { + if (user.nick == nick) { + LinksMgr.twitter.notification("$nick has joined ${event.channel.name} on irc://$serverHostname") + } else { + tell.send(event) + } } } } override fun onMessage(event: MessageEvent?) { - val sender = event!!.user!!.nick - val message = event.message - tell.send(event) - if (message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command> - if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") - val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) - val cmd = cmds[0].lowercase() - val args = if (cmds.size > 1) { - cmds[1].trim() - } else "" - if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help - helpResponse(event, args) - } else { - // Execute module or command - addons.exec(channel, cmd, args, event) + event?.user?.let { user -> + val sender = user.nick + val message = event.message + tell.send(event) + if (message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command> + if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") + val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) + val cmd = cmds[0].lowercase() + val args = if (cmds.size > 1) { + cmds[1].trim() + } else "" + if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help + helpResponse(event, args) + } else { + // Execute module or command + addons.exec(channel, cmd, args, event) + } + } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. + if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") } - } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. - if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") + storeRecap(sender, message, false) } - storeRecap(sender, message, false) } override fun onNickChange(event: NickChangeEvent?) { - tell.send(event!!) + event?.let { tell.send(event) } } override fun onPart(event: PartEvent?) { - with(event!!.getBot<PircBotX>()) { - if (event.user!!.nick == nick) { - LinksMgr.twitter.notification("$nick has left ${event.channel.name} on irc://$serverHostname") + event?.user?.let { user -> + with(event.getBot<PircBotX>()) { + if (user.nick == nick) { + LinksMgr.twitter.notification("$nick has left ${event.channel.name} on irc://$serverHostname") + } } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 6cd8906..9721055 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -63,7 +63,9 @@ class CryptoPrices : ThreadedModule() { event.respond("${price.base} current price is $amount [${price.currency}]") } catch (e: CryptoException) { if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) - event.sendMessage(e.message!!) + e.message?.let { + event.sendMessage(it) + } } catch (e: IOException) { if (logger.isErrorEnabled) logger.error(debugMessage, e) event.sendMessage("An IO error has occurred while retrieving the cryptocurrency market price.") From 2963e747be50d767f671c322007db495845d540c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Nov 2021 22:02:45 -0800 Subject: [PATCH 583/842] Added more tests. --- .../thauvin/erik/mobibot/commands/Recap.kt | 4 +- .../erik/mobibot/commands/links/LinksMgr.kt | 26 +++--- .../erik/mobibot/commands/links/View.kt | 48 +++++----- .../erik/mobibot/commands/RecapTest.kt | 54 +++++++++++ .../mobibot/commands/links/LinksMgrTest.kt | 75 +++++++++++++++ .../erik/mobibot/commands/links/ViewTest.kt | 92 +++++++++++++++++++ 6 files changed, 264 insertions(+), 35 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 03b349c..6fe1e36 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -50,6 +50,8 @@ class Recap : AbstractCommand() { override val isVisible = true companion object { + const val MAX_RECAPS = 10 + @JvmField val recaps = mutableListOf<String>() @@ -62,7 +64,7 @@ class Recap : AbstractCommand() { LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + " - $sender" + (if (isAction) " " else ": ") + message ) - if (recaps.size > 10) { + if (recaps.size > MAX_RECAPS) { recaps.removeFirst() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 74be69f..17b06e1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -42,7 +42,8 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored import net.thauvin.erik.mobibot.entries.Entries -import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.mobibot.modules.Twitter import org.jsoup.Jsoup @@ -124,7 +125,7 @@ class LinksMgr : AbstractCommand() { val entry = EntryLink(link, title, sender, login, channel, tags) entries.links.add(entry) val index = entries.links.lastIndexOf(entry) - event.sendMessage(EntriesUtils.buildLink(index, entry)) + event.sendMessage(buildLink(index, entry)) pinboard.addPin(event.bot().serverHostname, entry) @@ -135,7 +136,7 @@ class LinksMgr : AbstractCommand() { if (Constants.NO_TITLE == entry.title) { event.sendMessage("Please specify a title, by typing:") - event.sendMessage(helpFormat("${EntriesUtils.buildLinkLabel(index)}:|This is the title")) + event.sendMessage(helpFormat("${buildLinkLabel(index)}:|This is the title")) } } } @@ -147,7 +148,7 @@ class LinksMgr : AbstractCommand() { return message.matches(LINK_MATCH.toRegex()) } - private fun fetchTitle(link: String): String { + internal fun fetchTitle(link: String): String { try { val html = Jsoup.connect(link) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") @@ -164,18 +165,19 @@ class LinksMgr : AbstractCommand() { private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean { synchronized(entries) { - for (i in entries.links.indices) { - if (link == entries.links[i].link) { - val entry: EntryLink = entries.links[i] - event.sendMessage("Duplicate".bold() + " >> " + EntriesUtils.buildLink(i, entry)) - return true - } + return try { + val match = entries.links.single { it.link == link } + event.sendMessage( + "Duplicate".bold() + " >> " + buildLink(entries.links.indexOf(match), match) + ) + true + } catch (ignore: NoSuchElementException) { + false } } - return false } - private fun matchTagKeywords(title: String, tags: MutableList<String>) { + internal fun matchTagKeywords(title: String, tags: MutableList<String>) { for (match in keywords) { val m = Regex.escape(match) if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 5d446d2..c9021fb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -60,56 +60,60 @@ class View : AbstractCommand() { override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (entries.links.isNotEmpty()) { - showPosts(args, event) + val p = parseArgs(args) + showPosts(p.first, p.second, event) } else { event.sendMessage("There is currently nothing to view. Why don't you post something?") } } - private fun showPosts(args: String, event: GenericMessageEvent) { - val max = entries.links.size - var lcArgs = args.lowercase() - var i = 0 - if (lcArgs.isEmpty() && max > maxEntries) { - i = max - maxEntries + internal fun parseArgs(args: String): Pair<Int, String> { + var query = args.lowercase().trim() + var start = 0 + if (query.isEmpty() && entries.links.size > maxEntries) { + start = entries.links.size - maxEntries } - if (lcArgs.matches("^\\d+(| .*)".toRegex())) { - val split = lcArgs.split(" ", limit = 2) + if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>] + val split = query.split(" ", limit = 2) try { - i = split[0].toInt() - 1 - lcArgs = if (split.size == 2) { + start = split[0].toInt() - 1 + query = if (split.size == 2) { split[1].trim() } else { "" } - if (i > max) { - i = 0 + if (start > entries.links.size) { + start = 0 } } catch (ignore: NumberFormatException) { // Do nothing } } + return Pair(start, query) + } + private fun showPosts(start: Int, query: String, event: GenericMessageEvent) { + var index = start var entry: EntryLink var sent = 0 - while (i < max && sent < maxEntries) { - entry = entries.links[i] - if (lcArgs.isNotBlank()) { - if (entry.matches(lcArgs)) { - event.sendMessage(EntriesUtils.buildLink(i, entry, true)) + while (index < entries.links.size && sent < maxEntries) { + entry = entries.links[index] + if (query.isNotBlank()) { + if (entry.matches(query)) { + event.sendMessage(EntriesUtils.buildLink(index, entry, true)) sent++ } } else { - event.sendMessage(EntriesUtils.buildLink(i, entry, true)) + event.sendMessage(EntriesUtils.buildLink(index, entry, true)) sent++ } - i++ - if (sent == maxEntries && i < max) { + index++ + if (sent == maxEntries && index < entries.links.size) { event.sendMessage("To view more, try: ") event.sendMessage( helpFormat( buildCmdSyntax( - "%c $name ${i + 1} $lcArgs", + "%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent ) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt new file mode 100644 index 0000000..d347f1a --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -0,0 +1,54 @@ +/* + * RecapTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.prop +import assertk.assertions.size +import org.testng.annotations.Test + +class RecapTest { + @Test + fun storeRecapTest() { + for (i in 1..20) { + Recap.storeRecap("sender$i", "test $1", false) + } + assertThat(Recap.recaps).all { + size().isEqualTo(Recap.MAX_RECAPS) + prop(MutableList<String>::last).contains("sender20") + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt new file mode 100644 index 0000000..12fccbd --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt @@ -0,0 +1,75 @@ +/* + * LinksMgrTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isTrue +import assertk.assertions.size +import net.thauvin.erik.mobibot.Constants +import org.testng.annotations.Test + +class LinksMgrTest { + private val linksMgr = LinksMgr() + + @Test + fun fetchTitle() { + assertThat(linksMgr.fetchTitle("https://erik.thauvin.net/"), "Erik").contains("Erik's Weblog") + assertThat(linksMgr.fetchTitle("https://www.google.com/foo"), "Foo").isEqualTo(Constants.NO_TITLE) + } + + @Test + fun testMatches() { + assertThat(linksMgr.matches("https://www.example.com/"), "https").isTrue() + assertThat(linksMgr.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "HTTP").isTrue() + } + + @Test + fun matchTagKeywordsTest() { + linksMgr.setProperty(LinksMgr.KEYWORDS_PROP, "key1 key2,key3") + val tags = mutableListOf<String>() + + linksMgr.matchTagKeywords("Test title with key2", tags) + assertThat(tags, "key2").contains("key2") + tags.clear() + + linksMgr.matchTagKeywords("Test key3 title with key1", tags) + assertThat(tags, "key1 & key 3").all { + contains("key1") + contains("key3") + size().isEqualTo(2) + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt new file mode 100644 index 0000000..a239dfd --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -0,0 +1,92 @@ +/* + * ViewTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.prop +import net.thauvin.erik.mobibot.entries.EntryLink +import org.testng.annotations.Test + +class ViewTest { + @Test + fun testParseArgs() { + val view = View() + + for (i in 1..3) { + LinksMgr.entries.links.add( + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) + ) + } + + assertThat(view.parseArgs("1"), "parseArgs(1)").all { + prop(Pair<Int, String>::first).isEqualTo(0) + prop(Pair<Int, String>::second).isEqualTo("") + } + + assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all { + prop(Pair<Int, String>::first).isEqualTo(1) + prop(Pair<Int, String>::second).isEqualTo("foo") + } + + assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all { + prop(Pair<Int, String>::first).isEqualTo(2) + prop(Pair<Int, String>::second).isEqualTo("foo") + } + + assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all { + prop(Pair<Int, String>::first).isEqualTo(3) + prop(Pair<Int, String>::second).isEqualTo("foo bar") + } + + assertThat(view.parseArgs("5"), "parseArgs(5)").all { + prop(Pair<Int, String>::first).isEqualTo(0) + prop(Pair<Int, String>::second).isEqualTo("") + } + + LinksMgr.entries.links.clear() + + assertThat(view.parseArgs("4"), "parseArgs(4)").all { + prop(Pair<Int, String>::first).isEqualTo(0) + prop(Pair<Int, String>::second).isEqualTo("") + } + } +} From aab95fb0400495ad2de460554167174f32e8cb08 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Nov 2021 22:04:41 -0800 Subject: [PATCH 584/842] Minor cleanups. --- .../kotlin/net/thauvin/erik/mobibot/FeedReader.kt | 3 +-- .../kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt | 1 - .../kotlin/net/thauvin/erik/mobibot/commands/Info.kt | 3 +-- .../kotlin/net/thauvin/erik/mobibot/commands/Say.kt | 1 - .../kotlin/net/thauvin/erik/mobibot/commands/Users.kt | 1 - .../thauvin/erik/mobibot/commands/links/Comment.kt | 11 ++++++----- 6 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 739f45a..a61a6a7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.XmlReader -import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.sendMessage @@ -84,7 +83,7 @@ class FeedReader(private val url: String, val event: GenericMessageEvent) : Runn } else { items.take(maxItems).forEach { messages.add(NoticeMessage(it.title)) - messages.add(NoticeMessage(helpFormat(it.link.cyan(), false))) + messages.add(NoticeMessage(helpFormat(it.link.green(), false))) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index e5b019d..00d6638 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -47,7 +47,6 @@ class Cycle : AbstractCommand() { override val isPublic = false override val isVisible = true - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { with(event.bot()) { if (isChannelOp(channel, event)) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 8e54bf0..41065ff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.ReleaseInfo import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp @@ -47,7 +46,7 @@ import java.lang.management.ManagementFactory class Info(private val tell: Tell) : AbstractCommand() { private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.cyan()})", + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" ) override val name = "info" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index a89c64e..34b4aec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -44,7 +44,6 @@ class Say : AbstractCommand() { override val isPublic = false override val isVisible = true - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isChannelOp(channel, event)) { event.bot().sendIRC().message(channel, args) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 5133146..e643858 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -44,7 +44,6 @@ class Users : AbstractCommand() { override val isPublic = true override val isVisible = true - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { val nicks = mutableListOf<String>() val ch = event.bot().userChannelDao.getChannel(channel) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 02f0a3c..5b137ff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -38,7 +38,8 @@ import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import org.pircbotx.hooks.types.GenericMessageEvent @@ -110,7 +111,7 @@ class Comment : AbstractCommand() { if (isChannelOp(channel, event) && cmd.length > 1) { val comment = entry.getComment(commentIndex) comment.nick = cmd.substring(1) - event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment)) + event.sendMessage(buildComment(entryIndex, commentIndex, comment)) LinksMgr.entries.save() } else { event.sendMessage("Please ask a channel op to change the author of this comment for you.") @@ -126,7 +127,7 @@ class Comment : AbstractCommand() { ) { if (isChannelOp(channel, event) || event.user.nick == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) - event.sendMessage("Comment ${EntriesUtils.buildLinkLabel(entryIndex)}.${commentIndex + 1} removed.") + event.sendMessage("Comment ${buildLinkLabel(entryIndex)}.${commentIndex + 1} removed.") LinksMgr.entries.save() } else { event.sendMessage("Please ask a channel op to delete this comment for you.") @@ -141,11 +142,11 @@ class Comment : AbstractCommand() { event: GenericMessageEvent ) { entry.setComment(commentIndex, cmd, event.user.nick) - event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + event.sendMessage(buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) LinksMgr.entries.save() } private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) { - event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + event.sendMessage(buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) } } From 655889d2b999fb7b4b498d24390fcc33b2178f0a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Nov 2021 22:07:31 -0800 Subject: [PATCH 585/842] Use filters instead of looping through all messages. --- .../erik/mobibot/commands/tell/Tell.kt | 170 ++++++++---------- 1 file changed, 74 insertions(+), 96 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index f66ba2b..b18327f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -61,62 +61,6 @@ class Tell(private val serialObject: String) : AbstractCommand() { // Message maximum queue size private var maxSize = 50 - /** - * Cleans the messages queue. - */ - private fun clean(): Boolean { - // if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.") - return TellMessagesMgr.clean(messages, maxDays.toLong()) - } - - // Delete message. - private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) { - val split = args.split(" ") - if (split.size == 2) { - val id = split[1] - var deleted = false - if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { - for (message in messages) { - if (message.sender.equals(event.user.nick, ignoreCase = true) && message.isReceived) { - messages.remove(message) - deleted = true - } - } - if (deleted) { - save() - event.sendMessage("Delivered messages have been deleted.") - } else { - event.sendMessage("No delivered messages were found.") - } - } else { - var found = false - for (message in messages) { - found = (message.id == id) - if (found && (message.sender.equals(event.user.nick, ignoreCase = true) || isChannelOp( - channel, - event - )) - ) { - messages.remove(message) - save() - event.sendMessage("Your message was deleted from the queue.") - deleted = true - break - } - } - if (!deleted) { - if (found) { - event.sendMessage("Only messages that you sent can be deleted.") - } else { - event.sendMessage("The specified message [ID $id] could not be found.") - } - } - } - } else { - helpResponse(channel, args, event) - } - } - /** * The tell command. */ @@ -127,12 +71,20 @@ class Tell(private val serialObject: String) : AbstractCommand() { helpFormat("%c $name <nick> <message>"), "To view queued and sent messages:", helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${bold(maxDays)}" + " day".plural(maxDays.toLong()) + '.' + "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' ) override val isOpOnly: Boolean = false override val isPublic: Boolean = isEnabled() override val isVisible: Boolean = isEnabled() + /** + * Cleans the messages queue. + */ + private fun clean(): Boolean { + // if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.") + return TellMessagesMgr.clean(messages, maxDays.toLong()) + } + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isEnabled()) { if (args.isBlank()) { @@ -154,6 +106,34 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } + // Delete message. + private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) { + val split = args.split(" ") + if (split.size == 2) { + val id = split[1] + if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { + if (messages.removeIf { it.sender.equals(event.user.nick, true) && it.isReceived }) { + save() + event.sendMessage("Delivered messages have been deleted.") + } else { + event.sendMessage("No delivered messages were found.") + } + } else { + if (messages.removeIf { + it.id == id && + (it.sender.equals(event.user.nick, true) || isChannelOp(channel, event)) + }) { + save() + event.sendMessage("The message was deleted from the queue.") + } else { + event.sendMessage("The specified message [ID $id] could not be found.") + } + } + } else { + helpResponse(channel, args, event) + } + } + override fun isEnabled(): Boolean { return maxSize > 0 && maxDays > 0 } @@ -175,7 +155,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { val message = TellMessage(event.user.nick, split[0], split[1].trim()) messages.add(message) save() - event.sendMessage("Message [ID ${message.id}] was queued for ${bold(message.recipient)}") + event.sendMessage("Message [ID ${message.id}] was queued for ${message.recipient.bold()}") } else { event.sendMessage("Sorry, the messages queue is currently full.") } @@ -197,36 +177,35 @@ class Tell(private val serialObject: String) : AbstractCommand() { fun send(event: GenericUserEvent) { val nickname = event.user.nick if (isEnabled() && nickname != event.getBot<PircBotX>().nick) { - messages.stream().filter { message: TellMessage -> message.isMatch(nickname) } - .forEach { message: TellMessage -> - if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { - if (message.sender == nickname) { - if (event !is MessageEvent) { - event.user.send().message( - "${bold("You")} wanted me to remind you: ${reverseColor(message.message)}" - ) - message.isReceived = true - message.isNotified = true - save() - } - } else { + messages.filter { it.isMatch(nickname) }.forEach { message -> + if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { + if (message.sender == nickname) { + if (event !is MessageEvent) { event.user.send().message( - "${message.sender} wanted me to tell you: ${reverseColor(message.message)}" + "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" ) message.isReceived = true + message.isNotified = true save() } - } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified - ) { + } else { event.user.send().message( - "Your message ${reverseColor("[ID ${message.id}]")} was sent to " - + "${bold(message.recipient)} on ${message.receptionDate}" + "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" ) - message.isNotified = true + message.isReceived = true save() } + } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived + && !message.isNotified + ) { + event.user.send().message( + "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " + + "${message.recipient.bold()} on ${message.receptionDate}" + ) + message.isNotified = true + save() } + } } } @@ -242,7 +221,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (messages.isNotEmpty()) { for (message in messages) { event.sendMessage( - "${bold(message.sender)}$ARROW${bold(message.recipient)} [ID: ${message.id}, " + + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + (if (message.isReceived) "DELIVERED]" else "QUEUED]") ) } @@ -254,24 +233,23 @@ class Tell(private val serialObject: String) : AbstractCommand() { // View messages. private fun viewMessages(event: GenericMessageEvent) { var hasMessage = false - for (message in messages) { - if (message.isMatch(event.user.nick)) { - if (!hasMessage) { - hasMessage = true; event.sendMessage("Here are your messages: ") - } - if (message.isReceived) { - event.sendMessage( - bold(message.sender) + ARROW + bold(message.recipient) + - " [${message.receptionDate.toUtcDateTime()}, ID: ${bold(message.id)}, DELIVERED]" - ) - } else { - event.sendMessage( - bold(message.sender) + ARROW + bold(message.recipient) + - " [${message.queued.toUtcDateTime()}, ID: ${bold(message.id)}, QUEUED]" - ) - } - event.sendMessage(helpFormat(message.message)) + for (message in messages.filter { it.isMatch(event.user.nick) }) { + if (!hasMessage) { + hasMessage = true + event.sendMessage("Here are your messages: ") } + if (message.isReceived) { + event.sendMessage( + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" + ) + } else { + event.sendMessage( + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" + ) + } + event.sendMessage(helpFormat(message.message)) } if (!hasMessage) { event.sendMessage("You have no messages in the queue.") From ede05c39df3fd6a25285f8d8a7f581d8c8a998a4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Nov 2021 22:08:38 -0800 Subject: [PATCH 586/842] Upgraded to Kotlin 1.6.0. Trying out Kover instead of Jacoco. --- .idea/jarRepositories.xml | 5 +++++ build.gradle | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 75b7aaa..7164ee5 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -36,5 +36,10 @@ <option name="name" value="maven" /> <option name="url" value="https://jitpack.io" /> </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://packages.jetbrains.team/maven/p/ij/intellij-dependencies" /> + </remote-repository> </component> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index b6edcf7..323deeb 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,9 @@ plugins { id 'jacoco' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC2' - id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC2' + id 'org.jetbrains.kotlin.jvm' version '1.6.0' + id 'org.jetbrains.kotlin.kapt' version '1.6.0' + id 'org.jetbrains.kotlinx.kover' version '0.4.2' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -59,7 +60,7 @@ dependencies { implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'com.google.code.gson:gson:2.8.9' - + implementation 'net.objecthunter:exp4j:0.4.8' implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' @@ -154,6 +155,7 @@ incrementBuildMeta { sonarqube { properties { + property('sonar.jacoco.reportPaths', "$buildDir/reports/kover/report.xml") property('sonar.organization', 'ethauvin-github') property('sonar.projectKey', 'ethauvin_mobibot') property('sonar.host.url', 'https://sonarcloud.io') @@ -173,7 +175,7 @@ jacocoTestReport { } tasks.sonarqube { - dependsOn 'jacocoTestReport' + dependsOn 'koverReport' } task copyToDeploy(type: Copy) { From e462a701475cc147c3d22178d99f8e1db79eedca Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 16 Nov 2021 22:16:17 -0800 Subject: [PATCH 587/842] Reverted back to Jacoco reports for Sonarqube. --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 323deeb..53ec183 100644 --- a/build.gradle +++ b/build.gradle @@ -155,7 +155,6 @@ incrementBuildMeta { sonarqube { properties { - property('sonar.jacoco.reportPaths', "$buildDir/reports/kover/report.xml") property('sonar.organization', 'ethauvin-github') property('sonar.projectKey', 'ethauvin_mobibot') property('sonar.host.url', 'https://sonarcloud.io') @@ -175,7 +174,7 @@ jacocoTestReport { } tasks.sonarqube { - dependsOn 'koverReport' + dependsOn 'jacocoTestReport' } task copyToDeploy(type: Copy) { From 7c270ed8729cb70e5eab0aa1769eb7a37e1dacea Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 17 Nov 2021 08:05:45 -0800 Subject: [PATCH 588/842] Added more tests. --- build.gradle | 3 ++- .../erik/mobibot/commands/links/View.kt | 10 ++++---- .../erik/mobibot/commands/links/ViewTest.kt | 24 +++++++++++++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 53ec183..3d1220c 100644 --- a/build.gradle +++ b/build.gradle @@ -158,6 +158,7 @@ sonarqube { property('sonar.organization', 'ethauvin-github') property('sonar.projectKey', 'ethauvin_mobibot') property('sonar.host.url', 'https://sonarcloud.io') + property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/report.xml") } } @@ -174,7 +175,7 @@ jacocoTestReport { } tasks.sonarqube { - dependsOn 'jacocoTestReport' + dependsOn 'koverReport' } task copyToDeploy(type: Copy) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index c9021fb..9ec9f70 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -44,7 +44,6 @@ import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent class View : AbstractCommand() { - private val maxEntries = 6 override val name = VIEW_CMD override val help = listOf( "To list or search the current URL posts:", @@ -55,6 +54,7 @@ class View : AbstractCommand() { override val isVisible = true companion object { + const val MAX_ENTRIES = 6 const val VIEW_CMD = "view" } @@ -70,8 +70,8 @@ class View : AbstractCommand() { internal fun parseArgs(args: String): Pair<Int, String> { var query = args.lowercase().trim() var start = 0 - if (query.isEmpty() && entries.links.size > maxEntries) { - start = entries.links.size - maxEntries + if (query.isEmpty() && entries.links.size > MAX_ENTRIES) { + start = entries.links.size - MAX_ENTRIES } if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>] val split = query.split(" ", limit = 2) @@ -96,7 +96,7 @@ class View : AbstractCommand() { var index = start var entry: EntryLink var sent = 0 - while (index < entries.links.size && sent < maxEntries) { + while (index < entries.links.size && sent < MAX_ENTRIES) { entry = entries.links[index] if (query.isNotBlank()) { if (entry.matches(query)) { @@ -108,7 +108,7 @@ class View : AbstractCommand() { sent++ } index++ - if (sent == maxEntries && index < entries.links.size) { + if (sent == MAX_ENTRIES && index < entries.links.size) { event.sendMessage("To view more, try: ") event.sendMessage( helpFormat( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index a239dfd..dae5cb4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -44,7 +44,7 @@ class ViewTest { fun testParseArgs() { val view = View() - for (i in 1..3) { + for (i in 1..10) { LinksMgr.entries.links.add( EntryLink( "https://www.example.com/$i", @@ -77,8 +77,28 @@ class ViewTest { prop(Pair<Int, String>::second).isEqualTo("foo bar") } - assertThat(view.parseArgs("5"), "parseArgs(5)").all { + assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all { prop(Pair<Int, String>::first).isEqualTo(0) + prop(Pair<Int, String>::second).isEqualTo("foo bar") + } + + assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all { + prop(Pair<Int, String>::first).isEqualTo(0) + prop(Pair<Int, String>::second).isEqualTo("${Int.MAX_VALUE}1") + } + + assertThat(view.parseArgs("1a"), "parseArgs(1a)").all { + prop(Pair<Int, String>::first).isEqualTo(0) + prop(Pair<Int, String>::second).isEqualTo("1a") + } + + assertThat(view.parseArgs("20"), "parseArgs(20)").all { + prop(Pair<Int, String>::first).isEqualTo(0) + prop(Pair<Int, String>::second).isEqualTo("") + } + + assertThat(view.parseArgs(""), "parseArgs()").all { + prop(Pair<Int, String>::first).isEqualTo(LinksMgr.entries.links.size - View.MAX_ENTRIES) prop(Pair<Int, String>::second).isEqualTo("") } From 4e1e09b069b7f5e1a33ea304408e54a1fe4a220e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:17:35 -0800 Subject: [PATCH 589/842] Switched uptime to Duration API. --- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 104 ++++++++++-------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 0d220c8..835125d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -49,8 +49,9 @@ import java.time.ZoneId import java.time.format.DateTimeFormatter import java.util.Date import java.util.Properties -import java.util.concurrent.TimeUnit import java.util.stream.Collectors +import kotlin.time.DurationUnit +import kotlin.time.toDuration /** * Miscellaneous utilities. @@ -64,7 +65,7 @@ object Utils { */ @JvmStatic fun String.appendIfMissing(suffix: Char): String { - return if (this.last() != suffix) { + return if (last() != suffix) { "$this${suffix}" } else { this @@ -75,19 +76,19 @@ object Utils { * Makes the given int bold. */ @JvmStatic - fun Int.bold(): String = this.toString().bold() + fun Int.bold(): String = toString().bold() /** * Makes the given long bold. */ @JvmStatic - fun Long.bold(): String = this.toString().bold() + fun Long.bold(): String = toString().bold() /** * Makes the given string bold. */ @JvmStatic - fun String?.bold(): String = this.colorize(Colors.BOLD) + fun String?.bold(): String = colorize(Colors.BOLD) /** * Returns the [PircBotX] instance. @@ -110,7 +111,7 @@ object Utils { * Capitalize a string. */ @JvmStatic - fun String.capitalise(): String = this.lowercase().replaceFirstChar { it.uppercase() } + fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() } /** * Capitalize words @@ -123,7 +124,7 @@ object Utils { */ @JvmStatic fun String?.colorize(color: String): String { - return if (this.isNullOrEmpty()) { + return if (isNullOrEmpty()) { "" } else if (color == DEFAULT_COLOR) { this @@ -138,7 +139,7 @@ object Utils { * Makes the given string cyan. */ @JvmStatic - fun String?.cyan(): String = this.colorize(Colors.CYAN) + fun String?.cyan(): String = colorize(Colors.CYAN) /** * URL encodes the given string. @@ -151,14 +152,14 @@ object Utils { */ @JvmStatic fun Properties.getIntProperty(key: String, defaultValue: Int): Int { - return this.getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue + return getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue } /** * Makes the given string green. */ @JvmStatic - fun String?.green(): String = this.colorize(Colors.DARK_GREEN) + fun String?.green(): String = colorize(Colors.DARK_GREEN) /** * Returns a formatted help string. @@ -178,13 +179,23 @@ object Utils { return event.bot().userChannelDao.getChannel(channel).isOp(event.user) } + /** + * Return the last item of a list of strings or empty if none. + */ + fun List<String>.lastOrEmpty() : String { + return if (this.size >= 2) { + this.last() + } else + "" + } + /** * Obfuscates the given string. */ @JvmStatic fun String.obfuscate(): String { - return if (this.isNotBlank()) { - "x".repeat(this.length) + return if (isNotBlank()) { + "x".repeat(length) } else this } @@ -200,7 +211,7 @@ object Utils { * Makes the given string red. */ @JvmStatic - fun String?.red(): String = this.colorize(Colors.RED) + fun String?.red(): String = colorize(Colors.RED) /** * Replaces all occurrences of Strings within another String. @@ -220,7 +231,7 @@ object Utils { * Makes the given string reverse color. */ @JvmStatic - fun String?.reverseColor(): String = this.colorize(Colors.REVERSE) + fun String?.reverseColor(): String = colorize(Colors.REVERSE) /** * Send a formatted commands/modules, etc. list. @@ -284,7 +295,7 @@ object Utils { @JvmStatic fun String.toIntOrDefault(defaultValue: Int): Int { return try { - this.toInt() + toInt() } catch (e: NumberFormatException) { defaultValue } @@ -295,28 +306,28 @@ object Utils { */ @JvmStatic fun Date.toIsoLocalDate(): String { - return LocalDateTime.ofInstant(this.toInstant(), ZoneId.systemDefault()).toIsoLocalDate() + return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toIsoLocalDate() } /** * Returns the specified date as an ISO local date string. */ @JvmStatic - fun LocalDateTime.toIsoLocalDate(): String = this.format(DateTimeFormatter.ISO_LOCAL_DATE) + fun LocalDateTime.toIsoLocalDate(): String = format(DateTimeFormatter.ISO_LOCAL_DATE) /** * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. */ @JvmStatic fun Date.toUtcDateTime(): String { - return LocalDateTime.ofInstant(this.toInstant(), ZoneId.systemDefault()).toUtcDateTime() + return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toUtcDateTime() } /** * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. */ @JvmStatic - fun LocalDateTime.toUtcDateTime(): String = this.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) /** * Converts XML/XHTML entities to plain text. @@ -329,38 +340,35 @@ object Utils { */ @JvmStatic fun uptime(uptime: Long): String { - val info = StringBuilder() - var days = TimeUnit.MILLISECONDS.toDays(uptime) - val years = days / 365 - days %= 365 - val months = days / 30 - days %= 30 - val weeks = days / 7 - days %= 7 - val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(uptime)) - val minutes = - TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(uptime)) + uptime.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, _, _ -> + val years = wholeDays / 365 + var days = wholeDays % 365 + val months = days / 30 + days %= 30 + val weeks = days / 7 + days %= 7 - with(info) { - if (years > 0) { - append(years).append(" year".plural(years)).append(' ') - } - if (months > 0) { - append(weeks).append(" month".plural(months)).append(' ') - } - if (weeks > 0) { - append(weeks).append(" week".plural(weeks)).append(' ') - } - if (days > 0) { - append(days).append(" day".plural(days)).append(' ') - } - if (hours > 0) { - append(hours).append(" hour".plural(hours)).append(' ') - } + with(StringBuffer()) { + if (years > 0) { + append(years).append(" year".plural(years)).append(' ') + } + if (months > 0) { + append(weeks).append(" month".plural(months)).append(' ') + } + if (weeks > 0) { + append(weeks).append(" week".plural(weeks)).append(' ') + } + if (days > 0) { + append(days).append(" day".plural(days)).append(' ') + } + if (hours > 0) { + append(hours).append(" hour".plural(hours.toLong())).append(' ') + } - append(minutes).append(" minute".plural(minutes)) + append(minutes).append(" minute".plural(minutes.toLong())) - return toString() + return toString() + } } } From 10f1ee8518780073477a89e5864322f3714563cb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:18:28 -0800 Subject: [PATCH 590/842] Cleaned up constructors. --- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 32 +++++++------------ 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index eb058b7..f1bdabd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -39,7 +39,7 @@ import java.time.format.DateTimeFormatter /** * The `TellMessage` class. */ -class TellMessage internal constructor( +class TellMessage( /** * Returns the message's sender. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index d7730a5..da8ef87 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -41,31 +41,31 @@ import java.util.Date /** * The class used to store link entries. */ -class EntryLink : Serializable { +class EntryLink( // Link's comments - val comments: MutableList<EntryComment> = mutableListOf() + val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories - val tags: MutableList<SyndCategory> = mutableListOf() + val tags: MutableList<SyndCategory> = mutableListOf(), // Channel - var channel: String + var channel: String, // Creation date - var date: Date = Calendar.getInstance().time + var date: Date = Calendar.getInstance().time, // Link's URL - var link: String + var link: String, // Author's login - var login = "" + var login: String = "", // Author's nickname - var nick: String + var nick: String, // Link's title var title: String - +) : Serializable { /** * Creates a new entry. */ @@ -76,12 +76,7 @@ class EntryLink : Serializable { login: String, channel: String, tags: List<String?> - ) { - this.link = link - this.title = title - this.nick = nick - this.login = login - this.channel = channel + ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { setTags(tags) } @@ -95,12 +90,7 @@ class EntryLink : Serializable { channel: String, date: Date, tags: List<SyndCategory> - ) { - this.link = link - this.title = title - this.nick = nick - this.channel = channel - this.date = Date(date.time) + ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { this.tags.addAll(tags) } From 97a9fd15973e4a641b5364231a6b5339637935fb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:24:14 -0800 Subject: [PATCH 591/842] Added lastOrEmpty() extension function. --- .../kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 15 ++++----------- .../thauvin/erik/mobibot/commands/links/View.kt | 7 ++----- .../kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 9 +++++++++ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index ec571f7..bee3340 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -36,6 +36,7 @@ import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.toIsoLocalDate @@ -106,7 +107,7 @@ import java.util.regex.Pattern import kotlin.system.exitProcess @Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") -class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { +class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { // The bot configuration. private val config: Configuration @@ -116,9 +117,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert // Tell module private val tell: Tell - /** Main channel. */ - val channel: String - /** Logger. */ val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java) @@ -185,9 +183,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}") val cmds = event.message.trim().split(" ".toRegex(), 2) val cmd = cmds[0].lowercase() - val args = if (cmds.size > 1) { - cmds[1].trim() - } else "" + val args = cmds.lastOrEmpty().trim() if (cmd.startsWith(Constants.HELP_CMD)) { // help helpResponse(event, args) } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module @@ -217,9 +213,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) val cmd = cmds[0].lowercase() - val args = if (cmds.size > 1) { - cmds[1].trim() - } else "" + val args = cmds.lastOrEmpty().trim() if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help helpResponse(event, args) } else { @@ -354,7 +348,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert * Initialize the bot. */ init { - this.channel = channel val ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) config = Configuration.Builder().apply { name = nickname diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 9ec9f70..e3b731e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -35,6 +35,7 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries @@ -77,11 +78,7 @@ class View : AbstractCommand() { val split = query.split(" ", limit = 2) try { start = split[0].toInt() - 1 - query = if (split.size == 2) { - split[1].trim() - } else { - "" - } + query = split.lastOrEmpty().trim() if (start > entries.links.size) { start = 0 } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 92ac36f..0cc1b41 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -46,6 +46,7 @@ import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.red @@ -187,6 +188,14 @@ class UtilsTest { assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17") } + @Test + fun testLastOrEmpty() { + val two = listOf("1", "2") + assertThat(two.lastOrEmpty(), "two").isEqualTo("2") + val one = listOf("1") + assertThat(one.lastOrEmpty(), "one").isEqualTo("") + } + @Test fun testObfuscate() { assertThat(ascii.obfuscate(), "obfuscate()").all { From 35d8be009ec73b10587414c76b31f096799300eb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:26:31 -0800 Subject: [PATCH 592/842] Switched to buildList/Map. --- .../erik/mobibot/modules/CurrencyConverter.kt | 7 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 544 +++++++++--------- 2 files changed, 273 insertions(+), 278 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 915aa49..16687f6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -180,9 +180,10 @@ class CurrencyConverter : ThreadedModule() { @JvmStatic fun currencyRates(): List<String> { - val rates = mutableListOf<String>() - for ((key, value) in EXCHANGE_RATES.toSortedMap()) { - rates.add("$key: ${value.padStart(8)}") + val rates = buildList { + for ((key, value) in EXCHANGE_RATES.toSortedMap()) { + add("$key: ${value.padStart(8)}") + } } return rates } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 5d9fcde..7abe820 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -40,7 +40,6 @@ import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField -import java.util.Collections /** * The WorldTime module. @@ -51,7 +50,275 @@ class WorldTime : AbstractModule() { const val BEATS_KEYWORD = ".beats" // Supported countries - var COUNTRIES_MAP: Map<String, String> + val COUNTRIES_MAP = buildMap<String, String> { + put("AG", "America/Antigua") + put("AI", "America/Anguilla") + put("AE", "Asia/Dubai") + put("AD", "Europe/Andorra") + put("AKDT", "America/Anchorage") + put("AF", "Asia/Kabul") + put("AKST", "America/Anchorage") + put("AL", "Europe/Tirane") + put("AM", "Asia/Yerevan") + put("AO", "Africa/Luanda") + put("AQ", "Antarctica/South_Pole") + put("AR", "America/Argentina/Buenos_Aires") + put("AS", "Pacific/Pago_Pago") + put("AT", "Europe/Vienna") + put("AU", "Australia/Sydney") + put("AW", "America/Aruba") + put("AX", "Europe/Mariehamn") + put("AZ", "Asia/Baku") + put("BA", "Europe/Sarajevo") + put("BB", "America/Barbados") + put("BD", "Asia/Dhaka") + put("BE", "Europe/Brussels") + put("BEAT", BEATS_KEYWORD) + put("BF", "Africa/Ouagadougou") + put("BG", "Europe/Sofia") + put("BH", "Asia/Bahrain") + put("BI", "Africa/Bujumbura") + put("BJ", "Africa/Porto-Novo") + put("BL", "America/St_Barthelemy") + put("BM", "Atlantic/Bermuda") + put("BMT", BEATS_KEYWORD) + put("BN", "Asia/Brunei") + put("BO", "America/La_Paz") + put("BQ", "America/Kralendijk") + put("BR", "America/Sao_Paulo") + put("BS", "America/Nassau") + put("BT", "Asia/Thimphu") + put("BW", "Africa/Gaborone") + put("BY", "Europe/Minsk") + put("BZ", "America/Belize") + put("CA", "America/Montreal") + put("CC", "Indian/Cocos") + put("CD", "Africa/Kinshasa") + put("CDT", "America/Chicago") + put("CET", "CET") + put("CF", "Africa/Bangui") + put("CG", "Africa/Brazzaville") + put("CH", "Europe/Zurich") + put("CI", "Africa/Abidjan") + put("CK", "Pacific/Rarotonga") + put("CL", "America/Santiago") + put("CM", "Africa/Douala") + put("CN", "Asia/Shanghai") + put("CO", "America/Bogota") + put("CR", "America/Costa_Rica") + put("CST", "America/Chicago") + put("CU", "Cuba") + put("CV", "Atlantic/Cape_Verde") + put("CW", "America/Curacao") + put("CX", "Indian/Christmas") + put("CY", "Asia/Nicosia") + put("CZ", "Europe/Prague") + put("DE", "Europe/Berlin") + put("DJ", "Africa/Djibouti") + put("DK", "Europe/Copenhagen") + put("DM", "America/Dominica") + put("DO", "America/Santo_Domingo") + put("DZ", "Africa/Algiers") + put("EC", "Pacific/Galapagos") + put("EDT", "America/New_York") + put("EE", "Europe/Tallinn") + put("EG", "Africa/Cairo") + put("EH", "Africa/El_Aaiun") + put("ER", "Africa/Asmara") + put("ES", "Europe/Madrid") + put("EST", "America/New_York") + put("ET", "Africa/Addis_Ababa") + put("FI", "Europe/Helsinki") + put("FJ", "Pacific/Fiji") + put("FK", "Atlantic/Stanley") + put("FM", "Pacific/Yap") + put("FO", "Atlantic/Faroe") + put("FR", "Europe/Paris") + put("GA", "Africa/Libreville") + put("GB", "Europe/London") + put("GD", "America/Grenada") + put("GE", "Asia/Tbilisi") + put("GF", "America/Cayenne") + put("GG", "Europe/Guernsey") + put("GH", "Africa/Accra") + put("GI", "Europe/Gibraltar") + put("GL", "America/Thule") + put("GM", "Africa/Banjul") + put("GMT", "GMT") + put("GN", "Africa/Conakry") + put("GP", "America/Guadeloupe") + put("GQ", "Africa/Malabo") + put("GR", "Europe/Athens") + put("GS", "Atlantic/South_Georgia") + put("GT", "America/Guatemala") + put("GU", "Pacific/Guam") + put("GW", "Africa/Bissau") + put("GY", "America/Guyana") + put("HK", "Asia/Hong_Kong") + put("HN", "America/Tegucigalpa") + put("HR", "Europe/Zagreb") + put("HST", "Pacific/Honolulu") + put("HT", "America/Port-au-Prince") + put("HU", "Europe/Budapest") + put("ID", "Asia/Jakarta") + put("IE", "Europe/Dublin") + put("IL", "Asia/Tel_Aviv") + put("IM", "Europe/Isle_of_Man") + put("IN", "Asia/Kolkata") + put("IO", "Indian/Chagos") + put("IQ", "Asia/Baghdad") + put("IR", "Asia/Tehran") + put("IS", "Atlantic/Reykjavik") + put("IT", "Europe/Rome") + put("JE", "Europe/Jersey") + put("JM", "Jamaica") + put("JO", "Asia/Amman") + put("JP", "Asia/Tokyo") + put("KE", "Africa/Nairobi") + put("KG", "Asia/Bishkek") + put("KH", "Asia/Phnom_Penh") + put("KI", "Pacific/Tarawa") + put("KM", "Indian/Comoro") + put("KN", "America/St_Kitts") + put("KP", "Asia/Pyongyang") + put("KR", "Asia/Seoul") + put("KW", "Asia/Riyadh") + put("KY", "America/Cayman") + put("KZ", "Asia/Oral") + put("LA", "Asia/Vientiane") + put("LB", "Asia/Beirut") + put("LC", "America/St_Lucia") + put("LI", "Europe/Vaduz") + put("LK", "Asia/Colombo") + put("LR", "Africa/Monrovia") + put("LS", "Africa/Maseru") + put("LT", "Europe/Vilnius") + put("LU", "Europe/Luxembourg") + put("LV", "Europe/Riga") + put("LY", "Africa/Tripoli") + put("MA", "Africa/Casablanca") + put("MC", "Europe/Monaco") + put("MD", "Europe/Chisinau") + put("MDT", "America/Denver") + put("ME", "Europe/Podgorica") + put("MF", "America/Marigot") + put("MG", "Indian/Antananarivo") + put("MH", "Pacific/Majuro") + put("MK", "Europe/Skopje") + put("ML", "Africa/Timbuktu") + put("MM", "Asia/Yangon") + put("MN", "Asia/Ulaanbaatar") + put("MO", "Asia/Macau") + put("MP", "Pacific/Saipan") + put("MQ", "America/Martinique") + put("MR", "Africa/Nouakchott") + put("MS", "America/Montserrat") + put("MST", "America/Denver") + put("MT", "Europe/Malta") + put("MU", "Indian/Mauritius") + put("MV", "Indian/Maldives") + put("MW", "Africa/Blantyre") + put("MX", "America/Mexico_City") + put("MY", "Asia/Kuala_Lumpur") + put("MZ", "Africa/Maputo") + put("NA", "Africa/Windhoek") + put("NC", "Pacific/Noumea") + put("NE", "Africa/Niamey") + put("NF", "Pacific/Norfolk") + put("NG", "Africa/Lagos") + put("NI", "America/Managua") + put("NL", "Europe/Amsterdam") + put("NO", "Europe/Oslo") + put("NP", "Asia/Kathmandu") + put("NR", "Pacific/Nauru") + put("NU", "Pacific/Niue") + put("NZ", "Pacific/Auckland") + put("OM", "Asia/Muscat") + put("PA", "America/Panama") + put("PDT", "America/Los_Angeles") + put("PE", "America/Lima") + put("PF", "Pacific/Tahiti") + put("PG", "Pacific/Port_Moresby") + put("PH", "Asia/Manila") + put("PK", "Asia/Karachi") + put("PL", "Europe/Warsaw") + put("PM", "America/Miquelon") + put("PN", "Pacific/Pitcairn") + put("PR", "America/Puerto_Rico") + put("PS", "Asia/Gaza") + put("PST", "America/Los_Angeles") + put("PT", "Europe/Lisbon") + put("PW", "Pacific/Palau") + put("PY", "America/Asuncion") + put("QA", "Asia/Qatar") + put("RE", "Indian/Reunion") + put("RO", "Europe/Bucharest") + put("RS", "Europe/Belgrade") + put("RU", "Europe/Moscow") + put("RW", "Africa/Kigali") + put("SA", "Asia/Riyadh") + put("SB", "Pacific/Guadalcanal") + put("SC", "Indian/Mahe") + put("SD", "Africa/Khartoum") + put("SE", "Europe/Stockholm") + put("SG", "Asia/Singapore") + put("SH", "Atlantic/St_Helena") + put("SI", "Europe/Ljubljana") + put("SJ", "Atlantic/Jan_Mayen") + put("SK", "Europe/Bratislava") + put("SL", "Africa/Freetown") + put("SM", "Europe/San_Marino") + put("SN", "Africa/Dakar") + put("SO", "Africa/Mogadishu") + put("SR", "America/Paramaribo") + put("SS", "Africa/Juba") + put("ST", "Africa/Sao_Tome") + put("SV", "America/El_Salvador") + put("SX", "America/Lower_Princes") + put("SY", "Asia/Damascus") + put("SZ", "Africa/Mbabane") + put("TC", "America/Grand_Turk") + put("TD", "Africa/Ndjamena") + put("TF", "Indian/Kerguelen") + put("TG", "Africa/Lome") + put("TH", "Asia/Bangkok") + put("TJ", "Asia/Dushanbe") + put("TK", "Pacific/Fakaofo") + put("TL", "Asia/Dili") + put("TM", "Asia/Ashgabat") + put("TN", "Africa/Tunis") + put("TO", "Pacific/Tongatapu") + put("TR", "Europe/Istanbul") + put("TT", "America/Port_of_Spain") + put("TV", "Pacific/Funafuti") + put("TW", "Asia/Taipei") + put("TZ", "Africa/Dar_es_Salaam") + put("UA", "Europe/Kiev") + put("UG", "Africa/Kampala") + put("UK", "Europe/London") + put("UM", "Pacific/Wake") + put("US", "America/New_York") + put("UTC", "UTC") + put("UY", "America/Montevideo") + put("UZ", "Asia/Tashkent") + put("VA", "Europe/Vatican") + put("VC", "America/St_Vincent") + put("VE", "America/Caracas") + put("VG", "America/Tortola") + put("VI", "America/St_Thomas") + put("VN", "Asia/Ho_Chi_Minh") + put("VU", "Pacific/Efate") + put("WF", "Pacific/Wallis") + put("WS", "Pacific/Apia") + put("YE", "Asia/Aden") + put("YT", "Indian/Mayotte") + put("ZA", "Africa/Johannesburg") + put("ZM", "Africa/Lusaka") + put("ZULU", "Zulu") + put("ZW", "Africa/Harare") + ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } + .forEach { tz -> put(tz, tz) } + } // The Time command private const val TIME_CMD = "time" @@ -93,279 +360,6 @@ class WorldTime : AbstractModule() { "Unsupported country/zone. Please try again." } } - - init { - // Initialize the zones map - val zones = mutableMapOf<String, String>() - zones["AD"] = "Europe/Andorra" - zones["AE"] = "Asia/Dubai" - zones["AF"] = "Asia/Kabul" - zones["AG"] = "America/Antigua" - zones["AI"] = "America/Anguilla" - zones["AKDT"] = "America/Anchorage" - zones["AKST"] = "America/Anchorage" - zones["AL"] = "Europe/Tirane" - zones["AM"] = "Asia/Yerevan" - zones["AO"] = "Africa/Luanda" - zones["AQ"] = "Antarctica/South_Pole" - zones["AR"] = "America/Argentina/Buenos_Aires" - zones["AS"] = "Pacific/Pago_Pago" - zones["AT"] = "Europe/Vienna" - zones["AU"] = "Australia/Sydney" - zones["AW"] = "America/Aruba" - zones["AX"] = "Europe/Mariehamn" - zones["AZ"] = "Asia/Baku" - zones["BA"] = "Europe/Sarajevo" - zones["BB"] = "America/Barbados" - zones["BD"] = "Asia/Dhaka" - zones["BE"] = "Europe/Brussels" - zones["BEAT"] = BEATS_KEYWORD - zones["BF"] = "Africa/Ouagadougou" - zones["BG"] = "Europe/Sofia" - zones["BH"] = "Asia/Bahrain" - zones["BI"] = "Africa/Bujumbura" - zones["BJ"] = "Africa/Porto-Novo" - zones["BL"] = "America/St_Barthelemy" - zones["BM"] = "Atlantic/Bermuda" - zones["BMT"] = BEATS_KEYWORD - zones["BN"] = "Asia/Brunei" - zones["BO"] = "America/La_Paz" - zones["BQ"] = "America/Kralendijk" - zones["BR"] = "America/Sao_Paulo" - zones["BS"] = "America/Nassau" - zones["BT"] = "Asia/Thimphu" - zones["BW"] = "Africa/Gaborone" - zones["BY"] = "Europe/Minsk" - zones["BZ"] = "America/Belize" - zones["CA"] = "America/Montreal" - zones["CC"] = "Indian/Cocos" - zones["CD"] = "Africa/Kinshasa" - zones["CDT"] = "America/Chicago" - zones["CET"] = "CET" - zones["CF"] = "Africa/Bangui" - zones["CG"] = "Africa/Brazzaville" - zones["CH"] = "Europe/Zurich" - zones["CI"] = "Africa/Abidjan" - zones["CK"] = "Pacific/Rarotonga" - zones["CL"] = "America/Santiago" - zones["CM"] = "Africa/Douala" - zones["CN"] = "Asia/Shanghai" - zones["CO"] = "America/Bogota" - zones["CR"] = "America/Costa_Rica" - zones["CST"] = "America/Chicago" - zones["CU"] = "Cuba" - zones["CV"] = "Atlantic/Cape_Verde" - zones["CW"] = "America/Curacao" - zones["CX"] = "Indian/Christmas" - zones["CY"] = "Asia/Nicosia" - zones["CZ"] = "Europe/Prague" - zones["DE"] = "Europe/Berlin" - zones["DJ"] = "Africa/Djibouti" - zones["DK"] = "Europe/Copenhagen" - zones["DM"] = "America/Dominica" - zones["DO"] = "America/Santo_Domingo" - zones["DZ"] = "Africa/Algiers" - zones["EC"] = "Pacific/Galapagos" - zones["EDT"] = "America/New_York" - zones["EE"] = "Europe/Tallinn" - zones["EG"] = "Africa/Cairo" - zones["EH"] = "Africa/El_Aaiun" - zones["ER"] = "Africa/Asmara" - zones["ES"] = "Europe/Madrid" - zones["EST"] = "America/New_York" - zones["ET"] = "Africa/Addis_Ababa" - zones["FI"] = "Europe/Helsinki" - zones["FJ"] = "Pacific/Fiji" - zones["FK"] = "Atlantic/Stanley" - zones["FM"] = "Pacific/Yap" - zones["FO"] = "Atlantic/Faroe" - zones["FR"] = "Europe/Paris" - zones["GA"] = "Africa/Libreville" - zones["GB"] = "Europe/London" - zones["GD"] = "America/Grenada" - zones["GE"] = "Asia/Tbilisi" - zones["GF"] = "America/Cayenne" - zones["GG"] = "Europe/Guernsey" - zones["GH"] = "Africa/Accra" - zones["GI"] = "Europe/Gibraltar" - zones["GL"] = "America/Thule" - zones["GM"] = "Africa/Banjul" - zones["GMT"] = "GMT" - zones["GN"] = "Africa/Conakry" - zones["GP"] = "America/Guadeloupe" - zones["GQ"] = "Africa/Malabo" - zones["GR"] = "Europe/Athens" - zones["GS"] = "Atlantic/South_Georgia" - zones["GT"] = "America/Guatemala" - zones["GU"] = "Pacific/Guam" - zones["GW"] = "Africa/Bissau" - zones["GY"] = "America/Guyana" - zones["HK"] = "Asia/Hong_Kong" - zones["HN"] = "America/Tegucigalpa" - zones["HR"] = "Europe/Zagreb" - zones["HST"] = "Pacific/Honolulu" - zones["HT"] = "America/Port-au-Prince" - zones["HU"] = "Europe/Budapest" - zones["ID"] = "Asia/Jakarta" - zones["IE"] = "Europe/Dublin" - zones["IL"] = "Asia/Tel_Aviv" - zones["IM"] = "Europe/Isle_of_Man" - zones["IN"] = "Asia/Kolkata" - zones["IO"] = "Indian/Chagos" - zones["IQ"] = "Asia/Baghdad" - zones["IR"] = "Asia/Tehran" - zones["IS"] = "Atlantic/Reykjavik" - zones["IT"] = "Europe/Rome" - zones["JE"] = "Europe/Jersey" - zones["JM"] = "Jamaica" - zones["JO"] = "Asia/Amman" - zones["JP"] = "Asia/Tokyo" - zones["KE"] = "Africa/Nairobi" - zones["KG"] = "Asia/Bishkek" - zones["KH"] = "Asia/Phnom_Penh" - zones["KI"] = "Pacific/Tarawa" - zones["KM"] = "Indian/Comoro" - zones["KN"] = "America/St_Kitts" - zones["KP"] = "Asia/Pyongyang" - zones["KR"] = "Asia/Seoul" - zones["KW"] = "Asia/Riyadh" - zones["KY"] = "America/Cayman" - zones["KZ"] = "Asia/Oral" - zones["LA"] = "Asia/Vientiane" - zones["LB"] = "Asia/Beirut" - zones["LC"] = "America/St_Lucia" - zones["LI"] = "Europe/Vaduz" - zones["LK"] = "Asia/Colombo" - zones["LR"] = "Africa/Monrovia" - zones["LS"] = "Africa/Maseru" - zones["LT"] = "Europe/Vilnius" - zones["LU"] = "Europe/Luxembourg" - zones["LV"] = "Europe/Riga" - zones["LY"] = "Africa/Tripoli" - zones["MA"] = "Africa/Casablanca" - zones["MC"] = "Europe/Monaco" - zones["MD"] = "Europe/Chisinau" - zones["MDT"] = "America/Denver" - zones["ME"] = "Europe/Podgorica" - zones["MF"] = "America/Marigot" - zones["MG"] = "Indian/Antananarivo" - zones["MH"] = "Pacific/Majuro" - zones["MK"] = "Europe/Skopje" - zones["ML"] = "Africa/Timbuktu" - zones["MM"] = "Asia/Yangon" - zones["MN"] = "Asia/Ulaanbaatar" - zones["MO"] = "Asia/Macau" - zones["MP"] = "Pacific/Saipan" - zones["MQ"] = "America/Martinique" - zones["MR"] = "Africa/Nouakchott" - zones["MS"] = "America/Montserrat" - zones["MST"] = "America/Denver" - zones["MT"] = "Europe/Malta" - zones["MU"] = "Indian/Mauritius" - zones["MV"] = "Indian/Maldives" - zones["MW"] = "Africa/Blantyre" - zones["MX"] = "America/Mexico_City" - zones["MY"] = "Asia/Kuala_Lumpur" - zones["MZ"] = "Africa/Maputo" - zones["NA"] = "Africa/Windhoek" - zones["NC"] = "Pacific/Noumea" - zones["NE"] = "Africa/Niamey" - zones["NF"] = "Pacific/Norfolk" - zones["NG"] = "Africa/Lagos" - zones["NI"] = "America/Managua" - zones["NL"] = "Europe/Amsterdam" - zones["NO"] = "Europe/Oslo" - zones["NP"] = "Asia/Kathmandu" - zones["NR"] = "Pacific/Nauru" - zones["NU"] = "Pacific/Niue" - zones["NZ"] = "Pacific/Auckland" - zones["OM"] = "Asia/Muscat" - zones["PA"] = "America/Panama" - zones["PDT"] = "America/Los_Angeles" - zones["PE"] = "America/Lima" - zones["PF"] = "Pacific/Tahiti" - zones["PG"] = "Pacific/Port_Moresby" - zones["PH"] = "Asia/Manila" - zones["PK"] = "Asia/Karachi" - zones["PL"] = "Europe/Warsaw" - zones["PM"] = "America/Miquelon" - zones["PN"] = "Pacific/Pitcairn" - zones["PR"] = "America/Puerto_Rico" - zones["PS"] = "Asia/Gaza" - zones["PST"] = "America/Los_Angeles" - zones["PT"] = "Europe/Lisbon" - zones["PW"] = "Pacific/Palau" - zones["PY"] = "America/Asuncion" - zones["QA"] = "Asia/Qatar" - zones["RE"] = "Indian/Reunion" - zones["RO"] = "Europe/Bucharest" - zones["RS"] = "Europe/Belgrade" - zones["RU"] = "Europe/Moscow" - zones["RW"] = "Africa/Kigali" - zones["SA"] = "Asia/Riyadh" - zones["SB"] = "Pacific/Guadalcanal" - zones["SC"] = "Indian/Mahe" - zones["SD"] = "Africa/Khartoum" - zones["SE"] = "Europe/Stockholm" - zones["SG"] = "Asia/Singapore" - zones["SH"] = "Atlantic/St_Helena" - zones["SI"] = "Europe/Ljubljana" - zones["SJ"] = "Atlantic/Jan_Mayen" - zones["SK"] = "Europe/Bratislava" - zones["SL"] = "Africa/Freetown" - zones["SM"] = "Europe/San_Marino" - zones["SN"] = "Africa/Dakar" - zones["SO"] = "Africa/Mogadishu" - zones["SR"] = "America/Paramaribo" - zones["SS"] = "Africa/Juba" - zones["ST"] = "Africa/Sao_Tome" - zones["SV"] = "America/El_Salvador" - zones["SX"] = "America/Lower_Princes" - zones["SY"] = "Asia/Damascus" - zones["SZ"] = "Africa/Mbabane" - zones["TC"] = "America/Grand_Turk" - zones["TD"] = "Africa/Ndjamena" - zones["TF"] = "Indian/Kerguelen" - zones["TG"] = "Africa/Lome" - zones["TH"] = "Asia/Bangkok" - zones["TJ"] = "Asia/Dushanbe" - zones["TK"] = "Pacific/Fakaofo" - zones["TL"] = "Asia/Dili" - zones["TM"] = "Asia/Ashgabat" - zones["TN"] = "Africa/Tunis" - zones["TO"] = "Pacific/Tongatapu" - zones["TR"] = "Europe/Istanbul" - zones["TT"] = "America/Port_of_Spain" - zones["TV"] = "Pacific/Funafuti" - zones["TW"] = "Asia/Taipei" - zones["TZ"] = "Africa/Dar_es_Salaam" - zones["UA"] = "Europe/Kiev" - zones["UG"] = "Africa/Kampala" - zones["UK"] = "Europe/London" - zones["UM"] = "Pacific/Wake" - zones["US"] = "America/New_York" - zones["UTC"] = "UTC" - zones["UY"] = "America/Montevideo" - zones["UZ"] = "Asia/Tashkent" - zones["VA"] = "Europe/Vatican" - zones["VC"] = "America/St_Vincent" - zones["VE"] = "America/Caracas" - zones["VG"] = "America/Tortola" - zones["VI"] = "America/St_Thomas" - zones["VN"] = "Asia/Ho_Chi_Minh" - zones["VU"] = "Pacific/Efate" - zones["WF"] = "Pacific/Wallis" - zones["WS"] = "Pacific/Apia" - zones["YE"] = "Asia/Aden" - zones["YT"] = "Indian/Mayotte" - zones["ZA"] = "Africa/Johannesburg" - zones["ZM"] = "Africa/Lusaka" - zones["ZULU"] = "Zulu" - zones["ZW"] = "Africa/Harare" - ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !zones.containsKey(it) } - .forEach { tz -> zones[tz] = tz } - COUNTRIES_MAP = Collections.unmodifiableMap(zones) - } } override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { From 65e221a4f6039b48e7ef17738ed12d2247855040 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:27:09 -0800 Subject: [PATCH 593/842] Improved IP address regex. --- src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt | 5 ++--- .../kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 703f138..83caf6e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -54,9 +54,8 @@ class Lookup : AbstractModule() { event.respondWith(nslookup(args).prependIndent()) } catch (ignore: UnknownHostException) { if (args.matches( - ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + - "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex() + ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + .toRegex() ) ) { try { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index b3e3dff..b0fdfcd 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -45,8 +45,11 @@ class LookupTest { @Test @Throws(Exception::class) fun testLookup() { - val result = nslookup("apple.com") + var result = nslookup("apple.com") assertThat(result, "lookup(apple.com)").contains("17.253.144.10") + + result = nslookup("204.122.17.9") + assertThat(result, "lookup(204.122.17.9)").contains("nix3.thauvin.us") } @Test From 54e6e6dd87075b76e44f38be7054c03218c8c9e1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:27:32 -0800 Subject: [PATCH 594/842] Cleaned up. --- src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt | 4 ++-- src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index bc527f0..716d829 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -60,8 +60,8 @@ class Addons { if (isEnabled) { modules.add(this) - modulesNames.add(this.javaClass.simpleName) - names.addAll(this.commands) + modulesNames.add(javaClass.simpleName) + names.addAll(commands) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 8083c74..16bd365 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -110,7 +110,7 @@ class Pinboard { */ private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( - this.toInstant().truncatedTo(ChronoUnit.SECONDS), + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() ).format(DateTimeFormatter.ISO_INSTANT) } @@ -119,7 +119,7 @@ class Pinboard { * Returns the pinboard.in extended attribution line. */ private fun EntryLink.postedBy(ircServer: String): String { - return "Posted by ${this.nick} on ${this.channel} ( $ircServer )" + return "Posted by $nick on $channel ( $ircServer )" } } From db1a1a9474ca711f1d6b67327b702626e03f4b2a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:30:58 -0800 Subject: [PATCH 595/842] Removed jacoco. Added isNonStable function for versions plugin. --- build.gradle | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index 3d1220c..779afda 100644 --- a/build.gradle +++ b/build.gradle @@ -2,8 +2,7 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.19.0-RC1' - id 'jacoco' + id 'io.gitlab.arturbosch.detekt' version '1.19.0-RC2' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.6.0' @@ -19,11 +18,17 @@ final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' final def semverProcessor = "net.thauvin.erik:semver:1.2.0" +def isNonStable = { String version -> + def stableKeyword = ['RELEASE', 'FINAL', 'GA'].any { it -> version.toUpperCase().contains(it) } + def regex = /^[0-9,.v-]+(-r)?$/ + return !stableKeyword && !(version ==~ regex) +} + mainClassName = packageName + '.Mobibot' ext.versions = [ log4j: '2.14.1', - pmd : '6.40.0', + pmd : '6.41.0', ] repositories { @@ -46,7 +51,7 @@ dependencies { implementation 'com.google.guava:guava:31.0.1-jre' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-RC' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" @@ -56,7 +61,7 @@ dependencies { implementation 'commons-net:commons-net:3.8.0' implementation 'com.rometools:rome:1.16.0' - implementation 'com.squareup.okhttp3:okhttp:4.9.2' + implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'com.google.code.gson:gson:2.8.9' @@ -113,6 +118,12 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { } } +tasks.named("dependencyUpdates").configure { + rejectVersionIf { + isNonStable(it.candidate.version) + } +} + pmd { toolVersion = versions.pmd ignoreFailures = true @@ -162,18 +173,6 @@ sonarqube { } } -jacoco { - toolVersion = '0.8.7' -} - -jacocoTestReport { - dependsOn test - reports { - html.required = true - xml.required = true - } -} - tasks.sonarqube { dependsOn 'koverReport' } From db6f322ce5987e2411ba88c632b7982499acb49c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 28 Nov 2021 23:32:23 -0800 Subject: [PATCH 596/842] Baseline update. --- config/detekt/baseline.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 3bcc3cb..0188ff5 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -9,6 +9,7 @@ <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</ID> + <ID>LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> @@ -21,7 +22,6 @@ <ID>MagicNumber:Ignore.kt$Ignore$8</ID> <ID>MagicNumber:Mobibot.kt$Mobibot$8</ID> <ID>MagicNumber:Modules.kt$Modules$7</ID> - <ID>MagicNumber:Recap.kt$Recap.Companion$10</ID> <ID>MagicNumber:StockQuote.kt$StockQuote.Companion$10</ID> <ID>MagicNumber:Tell.kt$Tell$50</ID> <ID>MagicNumber:Tell.kt$Tell$7</ID> @@ -32,7 +32,6 @@ <ID>MagicNumber:Utils.kt$Utils$30</ID> <ID>MagicNumber:Utils.kt$Utils$365</ID> <ID>MagicNumber:Utils.kt$Utils$7</ID> - <ID>MagicNumber:View.kt$View$6</ID> <ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID> <ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID> <ID>MagicNumber:Weather2.kt$Weather2.Companion$404</ID> @@ -40,7 +39,6 @@ <ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID> <ID>MagicNumber:WorldTime.kt$WorldTime$14</ID> <ID>MagicNumber:WorldTime.kt$WorldTime$4</ID> - <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> @@ -56,7 +54,6 @@ <ID>NestedBlockDepth:Posting.kt$Posting$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:Tell.kt$Tell$ fun send(event: GenericUserEvent)</ID> - <ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun load(file: String): List<TellMessage></ID> <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?)</ID> <ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @Throws(TwitterException::class, IOException::class) @JvmStatic fun main(args: Array<String>)</ID> From fd39c25c7d1702504e2cb6b05c64873f45d83923 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 5 Dec 2021 22:53:29 -0800 Subject: [PATCH 597/842] Cleanup. --- .idea/runConfigurations.xml | 10 ---------- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 3 ++- .../erik/mobibot/commands/links/LinksMgr.kt | 19 +++++++++++++++---- .../erik/mobibot/commands/tell/Tell.kt | 1 - .../mobibot/commands/tell/TellMessagesMgr.kt | 6 +++++- .../erik/mobibot/entries/EntriesUtils.kt | 4 ++++ .../thauvin/erik/mobibot/entries/FeedsMgr.kt | 2 ++ .../erik/mobibot/modules/CryptoPrices.kt | 1 + .../net/thauvin/erik/mobibot/modules/Dice.kt | 1 + .../thauvin/erik/mobibot/modules/WorldTime.kt | 8 ++++++-- .../net/thauvin/erik/mobibot/msg/Message.kt | 1 - 13 files changed, 38 insertions(+), 22 deletions(-) delete mode 100644 .idea/runConfigurations.xml diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 797acea..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="RunConfigurationProducerService"> - <option name="ignoredProducers"> - <set> - <option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" /> - </set> - </option> - </component> -</project> \ No newline at end of file diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index bee3340..62e0fca 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -242,8 +242,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro } companion object { - @Throws(Exception::class) @JvmStatic + @Throws(Exception::class) fun main(args: Array<String>) { // Set up the command line options val options = Options() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt index a96b491..345943b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt @@ -59,8 +59,8 @@ object TwitterOAuth { * * @param args The consumerKey and consumerSecret should be passed as arguments. */ - @Throws(TwitterException::class, IOException::class) @JvmStatic + @Throws(TwitterException::class, IOException::class) fun main(args: Array<String>) { if (args.size == 2) { with(TwitterFactory.getSingleton()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 835125d..f489194 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -182,7 +182,8 @@ object Utils { /** * Return the last item of a list of strings or empty if none. */ - fun List<String>.lastOrEmpty() : String { + @JvmStatic + fun List<String>.lastOrEmpty(): String { return if (this.size >= 2) { this.last() } else diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 17b06e1..07abbf5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -70,16 +70,27 @@ class LinksMgr : AbstractCommand() { const val TAGS_PROP = "tags" const val TAG_MATCH = ", *| +" - /** Entries array **/ + /** + * Entries array + */ + @JvmField val entries = Entries() - /** Pinboard handler. **/ + /** + * Pinboard handler. + */ + @JvmField val pinboard = Pinboard() - /** Twitter handler. **/ + /** + * Twitter handler. + */ + @JvmField val twitter = Twitter() - /** Let the user know if the entries are too old to be modified. **/ + /** + * Let the user know if the entries are too old to be modified. + */ @JvmStatic fun isUpToDate(event: GenericMessageEvent): Boolean { if (entries.lastPubDate != today()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index b18327f..c3a0b65 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -81,7 +81,6 @@ class Tell(private val serialObject: String) : AbstractCommand() { * Cleans the messages queue. */ private fun clean(): Boolean { - // if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.") return TellMessagesMgr.clean(messages, maxDays.toLong()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index 5960b63..e61b814 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -48,12 +48,14 @@ import kotlin.io.path.exists * The Tell Messages Manager. */ object TellMessagesMgr { - val logger: Logger = LoggerFactory.getLogger(TellMessagesMgr::class.java) + private val logger: Logger = LoggerFactory.getLogger(TellMessagesMgr::class.java) /** * Cleans the messages queue. */ + @JvmStatic fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean { + if (logger.isDebugEnabled) logger.debug("Cleaning the messages.") val today = LocalDateTime.now(Clock.systemUTC()) return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) } } @@ -61,6 +63,7 @@ object TellMessagesMgr { /** * Loads the messages. */ + @JvmStatic fun load(file: String): List<TellMessage> { val serialFile = Paths.get(file) if (serialFile.exists()) { @@ -84,6 +87,7 @@ object TellMessagesMgr { /** * Saves the messages. */ + @JvmStatic fun save(file: String, messages: List<TellMessage?>?) { try { BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 4c4c29c..02cfd4d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -42,17 +42,20 @@ object EntriesUtils { /** * Build link label based on its index. e.g: L1 */ + @JvmStatic fun buildLinkLabel(index: Int): String = Constants.LINK_CMD + (index + 1) /** * Builds an entry's comment for display on the channel. */ + @JvmStatic fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = ("${buildLinkLabel(entryIndex)}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** * Builds an entry's link for display on the channel. */ + @JvmStatic @JvmOverloads fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { val buff = StringBuilder().append(buildLinkLabel(entryIndex)).append(": ") @@ -75,6 +78,7 @@ object EntriesUtils { /** * Build an entry's tags/categories for display on the channel. */ + @JvmStatic fun buildTags(entryIndex: Int, entry: EntryLink): String = buildLinkLabel(entryIndex) + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ") } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt index ccbcd46..5891720 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt @@ -68,6 +68,7 @@ class FeedsMgr private constructor() { /** * Loads the current feed. */ + @JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String { entries.links.clear() @@ -113,6 +114,7 @@ class FeedsMgr private constructor() { /** * Saves the feeds. */ + @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml) { if (logger.isDebugEnabled) logger.debug("Saving the feeds...") if (entries.logsDir.isNotBlank()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 9721055..35b0b86 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -83,6 +83,7 @@ class CryptoPrices : ThreadedModule() { /** * Get current market price. */ + @JvmStatic fun currentPrice(args: List<String>): CryptoPrice { return if (args.size == 2) spotPrice(args[0], args[1]) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 4dced28..848df2c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -76,6 +76,7 @@ class Dice : AbstractModule() { // Dice faces private val DICE_FACES = arrayOf("", "⚀", "⚁", "⚂", "⚃", "⚄", "⚅") + @JvmStatic fun winLoseOrTie(bot: Int, player: Int): Result { return when { bot > player -> { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 7abe820..b5c80eb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -46,10 +46,14 @@ import java.time.temporal.ChronoField */ class WorldTime : AbstractModule() { companion object { - // Beats (Internet Time) keyword + /** + * Beats (Internet Time) keyword + */ const val BEATS_KEYWORD = ".beats" - // Supported countries + /** + * Supported countries + */ val COUNTRIES_MAP = buildMap<String, String> { put("AG", "America/Antigua") put("AI", "America/Anguilla") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 471c310..1d2e5bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -45,7 +45,6 @@ open class Message @JvmOverloads constructor( ) { companion object { var DEFAULT_COLOR = Constants.EMPTY - } init { From 562adec17760842174548766b0c23527604b093d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 5 Dec 2021 22:56:36 -0800 Subject: [PATCH 598/842] Added more tests. --- .../net/thauvin/erik/mobibot/AddonsTest.kt | 86 +++++++++++++++++ .../erik/mobibot/commands/RecapTest.kt | 13 ++- .../mobibot/commands/tell/TellMessageTest.kt | 3 - .../commands/tell/TellMessagesMgrTest.kt | 88 ++++++++++++++++++ .../erik/mobibot/entries/EntriesUtilsTest.kt | 92 +++++++++++++++++++ .../erik/mobibot/entries/FeedMgrTest.kt | 14 +-- .../thauvin/erik/mobibot/msg/TestMessage.kt | 10 ++ 7 files changed, 293 insertions(+), 13 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt new file mode 100644 index 0000000..b618dc7 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -0,0 +1,86 @@ +/* + * AddonsTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import assertk.assertThat +import assertk.assertions.containsExactly +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.commands.ChannelFeed +import net.thauvin.erik.mobibot.commands.Cycle +import net.thauvin.erik.mobibot.commands.Die +import net.thauvin.erik.mobibot.commands.Ignore +import net.thauvin.erik.mobibot.commands.links.Comment +import net.thauvin.erik.mobibot.commands.links.View +import net.thauvin.erik.mobibot.modules.Joke +import net.thauvin.erik.mobibot.modules.RockPaperScissors +import net.thauvin.erik.mobibot.modules.Twitter +import org.testng.annotations.Test +import java.util.* + +class AddonsTest { + private val addons = Addons() + + @Test + fun addTest() { + val p = Properties() + + // Modules + addons.add(Joke(), p) + addons.add(RockPaperScissors(), p) + addons.add(Twitter(), p) // no properties, disabled. + assertThat(addons.modules.size, "modules = 2").isEqualTo(2) + + assertThat(addons.modulesNames, "module names").containsExactly("Joke", "RockPaperScissors") + + // Commands + addons.add(View(), p) + addons.add(Comment(), p) + addons.add(Cycle(), p) + addons.add(Die(), p) // invisible + addons.add(ChannelFeed("channel"), p) // no properties, disabled + addons.add(Ignore(), p.apply { put(Ignore.IGNORE_PROP, "nick") }) + assertThat(addons.commands.size, "commands = 4").isEqualTo(5) + + assertThat(addons.ops, "ops").containsExactly("cycle") + + assertThat(addons.names, "names").containsExactly( + "joke", + "rock", + "paper", + "scissors", + "view", + "comment", + "ignore" + ) + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index d347f1a..2b5ebac 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -44,11 +44,18 @@ class RecapTest { @Test fun storeRecapTest() { for (i in 1..20) { - Recap.storeRecap("sender$i", "test $1", false) + Recap.storeRecap("sender$i", "test $i", false) } - assertThat(Recap.recaps).all { + assertThat(Recap.recaps, "recap first and last").all { size().isEqualTo(Recap.MAX_RECAPS) - prop(MutableList<String>::last).contains("sender20") + prop(MutableList<String>::first) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) + prop(MutableList<String>::last) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) } + + Recap.storeRecap("sender", "test action", true) + assertThat(Recap.recaps.last()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 808ea97..6618f90 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -68,9 +68,6 @@ class TellMessageTest { tellMessage.isReceived = false assertThat(tellMessage.receptionDate, "reception date not set").isEqualTo(LocalDateTime.MIN) tellMessage.isReceived = true - assertThat(tellMessage.isReceived, "is received").isTrue() assertThat(isValidDate(tellMessage.receptionDate), "received is valid date/time").isTrue() - tellMessage.isNotified = true - assertThat(tellMessage.isNotified, "is notified").isTrue() } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt new file mode 100644 index 0000000..97363cd --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -0,0 +1,88 @@ +/* + * TellMessagesMgrTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.tell + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import org.testng.annotations.AfterClass +import org.testng.annotations.BeforeClass +import org.testng.annotations.Test +import java.time.LocalDateTime +import kotlin.io.path.createTempFile +import kotlin.io.path.deleteIfExists +import kotlin.io.path.fileSize + +class TellMessagesMgrTest { + private val testFile = createTempFile(suffix = ".ser") + private val maxDays = 10L + private val testMessages = mutableListOf<TellMessage>().apply { + for (i in 0..5) { + this.add(i, TellMessage("sender$i", "recipient$i", "message $i")) + } + } + + @BeforeClass + fun saveTest() { + TellMessagesMgr.save(testFile.toAbsolutePath().toString(), testMessages) + assertThat(testFile.fileSize()).isGreaterThan(0) + } + + @AfterClass + fun afterClass() { + testFile.deleteIfExists() + } + + @Test + fun cleanTest() { + testMessages.add(TellMessage("sender", "recipient", "message").apply { + queued = LocalDateTime.now().minusDays(maxDays) + }) + val size = testMessages.size + assertThat(TellMessagesMgr.clean(testMessages, maxDays + 2), "maxDays = 12").isFalse() + assertThat(TellMessagesMgr.clean(testMessages, maxDays), "maxDays = 10").isTrue() + assertThat(testMessages.size, "size-- after clean").isEqualTo(size - 1) + } + + @Test + fun loadTest() { + val messages = TellMessagesMgr.load(testFile.toAbsolutePath().toString()) + for (i in messages.indices) { + assertThat(messages[i]).all { + prop(TellMessage::sender).isEqualTo(testMessages[i].sender) + prop(TellMessage::recipient).isEqualTo(testMessages[i].recipient) + prop(TellMessage::message).isEqualTo(testMessages[i].message) + } + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt new file mode 100644 index 0000000..ab4ec8e --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -0,0 +1,92 @@ +/* + * EntriesUtilsTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.entries + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildTags +import org.testng.annotations.Test + +class EntriesUtilsTest { + private val comment = EntryComment("comment", "nick") + private val links = buildList { + for (i in 0..5) { + add( + EntryLink( + "https://www.mobitopia.org/$i", + "Mobitopia$i", + "Skynx$i", + "JimH$i", + "#mobitopia$i", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) + ) + } + } + + @Test + fun buildLinkLabelTest() { + assertThat(buildLinkLabel(1)).isEqualTo("${Constants.LINK_CMD}2") + } + + @Test + fun buildCommentTest() { + assertThat(buildComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") + } + + @Test + fun buildLinkTest() { + for (i in links.indices) { + assertThat( + buildLink(i - 1, links[i]), "link $i" + ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") + } + + assertThat(links.first().addComment(comment), "add comment").isEqualTo(0) + assertThat(buildLink(0, links.first(), isView = true), "isView = true").contains("[+1]") + } + + @Test + fun buildTagsTest() { + for (i in links.indices) { + assertThat( + buildTags(i - 1, links[i]), "tag $i" + ).isEqualTo("L${i}T: Skynx$i, tag1, tag2, tag3, tag4, tag5") + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index 04c36a7..271fc4c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -81,13 +81,13 @@ class FeedMgrTest { with(entries.links.first()) { assertThat(nick, "first nick").isEqualTo("ErikT") assertThat(date, "first date").isEqualTo(Date(1635638400000L)) - comments.forEachIndexed { i, entryComment -> - assertThat(entryComment.comment, "comment ${i + 1}").endsWith("comment ${i + 1}.") - if (i == 0) { - assertThat(entryComment.nick, "comment ${i + 1} nick").isEqualTo("ErikT") - } else { - assertThat(entryComment.nick, "comment ${i + 1} nick").isEqualTo("Skynx") - } + assertThat(comments.first(), "first comment").all { + prop(EntryComment::comment).endsWith("comment 1.") + prop(EntryComment::nick).isEqualTo("ErikT") + } + assertThat(comments.last(), "last comment").all { + prop(EntryComment::comment).endsWith("comment 2.") + prop(EntryComment::nick).isEqualTo("Skynx") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt index 3d588c5..50f8843 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt @@ -71,6 +71,16 @@ class TestMessage { } } + @Test + fun testPrivateMessage() { + val msg = PrivateMessage("foo") + assertThat(msg).all { + prop(Message::isPrivate).isTrue() + prop(Message::isError).isFalse() + prop(Message::isNotice).isFalse() + } + } + @Test fun testPublicMessage() { val msg = PublicMessage("foo") From 0e80f22b4f67c58818eb314940625f7d2793141b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 5 Dec 2021 22:57:33 -0800 Subject: [PATCH 599/842] Added isEnabled function. --- .../erik/mobibot/commands/ChannelFeed.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 5ac8d7f..9610592 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -36,7 +36,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.FeedReader import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendMessage import org.pircbotx.hooks.types.GenericMessageEvent class ChannelFeed(channel: String) : AbstractCommand() { @@ -55,16 +54,16 @@ class ChannelFeed(channel: String) : AbstractCommand() { } override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - with(properties[FEED_PROP]) { - if (!isNullOrBlank()) { - runBlocking { - launch { - FeedReader(this@with, event).run() - } + if (isEnabled()) { + runBlocking { + launch { + FeedReader(properties[FEED_PROP]!!, event).run() } - } else { - event.sendMessage("There is no feed setup for this channel.") } } } + + override fun isEnabled(): Boolean { + return !properties[FEED_PROP].isNullOrBlank() + } } From 5dd1f89105fdf7d4c296059ec3171e6317b83c32 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 5 Dec 2021 22:57:58 -0800 Subject: [PATCH 600/842] Added AddComment(EntryComment). --- .../net/thauvin/erik/mobibot/entries/EntryLink.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index da8ef87..29f90d7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -94,12 +94,19 @@ class EntryLink( this.tags.addAll(tags) } + /** + * Adds a new comment + */ + fun addComment(comment: EntryComment): Int { + comments.add(comment) + return comments.lastIndex + } + /** * Adds a new comment. */ fun addComment(comment: String, nick: String): Int { - comments.add(EntryComment(comment, nick)) - return comments.lastIndex + return addComment(EntryComment(comment, nick)) } /** From db8e1c198c9f6487cdf2924c4c7c783663b261d3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 5 Dec 2021 22:58:27 -0800 Subject: [PATCH 601/842] Updated dependencies. --- .idea/jarRepositories.xml | 22 +++----- .idea/libraries-with-intellij-classes.xml | 65 ----------------------- .idea/misc.xml | 5 +- build.gradle | 8 +-- config/detekt/baseline.xml | 16 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 6 files changed, 20 insertions(+), 98 deletions(-) delete mode 100644 .idea/libraries-with-intellij-classes.xml diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 7164ee5..501ee49 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -14,32 +14,22 @@ <remote-repository> <option name="id" value="MavenLocal" /> <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$" /> + <option name="url" value="file:$MAVEN_REPOSITORY$/" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven2" /> + <option name="name" value="maven2" /> + <option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" /> </remote-repository> <remote-repository> <option name="id" value="MavenRepo" /> <option name="name" value="MavenRepo" /> <option name="url" value="https://repo.maven.apache.org/maven2/" /> </remote-repository> - <remote-repository> - <option name="id" value="maven" /> - <option name="name" value="maven" /> - <option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$/" /> - </remote-repository> <remote-repository> <option name="id" value="maven" /> <option name="name" value="maven" /> <option name="url" value="https://jitpack.io" /> </remote-repository> - <remote-repository> - <option name="id" value="maven" /> - <option name="name" value="maven" /> - <option name="url" value="https://packages.jetbrains.team/maven/p/ij/intellij-dependencies" /> - </remote-repository> </component> </project> \ No newline at end of file diff --git a/.idea/libraries-with-intellij-classes.xml b/.idea/libraries-with-intellij-classes.xml deleted file mode 100644 index 9fa3156..0000000 --- a/.idea/libraries-with-intellij-classes.xml +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="libraries-with-intellij-classes"> - <option name="intellijApiContainingLibraries"> - <list> - <LibraryCoordinatesState> - <option name="artifactId" value="ideaIU" /> - <option name="groupId" value="com.jetbrains.intellij.idea" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="ideaIU" /> - <option name="groupId" value="com.jetbrains" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="ideaIC" /> - <option name="groupId" value="com.jetbrains.intellij.idea" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="ideaIC" /> - <option name="groupId" value="com.jetbrains" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="pycharmPY" /> - <option name="groupId" value="com.jetbrains.intellij.pycharm" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="pycharmPY" /> - <option name="groupId" value="com.jetbrains" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="pycharmPC" /> - <option name="groupId" value="com.jetbrains.intellij.pycharm" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="pycharmPC" /> - <option name="groupId" value="com.jetbrains" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="clion" /> - <option name="groupId" value="com.jetbrains.intellij.clion" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="clion" /> - <option name="groupId" value="com.jetbrains" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="riderRD" /> - <option name="groupId" value="com.jetbrains.intellij.rider" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="riderRD" /> - <option name="groupId" value="com.jetbrains" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="goland" /> - <option name="groupId" value="com.jetbrains.intellij.goland" /> - </LibraryCoordinatesState> - <LibraryCoordinatesState> - <option name="artifactId" value="goland" /> - <option name="groupId" value="com.jetbrains" /> - </LibraryCoordinatesState> - </list> - </option> - </component> -</project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1503b42..cbfe0de 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,8 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="EntryPointsManager"> - <pattern value="net.thauvin.erik.mobibot.modules.War" method="War" /> - </component> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="15" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="17" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 779afda..73b54c1 100644 --- a/build.gradle +++ b/build.gradle @@ -2,12 +2,12 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.39.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.19.0-RC2' + id 'io.gitlab.arturbosch.detekt' version '1.19.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.6.0' id 'org.jetbrains.kotlin.kapt' version '1.6.0' - id 'org.jetbrains.kotlinx.kover' version '0.4.2' + id 'org.jetbrains.kotlinx.kover' version '0.4.3' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -19,7 +19,7 @@ final def deployDir = 'deploy' final def semverProcessor = "net.thauvin.erik:semver:1.2.0" def isNonStable = { String version -> - def stableKeyword = ['RELEASE', 'FINAL', 'GA'].any { it -> version.toUpperCase().contains(it) } + def stableKeyword = ['RELEASE', 'FINAL', 'GA', 'JRE'].any { it -> version.toUpperCase().contains(it) } def regex = /^[0-9,.v-]+(-r)?$/ return !stableKeyword && !(version ==~ regex) } @@ -71,7 +71,7 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'org.json:json:20210307' + implementation 'org.json:json:20211205' implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.twitter4j:twitter4j-core:4.0.7' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 0188ff5..7f49882 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,10 +2,10 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> - <ID>ComplexMethod:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>ComplexMethod:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> <ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>LongMethod:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> - <ID>LongMethod:Mobibot.kt$Mobibot.Companion$@Throws(Exception::class) @JvmStatic fun main(args: Array<String>)</ID> + <ID>LongMethod:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) fun main(args: Array<String>)</ID> <ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</ID> @@ -46,17 +46,17 @@ <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> - <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> - <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> + <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID> <ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:Posting.kt$Posting$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>NestedBlockDepth:Tell.kt$Tell$ fun send(event: GenericUserEvent)</ID> - <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun load(file: String): List<TellMessage></ID> - <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?)</ID> - <ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @Throws(TwitterException::class, IOException::class) @JvmStatic fun main(args: Array<String>)</ID> + <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ @JvmStatic fun load(file: String): List<TellMessage></ID> + <ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ @JvmStatic fun save(file: String, messages: List<TellMessage?>?)</ID> + <ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @JvmStatic @Throws(TwitterException::class, IOException::class) fun main(args: Array<String>)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> <ID>ReturnCount:Addons.kt$Addons$ fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean</ID> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102..84d1f85 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 99a27c2769c61b3096b0843204322fef42f243b6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 17 Dec 2021 01:37:06 -0800 Subject: [PATCH 602/842] Upgraded to log4j 2.16.0 and Kotlin 1.6.10. --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 73b54c1..a621c9f 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.19.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.6.0' - id 'org.jetbrains.kotlin.kapt' version '1.6.0' + id 'org.jetbrains.kotlin.jvm' version '1.6.10' + id 'org.jetbrains.kotlin.kapt' version '1.6.10' id 'org.jetbrains.kotlinx.kover' version '0.4.3' id 'org.sonarqube' version '3.3' id 'pmd' @@ -27,7 +27,7 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.14.1', + log4j: '2.16.0', pmd : '6.41.0', ] From 8903d67e9c490d5d344264c91bf98007aba19711 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 17 Dec 2021 01:41:22 -0800 Subject: [PATCH 603/842] Added more tests. --- src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../net/thauvin/erik/mobibot/commands/RecapTest.kt | 2 +- .../erik/mobibot/commands/tell/TellMessagesMgrTest.kt | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index b618dc7..cd035cd 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -45,7 +45,7 @@ import net.thauvin.erik.mobibot.modules.Joke import net.thauvin.erik.mobibot.modules.RockPaperScissors import net.thauvin.erik.mobibot.modules.Twitter import org.testng.annotations.Test -import java.util.* +import java.util.Properties class AddonsTest { private val addons = Addons() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 2b5ebac..3d74205 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -34,8 +34,8 @@ package net.thauvin.erik.mobibot.commands import assertk.all import assertk.assertThat -import assertk.assertions.contains import assertk.assertions.isEqualTo +import assertk.assertions.matches import assertk.assertions.prop import assertk.assertions.size import org.testng.annotations.Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index 97363cd..8279331 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -34,7 +34,12 @@ package net.thauvin.erik.mobibot.commands.tell import assertk.all import assertk.assertThat -import assertk.assertions.* +import assertk.assertions.index +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isGreaterThan +import assertk.assertions.isTrue +import assertk.assertions.prop import org.testng.annotations.AfterClass import org.testng.annotations.BeforeClass import org.testng.annotations.Test @@ -78,7 +83,7 @@ class TellMessagesMgrTest { fun loadTest() { val messages = TellMessagesMgr.load(testFile.toAbsolutePath().toString()) for (i in messages.indices) { - assertThat(messages[i]).all { + assertThat(messages).index(i).all { prop(TellMessage::sender).isEqualTo(testMessages[i].sender) prop(TellMessage::recipient).isEqualTo(testMessages[i].recipient) prop(TellMessage::message).isEqualTo(testMessages[i].message) From 2573c7d59661e1ba80cbd4fcd383bb324cee5bf6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 17 Dec 2021 01:42:02 -0800 Subject: [PATCH 604/842] Upgraded to Gradle 7.3.2 --- .idea/misc.xml | 4 ++++ .idea/runConfigurations.xml | 10 ++++++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- version.properties | 6 +++--- 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 .idea/runConfigurations.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index cbfe0de..76aaf0c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> + <component name="EntryPointsManager"> + <pattern value="net.thauvin.erik.mobibot.modules.War" /> + <pattern value="net.thauvin.erik.mobibot.modules.War" method="War" /> + </component> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="17" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RunConfigurationProducerService"> + <option name="ignoredProducers"> + <set> + <option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" /> + </set> + </option> + </component> +</project> \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 84d1f85..d2880ba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/version.properties b/version.properties index d3947a4..2291461 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Nov 14 21:44:14 PST 2021 -version.buildmeta=2262 +#Fri Dec 17 01:31:11 PST 2021 +version.buildmeta=2422 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+2262 +version.semver=0.8.0-beta+2422 From bc2a427f48cc8f24f3bb07de4d47c2b37f9ba2c6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 19 Dec 2021 12:42:18 -0800 Subject: [PATCH 605/842] Updated to Log4j 2.17. --- build.gradle | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index a621c9f..240d0da 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.16.0', + log4j: '2.17.0', pmd : '6.41.0', ] diff --git a/version.properties b/version.properties index 2291461..dcff800 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Dec 17 01:31:11 PST 2021 -version.buildmeta=2422 +#Sun Dec 19 12:39:10 PST 2021 +version.buildmeta=2423 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+2422 +version.semver=0.8.0-beta+2423 From dfc711b1802c64b147dcbacd44dcbd7ded2f8ca6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 1 Jan 2022 06:37:44 -0800 Subject: [PATCH 606/842] Updated dependencies. --- build.gradle | 10 +++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- version.properties | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 240d0da..143d5f1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.39.0' + id 'com.github.ben-manes.versions' version '0.40.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.19.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.6.10' id 'org.jetbrains.kotlin.kapt' version '1.6.10' - id 'org.jetbrains.kotlinx.kover' version '0.4.3' + id 'org.jetbrains.kotlinx.kover' version '0.4.4' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -27,7 +27,7 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.17.0', + log4j: '2.17.1', pmd : '6.41.0', ] @@ -51,7 +51,7 @@ dependencies { implementation 'com.google.guava:guava:31.0.1-jre' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-RC' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" @@ -60,7 +60,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.5.0' implementation 'commons-net:commons-net:3.8.0' - implementation 'com.rometools:rome:1.16.0' + implementation 'com.rometools:rome:1.18.0' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'net.aksingh:owm-japis:2.5.3.0' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d2880ba..2e6e589 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/version.properties b/version.properties index dcff800..41eca0f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Dec 19 12:39:10 PST 2021 -version.buildmeta=2423 +#Sat Jan 01 06:34:39 PST 2022 +version.buildmeta=2428 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+2423 +version.semver=0.8.0-beta+2428 From 12bc76406bb6ac062894691d09da716496d04e72 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 1 Jan 2022 07:08:08 -0800 Subject: [PATCH 607/842] Updated copyright and licenses. --- LICENSE.txt | 2 +- licenses/LICENSE.txt | 2 +- licenses/OstermillerUtil License.txt | 16 ---------------- licenses/jsoup License.txt | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 2 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 2 +- .../net/thauvin/erik/mobibot/TwitterTimer.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../thauvin/erik/mobibot/commands/ChannelFeed.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../erik/mobibot/commands/links/LinksMgr.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../thauvin/erik/mobibot/commands/links/Tags.kt | 2 +- .../thauvin/erik/mobibot/commands/links/View.kt | 2 +- .../thauvin/erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../mobibot/commands/tell/TellMessagesMgr.kt | 2 +- .../net/thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../thauvin/erik/mobibot/entries/EntriesUtils.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../net/thauvin/erik/mobibot/entries/FeedsMgr.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../thauvin/erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../thauvin/erik/mobibot/modules/StockQuote.kt | 2 +- .../erik/mobibot/modules/ThreadedModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Twitter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../net/thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 2 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../thauvin/erik/mobibot/ExceptionSanitizer.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../net/thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/RecapTest.kt | 2 +- .../erik/mobibot/commands/links/LinksMgrTest.kt | 2 +- .../erik/mobibot/commands/links/ViewTest.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 2 +- .../mobibot/commands/tell/TellMessagesMgrTest.kt | 2 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 2 +- .../erik/mobibot/entries/EntryLinkTest.kt | 2 +- .../thauvin/erik/mobibot/entries/FeedMgrTest.kt | 2 +- .../net/thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- .../mobibot/modules/CurrencyConverterTest.kt | 2 +- .../net/thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../net/thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../thauvin/erik/mobibot/modules/LookupTest.kt | 2 +- .../erik/mobibot/modules/ModuleExceptionTest.kt | 2 +- .../net/thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../thauvin/erik/mobibot/modules/TwitterTest.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2Test.kt | 2 +- .../thauvin/erik/mobibot/modules/WordTimeTest.kt | 2 +- .../net/thauvin/erik/mobibot/msg/TestMessage.kt | 2 +- 90 files changed, 89 insertions(+), 105 deletions(-) delete mode 100644 licenses/OstermillerUtil License.txt diff --git a/LICENSE.txt b/LICENSE.txt index 1cd28ec..085f7c7 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/licenses/LICENSE.txt b/licenses/LICENSE.txt index 1cd28ec..085f7c7 100644 --- a/licenses/LICENSE.txt +++ b/licenses/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) +Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/licenses/OstermillerUtil License.txt b/licenses/OstermillerUtil License.txt deleted file mode 100644 index 7b9549c..0000000 --- a/licenses/OstermillerUtil License.txt +++ /dev/null @@ -1,16 +0,0 @@ -License (http://ostermiller.org/utils/) - -Copyright (C) 2004-2010 Stephen Ostermiller -http://ostermiller.org/contact.pl?regarding=Java+Utilities - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -See the GNU General Public License (https://ostermiller.org/utils/license.html) for more details. diff --git a/licenses/jsoup License.txt b/licenses/jsoup License.txt index 4d95827..31b785d 100644 --- a/licenses/jsoup License.txt +++ b/licenses/jsoup License.txt @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2009-2018 Jonathan Hedley <jonathan@hedley.net> +Copyright (c) 2009-2022 Jonathan Hedley <jonathan@hedley.net> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index c286091..16102b4 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,7 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 716d829..e530ae8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index dfe25dc..f96e30b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index a61a6a7..8fc4f2a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 62e0fca..ef6f842 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 16bd365..9d46188 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt index 345943b..3df1c0a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt @@ -1,7 +1,7 @@ /* * TwitterOAuth.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt index 374f301..09bfa46 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterTimer.kt @@ -1,7 +1,7 @@ /* * TwitterTimer.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index f489194..38fafa7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 425f309..674fe0f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 9610592..cc80595 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 00d6638..8c6e777 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index df715c4..26f05c3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 2dc04ca..afd9bf4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 41065ff..27521ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index 7a85f4f..f1098fa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 36a4baf..9ed298c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index edd9cc9..8bd4b37 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index c8275dd..c864763 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 6fe1e36..3647bcc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 34b4aec..c03a221 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index e643858..fddb9be 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index f7fa866..2875e06 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 5b137ff..59f31c8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 07abbf5..8485be0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -1,7 +1,7 @@ /* * LinksMgr.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 1a43c03..670daeb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 2970033..7c0d904 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index e3b731e..f853cae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index c3a0b65..1100c99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index f1bdabd..a504c92 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index e61b814..bb2f8cd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgr.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index 78602f6..a775168 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 02cfd4d..023ec86 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index be442d5..b0b138a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 29f90d7..9177ebe 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt index 5891720..9060146 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt @@ -1,7 +1,7 @@ /* * FeedsMgr.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 372ef2d..dd5d3d5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 25ae072..29c0805 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 35b0b86..57e3dc4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 16687f6..275e99c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 848df2c..667a0b0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index efe52ec..a0658bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 26d7af3..ff5c030 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 83caf6e..f6ae628 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index cf06b4e..2f39854 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 3be959e..b4876de 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 9dba554..95eb5e2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index a1f17c4..240ea0e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt index b44aab1..269b24d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt @@ -1,7 +1,7 @@ /* * ThreadedModule.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index c813392..61fbd1b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -1,7 +1,7 @@ /* * Twitter.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index f5f1e16..bdc5de9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index b5c80eb..7160d15 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index f071918..ae5651c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 1d2e5bc..2e51c02 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index d2353b8..f88b8dd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 6edcb96..827b682 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 9df2770..71b2a5b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index cd035cd..33cca2e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index 1660df7..ba486f0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index b828d68..e0e6aa3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index b764ab6..9249006 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 76b5b95..70c6b28 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 0cc1b41..06bb589 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 3d74205..ea3432e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt index 12fccbd..551ee1d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt @@ -1,7 +1,7 @@ /* * LinksMgrTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index dae5cb4..f5f30be 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 6618f90..436b81d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index 8279331..a2563c3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index ab4ec8e..3b39be4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 94a6b7e..36244de 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index 271fc4c..da0877d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index b37a09c..3d125f8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index e5f5e52..ca676d3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 962e2c2..8ba5c3b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index dfebd9d..a4a84c8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index fd7b4c7..003e825 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 865a65f..65c259b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index b0fdfcd..b915357 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 5791867..37ba63b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index 34cbfbb..247bf94 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 499c28c..66e95d0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 8812c21..57dcb4c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 379e15a..29ae3c4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -1,7 +1,7 @@ /* * TwitterTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 27b95c2..315fa06 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index abe9416..09ac600 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt index 50f8843..ef4be2c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/TestMessage.kt @@ -1,7 +1,7 @@ /* * TestMessage.kt * - * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without From e4dd6d5254e0cd7c8fb483c4aefd1d80274a5f17 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sat, 8 Jan 2022 23:11:44 -0800 Subject: [PATCH 608/842] Added support for disabled-modules and disabled-commands properties. --- build.gradle | 4 +- config/detekt/baseline.xml | 3 +- properties/mobibot.properties | 5 +- .../net/thauvin/erik/mobibot/modules/War.java | 6 ++ .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 53 ++++++++------ .../net/thauvin/erik/mobibot/Mobibot.kt | 70 ++++++++++--------- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 8 ++- .../erik/mobibot/modules/AbstractModule.kt | 5 ++ .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 + .../erik/mobibot/modules/CryptoPrices.kt | 2 + .../erik/mobibot/modules/CurrencyConverter.kt | 2 + .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 + .../erik/mobibot/modules/GoogleSearch.kt | 2 + .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 + .../thauvin/erik/mobibot/modules/Lookup.kt | 2 + .../net/thauvin/erik/mobibot/modules/Ping.kt | 5 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 + .../erik/mobibot/modules/StockQuote.kt | 2 + .../thauvin/erik/mobibot/modules/Twitter.kt | 2 + .../thauvin/erik/mobibot/modules/Weather2.kt | 2 + .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 + .../net/thauvin/erik/mobibot/AddonsTest.kt | 38 +++++----- version.properties | 6 +- 23 files changed, 144 insertions(+), 83 deletions(-) diff --git a/build.gradle b/build.gradle index 143d5f1..f62606e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.40.0' + id 'com.github.ben-manes.versions' version '0.41.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.19.0' id 'java' @@ -78,7 +78,7 @@ dependencies { testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation "org.mockito:mockito-core:4.0.0" - testImplementation 'org.testng:testng:7.4.0' + testImplementation 'org.testng:testng:7.5' } test { diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 7f49882..5f0626e 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -42,7 +42,8 @@ <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> - <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> + <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand)</ID> + <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(module: AbstractModule)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 67326f3..28f5a8d 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -1,5 +1,5 @@ channel=#mobitopia -server=irc.freenode.net +server=irc.libera.chat #port=6667 login=mobibot nick=mobibot @@ -24,6 +24,9 @@ backlogs=http://www.mobitopia.org/mobibot/logs tell-max-days=5 tell-max-size=50 +#disabled-commands=die, ignore +#disabled-modules=dice, joke + # # Credentials for: http://pinboard.in/ # diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 16102b4..5565ecb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -75,6 +75,12 @@ public final class War extends AbstractModule { help.add(Utils.helpFormat("%c " + WAR_CMD)); } + @NotNull + @Override + public String getName() { + return "War"; + } + /** * {@inheritDoc} */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index e530ae8..4e95ad0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -31,7 +31,9 @@ */ package net.thauvin.erik.mobibot +import net.thauvin.erik.mobibot.Utils.notContains import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.LinksMgr import net.thauvin.erik.mobibot.modules.AbstractModule import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent @@ -40,7 +42,10 @@ import java.util.Properties /** * Modules and Commands addons. */ -class Addons { +class Addons(private val props: Properties) { + private val disabledModules = props.getProperty("disabled-modules", "").split(LinksMgr.TAG_MATCH.toRegex()) + private val disableCommands = props.getProperty("disabled-commands", "").split(LinksMgr.TAG_MATCH.toRegex()) + val commands: MutableList<AbstractCommand> = mutableListOf() val modules: MutableList<AbstractModule> = mutableListOf() val modulesNames: MutableList<String> = mutableListOf() @@ -50,18 +55,20 @@ class Addons { /** * Add a module with properties. */ - fun add(module: AbstractModule, props: Properties) { + fun add(module: AbstractModule) { with(module) { - if (hasProperties()) { - propertyKeys.forEach { - setProperty(it, props.getProperty(it, "")) + if (disabledModules.notContains(name, true)) { + if (hasProperties()) { + propertyKeys.forEach { + setProperty(it, props.getProperty(it, "")) + } } - } - if (isEnabled) { - modules.add(this) - modulesNames.add(javaClass.simpleName) - names.addAll(commands) + if (isEnabled) { + modules.add(this) + modulesNames.add(name) + names.addAll(commands) + } } } } @@ -69,20 +76,22 @@ class Addons { /** * Add a command with properties. */ - fun add(command: AbstractCommand, props: Properties) { + fun add(command: AbstractCommand) { with(command) { - if (properties.isNotEmpty()) { - properties.keys.forEach { - setProperty(it, props.getProperty(it, "")) + if (disableCommands.notContains(name, true)) { + if (properties.isNotEmpty()) { + properties.keys.forEach { + setProperty(it, props.getProperty(it, "")) + } } - } - if (isEnabled()) { - commands.add(this) - if (isVisible) { - if (isOpOnly) { - ops.add(name) - } else { - names.add(name) + if (isEnabled()) { + commands.add(this) + if (isVisible) { + if (isOpOnly) { + ops.add(name) + } else { + names.add(name) + } } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index ef6f842..02467d6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -112,7 +112,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro private val config: Configuration // Commands and Modules - private val addons = Addons() + private val addons: Addons // Tell module private val tell: Tell @@ -391,46 +391,48 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro pinboard.setApiToken(p.getProperty("pinboard-api-token", "")) } + addons = Addons(p) + // Load the commands - addons.add(ChannelFeed(channel.removePrefix("#")), p) - addons.add(Comment(), p) - addons.add(Cycle(), p) - addons.add(Die(), p) - addons.add(Ignore(), p) - addons.add(LinksMgr(), p) - addons.add(Me(), p) - addons.add(Msg(), p) - addons.add(Nick(), p) - addons.add(Posting(), p) - addons.add(Recap(), p) - addons.add(Say(), p) - addons.add(Tags(), p) + addons.add(ChannelFeed(channel.removePrefix("#"))) + addons.add(Comment()) + addons.add(Cycle()) + addons.add(Die()) + addons.add(Ignore()) + addons.add(LinksMgr()) + addons.add(Me()) + addons.add(Msg()) + addons.add(Nick()) + addons.add(Posting()) + addons.add(Recap()) + addons.add(Say()) + addons.add(Tags()) // Tell command tell = Tell("${logsDirPath}${nickname}.ser") - addons.add(tell, p) + addons.add(tell) - addons.add(LinksMgr.twitter, p) - addons.add(Users(), p) - addons.add(Versions(), p) - addons.add(View(), p) + addons.add(LinksMgr.twitter) + addons.add(Users()) + addons.add(Versions()) + addons.add(View()) // Load the modules - addons.add(Calc(), p) - addons.add(CryptoPrices(), p) - addons.add(CurrencyConverter(), p) - addons.add(Dice(), p) - addons.add(GoogleSearch(), p) - addons.add(Info(tell), p) - addons.add(Joke(), p) - addons.add(Lookup(), p) - addons.add(Modules(addons.modulesNames), p) - addons.add(Ping(), p) - addons.add(RockPaperScissors(), p) - addons.add(StockQuote(), p) - addons.add(Weather2(), p) - addons.add(WorldTime(), p) - addons.add(War(), p) + addons.add(Calc()) + addons.add(CryptoPrices()) + addons.add(CurrencyConverter()) + addons.add(Dice()) + addons.add(GoogleSearch()) + addons.add(Info(tell)) + addons.add(Joke()) + addons.add(Lookup()) + addons.add(Modules(addons.modulesNames)) + addons.add(Ping()) + addons.add(RockPaperScissors()) + addons.add(StockQuote()) + addons.add(Weather2()) + addons.add(WorldTime()) + addons.add(War()) // Sort the addons addons.sort() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 38fafa7..b68ee47 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -180,7 +180,7 @@ object Utils { } /** - * Return the last item of a list of strings or empty if none. + * Returns the last item of a list of strings or empty if none. */ @JvmStatic fun List<String>.lastOrEmpty(): String { @@ -190,6 +190,12 @@ object Utils { "" } + /** + * Returns {@code true} if the list does not contain the given string. + */ + @JvmStatic + fun List<String>.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } + /** * Obfuscates the given string. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index dd5d3d5..25e7c9d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -41,6 +41,11 @@ import org.pircbotx.hooks.types.GenericMessageEvent * The `Module` abstract class. */ abstract class AbstractModule { + /** + * The module name. + */ + abstract val name: String + /** * The module's commands, if any. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 29c0805..76f3786 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -46,6 +46,8 @@ import java.text.DecimalFormat class Calc : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(Calc::class.java) + override val name = "Calc" + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 57e3dc4..8cb3396 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -47,6 +47,8 @@ import java.io.IOException class CryptoPrices : ThreadedModule() { private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) + override val name = "CryptoPrices" + /** * Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price). */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 275e99c..f6de896 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -59,6 +59,8 @@ import javax.xml.XMLConstants class CurrencyConverter : ThreadedModule() { private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) + override val name = "CurrencyConverter" + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { synchronized(this) { if (pubDate != today()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 667a0b0..9e7725d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -40,6 +40,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent * The Dice module. */ class Dice : AbstractModule() { + override val name = "Dice" + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val botRoll = roll() val roll = roll() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index a0658bc..6adffdd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -55,6 +55,8 @@ import java.net.URL class GoogleSearch : ThreadedModule() { private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) + override val name = "GoogleSearch" + /** * Searches Google. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index ff5c030..6922698 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -54,6 +54,8 @@ import java.net.URL class Joke : ThreadedModule() { private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) + override val name = "Joke" + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { runBlocking { launch { run(channel, cmd, args, event) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index f6ae628..90a0316 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -48,6 +48,8 @@ import java.net.UnknownHostException class Lookup : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java) + override val name = "Lookup" + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.matches("(\\S.)+(\\S)+".toRegex())) { try { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index b4876de..0818120 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -39,9 +39,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent * The Ping module. */ class Ping : AbstractModule() { - /** - * {@inheritDoc} - */ + override val name = "Ping" + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { event.bot().sendIRC().action(channel, randomPing()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 95eb5e2..c092548 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -42,6 +42,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent * Simple module example in Kotlin. */ class RockPaperScissors : AbstractModule() { + override val name = "RockPaperScissors" + init { with(commands) { add(Hands.ROCK.name.lowercase()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 240ea0e..63d358c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -55,6 +55,8 @@ import java.net.URL class StockQuote : ThreadedModule() { private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) + override val name = "StockQuote" + /** * Returns the specified stock quote from Alpha Vantage. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 61fbd1b..74f0ba0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -57,6 +57,8 @@ class Twitter : ThreadedModule() { // Twitter auto-posts. private val entries: MutableSet<Int> = HashSet() + override val name = "Twitter" + /** * Add an entry to be posted on Twitter. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index bdc5de9..96ae93e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -57,6 +57,8 @@ import kotlin.math.roundToInt class Weather2 : ThreadedModule() { private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) + override val name = "Weather" + /** * Fetches the weather data from a specific city. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 7160d15..fc0edf6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -45,6 +45,8 @@ import java.time.temporal.ChronoField * The WorldTime module. */ class WorldTime : AbstractModule() { + override val name = "WorldTime" + companion object { /** * Beats (Internet Time) keyword diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 33cca2e..54346a9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -41,35 +41,43 @@ import net.thauvin.erik.mobibot.commands.Die import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.View +import net.thauvin.erik.mobibot.modules.Dice import net.thauvin.erik.mobibot.modules.Joke +import net.thauvin.erik.mobibot.modules.Lookup import net.thauvin.erik.mobibot.modules.RockPaperScissors import net.thauvin.erik.mobibot.modules.Twitter +import net.thauvin.erik.mobibot.modules.War import org.testng.annotations.Test import java.util.Properties class AddonsTest { - private val addons = Addons() + private val p = Properties().apply { + put("disabled-modules", "war,dice Lookup") + put("disabled-commands", "View | comment") + } + private val addons = Addons(p) @Test fun addTest() { - val p = Properties() - // Modules - addons.add(Joke(), p) - addons.add(RockPaperScissors(), p) - addons.add(Twitter(), p) // no properties, disabled. + addons.add(Joke()) + addons.add(RockPaperScissors()) + addons.add(Twitter()) // no properties, disabled. + addons.add(War()) + addons.add(Dice()) + addons.add(Lookup()) assertThat(addons.modules.size, "modules = 2").isEqualTo(2) - assertThat(addons.modulesNames, "module names").containsExactly("Joke", "RockPaperScissors") // Commands - addons.add(View(), p) - addons.add(Comment(), p) - addons.add(Cycle(), p) - addons.add(Die(), p) // invisible - addons.add(ChannelFeed("channel"), p) // no properties, disabled - addons.add(Ignore(), p.apply { put(Ignore.IGNORE_PROP, "nick") }) - assertThat(addons.commands.size, "commands = 4").isEqualTo(5) + addons.add(View()) + addons.add(Comment()) + addons.add(Cycle()) + addons.add(Die()) // invisible + addons.add(ChannelFeed("channel")) // no properties, disabled + p[Ignore.IGNORE_PROP] = "nick" + addons.add(Ignore()) + assertThat(addons.commands.size, "commands = 3").isEqualTo(3) assertThat(addons.ops, "ops").containsExactly("cycle") @@ -78,8 +86,6 @@ class AddonsTest { "rock", "paper", "scissors", - "view", - "comment", "ignore" ) } diff --git a/version.properties b/version.properties index 41eca0f..4aea8d8 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jan 01 06:34:39 PST 2022 -version.buildmeta=2428 +#Sat Jan 08 23:07:31 PST 2022 +version.buildmeta=2440 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+2428 +version.semver=0.8.0-beta+2440 From c99d2ce805b2520975425a3fd29cc7d62af3c3c5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 16 Jan 2022 18:31:38 -0800 Subject: [PATCH 609/842] Updated dependencies. --- build.gradle | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index f62606e..fdc6286 100644 --- a/build.gradle +++ b/build.gradle @@ -44,37 +44,35 @@ dependencies { implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' - implementation 'org.apache.commons:commons-text:1.9' implementation 'org.apache.commons:commons-lang3:3.12.0' - implementation 'org.slf4j:slf4j-api:1.7.32' + implementation 'org.apache.commons:commons-text:1.9' + + implementation 'commons-cli:commons-cli:1.5.0' implementation 'commons-codec:commons-codec:1.15' + implementation 'commons-net:commons-net:3.8.0' + + implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:31.0.1-jre' implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0' + implementation 'org.slf4j:slf4j-api:1.7.33' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" - implementation 'commons-cli:commons-cli:1.5.0' - implementation 'commons-net:commons-net:3.8.0' - implementation 'com.rometools:rome:1.18.0' implementation 'com.squareup.okhttp3:okhttp:4.9.3' - implementation 'net.aksingh:owm-japis:2.5.3.0' - implementation 'com.google.code.gson:gson:2.8.9' - implementation 'net.objecthunter:exp4j:0.4.8' - - implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' - implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'org.json:json:20211205' implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.twitter4j:twitter4j-core:4.0.7' + implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' + implementation 'net.thauvin.erik:pinboard-poster:1.0.3' + testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation "org.mockito:mockito-core:4.0.0" From a70f22d40285e252a173bfbfce1804fe139b2f74 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Sun, 16 Jan 2022 18:32:28 -0800 Subject: [PATCH 610/842] Changed Modules command to fetch data directly from the addons. --- src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/commands/Modules.kt | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 02467d6..ee6ad12 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -401,6 +401,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(Ignore()) addons.add(LinksMgr()) addons.add(Me()) + addons.add(Modules(addons)) addons.add(Msg()) addons.add(Nick()) addons.add(Posting()) @@ -426,7 +427,6 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(Info(tell)) addons.add(Joke()) addons.add(Lookup()) - addons.add(Modules(addons.modulesNames)) addons.add(Ping()) addons.add(RockPaperScissors()) addons.add(StockQuote()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 9ed298c..1ded9ba 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -32,12 +32,13 @@ package net.thauvin.erik.mobibot.commands +import net.thauvin.erik.mobibot.Addons import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendList import org.pircbotx.hooks.types.GenericMessageEvent -class Modules(private val modulesNames: List<String>) : AbstractCommand() { +class Modules(private val addons: Addons) : AbstractCommand() { override val name = "modules" override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name")) override val isOpOnly = true @@ -46,11 +47,11 @@ class Modules(private val modulesNames: List<String>) : AbstractCommand() { override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isChannelOp(channel, event)) { - if (modulesNames.isEmpty()) { + if (addons.modulesNames.isEmpty()) { event.respondPrivateMessage("There are no enabled modules.") } else { event.respondPrivateMessage("The enabled modules are: ") - event.sendList(modulesNames, 7, isIndent = true) + event.sendList(addons.modulesNames, 7, isIndent = true) } } else { helpResponse(channel, args, event) From 881bd716b0f98a2cea5fb698b8ad2a3ae279ea53 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 18 Jan 2022 10:03:50 -0800 Subject: [PATCH 611/842] Added Names holding object to Addons. --- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 28 +++++++++++-------- .../net/thauvin/erik/mobibot/Mobibot.kt | 8 +++--- .../thauvin/erik/mobibot/commands/Modules.kt | 7 ++--- .../net/thauvin/erik/mobibot/AddonsTest.kt | 6 ++-- version.properties | 6 ++-- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 4e95ad0..8ae52a9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -48,9 +48,7 @@ class Addons(private val props: Properties) { val commands: MutableList<AbstractCommand> = mutableListOf() val modules: MutableList<AbstractModule> = mutableListOf() - val modulesNames: MutableList<String> = mutableListOf() - val names: MutableList<String> = mutableListOf() - val ops: MutableList<String> = mutableListOf() + val names = Names /** * Add a module with properties. @@ -66,8 +64,8 @@ class Addons(private val props: Properties) { if (isEnabled) { modules.add(this) - modulesNames.add(name) - names.addAll(commands) + names.modules.add(name) + names.commands.addAll(commands) } } } @@ -88,9 +86,9 @@ class Addons(private val props: Properties) { commands.add(this) if (isVisible) { if (isOpOnly) { - ops.add(name) + names.ops.add(name) } else { - names.add(name) + names.commands.add(name) } } } @@ -150,11 +148,17 @@ class Addons(private val props: Properties) { } /** - * Sort commands and modules names. + * Holds commands and modules names. */ - fun sort() { - names.sort() - ops.sort() - modulesNames.sort() + object Names { + val modules: MutableList<String> = mutableListOf() + val commands: MutableList<String> = mutableListOf() + val ops: MutableList<String> = mutableListOf() + + fun sort() { + modules.sort() + commands.sort() + ops.sort() + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index ee6ad12..d8ff578 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -143,10 +143,10 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro ), ) event.sendMessage("The commands are:") - event.sendList(addons.names, 8, isBold = true, isIndent = true) + event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) if (isChannelOp(channel, event)) { event.sendMessage("The op commands are:") - event.sendList(addons.ops, 8, isBold = true, isIndent = true) + event.sendList(addons.names.ops, 8, isBold = true, isIndent = true) } } @@ -401,7 +401,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(Ignore()) addons.add(LinksMgr()) addons.add(Me()) - addons.add(Modules(addons)) + addons.add(Modules(addons.names.modules)) addons.add(Msg()) addons.add(Nick()) addons.add(Posting()) @@ -435,7 +435,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(War()) // Sort the addons - addons.sort() + addons.names.sort() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 1ded9ba..82a0b06 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -32,13 +32,12 @@ package net.thauvin.erik.mobibot.commands -import net.thauvin.erik.mobibot.Addons import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendList import org.pircbotx.hooks.types.GenericMessageEvent -class Modules(private val addons: Addons) : AbstractCommand() { +class Modules(private val modules: List<String>) : AbstractCommand() { override val name = "modules" override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name")) override val isOpOnly = true @@ -47,11 +46,11 @@ class Modules(private val addons: Addons) : AbstractCommand() { override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isChannelOp(channel, event)) { - if (addons.modulesNames.isEmpty()) { + if (modules.isEmpty()) { event.respondPrivateMessage("There are no enabled modules.") } else { event.respondPrivateMessage("The enabled modules are: ") - event.sendList(addons.modulesNames, 7, isIndent = true) + event.sendList(modules, 7, isIndent = true) } } else { helpResponse(channel, args, event) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 54346a9..f03a2c2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -67,7 +67,7 @@ class AddonsTest { addons.add(Dice()) addons.add(Lookup()) assertThat(addons.modules.size, "modules = 2").isEqualTo(2) - assertThat(addons.modulesNames, "module names").containsExactly("Joke", "RockPaperScissors") + assertThat(addons.names.modules, "module names").containsExactly("Joke", "RockPaperScissors") // Commands addons.add(View()) @@ -79,9 +79,9 @@ class AddonsTest { addons.add(Ignore()) assertThat(addons.commands.size, "commands = 3").isEqualTo(3) - assertThat(addons.ops, "ops").containsExactly("cycle") + assertThat(addons.names.ops, "ops names").containsExactly("cycle") - assertThat(addons.names, "names").containsExactly( + assertThat(addons.names.commands, "command names").containsExactly( "joke", "rock", "paper", diff --git a/version.properties b/version.properties index 4aea8d8..e5629eb 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jan 08 23:07:31 PST 2022 -version.buildmeta=2440 +#Tue Jan 18 10:02:08 PST 2022 +version.buildmeta=2450 version.major=0 version.minor=8 version.patch=0 version.prerelease=beta version.project=mobibot -version.semver=0.8.0-beta+2440 +version.semver=0.8.0-beta+2450 From adb7a8c6924f4c4fd31c5c4ebaeaa6d0247388db Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 Jan 2022 15:25:07 -0800 Subject: [PATCH 612/842] Updated website docs. --- build.gradle | 2 +- version.properties | 8 ++++---- website/index.html | 21 +++++++++++++-------- website/simple.css | 3 +-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index fdc6286..96e8940 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.twitter4j:twitter4j-core:4.0.7' - implementation 'net.thauvin.erik:cryptoprice:0.9.0-SNAPSHOT' + implementation 'net.thauvin.erik:cryptoprice:0.9.0' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' diff --git a/version.properties b/version.properties index e5629eb..2f05c8d 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Jan 18 10:02:08 PST 2022 -version.buildmeta=2450 +#Wed Jan 19 15:20:26 PST 2022 +version.buildmeta=001 version.major=0 version.minor=8 version.patch=0 -version.prerelease=beta +version.prerelease=rc version.project=mobibot -version.semver=0.8.0-beta+2450 +version.semver=0.8.0-rc+001 diff --git a/website/index.html b/website/index.html index fe9468a..6427843 100644 --- a/website/index.html +++ b/website/index.html @@ -33,20 +33,19 @@ <h3>About mobibot</h3> <p><strong>mobibot</strong> is the - <a href="https://www.mobitopia.org/"><strong>#mobitopia</strong></a> IRC channel bot. It is built on Paul Mutton's - <a href="http://www.jibble.org/pircbot.php"><strong>PircBot Java-based Framework</strong></a>.</p> + <a href="https://www.mobitopia.org/"><strong>#mobitopia</strong></a> IRC channel bot written in <a href="https://kotlinlang.org/"><strong>Kotlin</strong></a>.</p> <p>mobibot is making extensive use of various <strong>open source libraries</strong>, including:</p> <ul> - <li><a href="https://commons.apache.org/proper/commons-cli/">Commons CLI</a></li> - <li><a href="https://hc.apache.org/httpclient-3.x/">Commons HTTPClient</a></li> - <li><a href="https://commons.apache.org/proper/commons-logging/">Commons Logging</a></li> - <li><a href="https://commons.apache.org/proper/commons-net/">Commons Net</a></li> + <li>Apache Commons <a href="https://commons.apache.org/proper/commons-cli/">CLI</a> and <a href="https://commons.apache.org/proper/commons-net/">Net</a></li> + <li><a href="https://github.com/ethauvin/cryptoprice">CryptoPrice</a></li> <li><a href="https://www.objecthunter.net/exp4j/">exp4j</a></li> <li><a href="https://jsoup.org/">jsoup</a></li> <li><a href="https://ostermiller.org/utils/">OstermillerUtils</a></li> + <li><a href="https://square.github.io/okhttp/">OkHttp</a></li> <li><a href="https://bitbucket.org/aksinghnet/owm-japis">OWM JAPIs</a></li> <li><a href="https://github.com/ethauvin/pinboard-poster">Pinboard Poster</a></li> + <li><a href="https://github.com/pircbotx/pircbotx">PircBotX</a></li> <li><a href="https://rometools.github.io/rome/">Rome</a></li> <li><a href="http://twitter4j.org/en/index.html">Twitter4J</a></li> </ul> @@ -58,7 +57,7 @@ <p>mobibot's main functionality is to <strong>capture URLs</strong> posted on the channel. The URLs are automatically gathered into a publishable - <a href="https://www.mobitopia.org/rss.xml"><strong>RSS feed</strong></a>. </p> + <a href="https://www.mobitopia.org/rss.xml"><strong>RSS feed</strong></a> and saved on <a href="https://pinboard.in/t:mobitopia"><strong>Pinboard</strong></a>.</p> <p>Other features include:</p> <ul> @@ -68,6 +67,10 @@ <li>Performing calculations <div><code>mobibot: calc (floor(sqrt(3)) + 3.14) * 3^2</code></div> </li> + <li>Crypto currencies prices + <div><code>mobibot: cryto btc</code></div> + <div><code>mobibot: cryto eth eur</code></div> + </li> <li>Converting between currencies <div><code>mobibot: currency 17.54 USD to EUR</code></div> </li> @@ -102,9 +105,11 @@ <li>Random jokes from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a> <div><code>mobibot: joke</code></div> </li> - <li>Rolling dice and playing war + <li>Rolling dice or Playing war and rock paper scissors <div><code>mobibot: dice</code></div> <div><code>mobibot: war</code></div> + <div><code>mobibot: paper</code></div> + <div><code>mobibot: rock</code></div> </li> <li>Posting to <a href="https://twitter.com/mobitopia">Twitter</a></li> </ul> diff --git a/website/simple.css b/website/simple.css index 40178ff..e1204d6 100644 --- a/website/simple.css +++ b/website/simple.css @@ -1,7 +1,7 @@ body { background: #ffffff; font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 13px; + font-size: 16px; color: #000000; margin: 0; } @@ -21,7 +21,6 @@ h3 { code { font-family: Courier New, Courier, mono, monospace; - font-size: 12px; color: #000066; background-color: #ffeedd; margin-left: 20px From 0cf98c6ca42ce3c68c705e7a26d35048131857fa Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 19 Jan 2022 22:55:21 -0800 Subject: [PATCH 613/842] Added PircBotX license. --- licenses/PircBot License.html | 60 ----------------------------------- licenses/PircBotX.txt | 13 ++++++++ 2 files changed, 13 insertions(+), 60 deletions(-) delete mode 100644 licenses/PircBot License.html create mode 100644 licenses/PircBotX.txt diff --git a/licenses/PircBot License.html b/licenses/PircBot License.html deleted file mode 100644 index 2a5a569..0000000 --- a/licenses/PircBot License.html +++ /dev/null @@ -1,60 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html> -<head> -<title>The GNU General Public License (GPL) - - -

    The GNU General Public License (GPL)

    -

    Version 2, June 1991

    -

    Copyright (C) 1989, 1991 Free Software Foundation, Inc.
    - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

    -

    Everyone is permitted to copy and distribute verbatim copies
    - of this license document, but changing it is not allowed.

    -

    Preamble

    -

    The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.

    -

    When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

    -

    To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.

    -

    For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

    -

    We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.

    -

    Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.

    -

    Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

    -

    The precise terms and conditions for copying, distribution and modification follow.

    -

    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

    -

    0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

    -

    Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

    -

    1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

    -

    You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

    -

    2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

    -
    -

    a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.

    -

    b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.

    -

    c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)

    -
    -

    These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

    -

    Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.

    -

    In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

    -

    3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:

    -
    -

    a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

    -

    b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

    -

    c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)

    -
    -

    The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

    -

    If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

    -

    4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

    -

    5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

    -

    6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

    -

    7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.

    -

    If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.

    -

    It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

    -

    This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

    -

    8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

    -

    9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

    -

    Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

    -

    10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

    -

    NO WARRANTY

    -

    11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

    -

    12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

    -

    END OF TERMS AND CONDITIONS

    - - diff --git a/licenses/PircBotX.txt b/licenses/PircBotX.txt new file mode 100644 index 0000000..9fa1ca4 --- /dev/null +++ b/licenses/PircBotX.txt @@ -0,0 +1,13 @@ +Copyright (C) 2010-2014 Leon Blakey + +PircBotX is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +PircBotX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +PircBotX. If not, see . \ No newline at end of file From fe6ddf267d907934d0e1f609da902b4e5bddc59f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 22:15:16 -0800 Subject: [PATCH 614/842] Added toLinkLabel extension function. --- .idea/runConfigurations.xml | 10 ---------- .../erik/mobibot/commands/links/Comment.kt | 6 +++--- .../erik/mobibot/commands/links/LinksMgr.kt | 4 ++-- .../erik/mobibot/commands/links/Posting.kt | 5 +++-- .../erik/mobibot/entries/EntriesUtils.kt | 18 +++++++++--------- .../thauvin/erik/mobibot/modules/Twitter.kt | 6 +++--- .../erik/mobibot/entries/EntriesUtilsTest.kt | 4 ++-- 7 files changed, 22 insertions(+), 31 deletions(-) delete mode 100644 .idea/runConfigurations.xml diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 797acea..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 59f31c8..10916de 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -39,7 +39,7 @@ import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.entries.EntriesUtils.buildComment -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import org.pircbotx.hooks.types.GenericMessageEvent @@ -97,7 +97,7 @@ class Comment : AbstractCommand() { } override fun matches(message: String): Boolean { - return message.matches("^${Constants.LINK_CMD}[0-9]+\\.[0-9]+:.*".toRegex()) + return message.matches("^${Constants.LINK_CMD}\\d+\\.\\d+:.*".toRegex()) } private fun changeAuthor( @@ -127,7 +127,7 @@ class Comment : AbstractCommand() { ) { if (isChannelOp(channel, event) || event.user.nick == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) - event.sendMessage("Comment ${buildLinkLabel(entryIndex)}.${commentIndex + 1} removed.") + event.sendMessage("Comment ${entryIndex.toLinkLabel()}.${commentIndex + 1} removed.") LinksMgr.entries.save() } else { event.sendMessage("Please ask a channel op to delete this comment for you.") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 8485be0..58efd89 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -43,7 +43,7 @@ import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored import net.thauvin.erik.mobibot.entries.Entries import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.mobibot.modules.Twitter import org.jsoup.Jsoup @@ -147,7 +147,7 @@ class LinksMgr : AbstractCommand() { if (Constants.NO_TITLE == entry.title) { event.sendMessage("Please specify a title, by typing:") - event.sendMessage(helpFormat("${buildLinkLabel(index)}:|This is the title")) + event.sendMessage(helpFormat("${index.toLinkLabel()}:|This is the title")) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 670daeb..bc91b97 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -41,6 +41,7 @@ import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import org.pircbotx.hooks.types.GenericMessageEvent @@ -86,7 +87,7 @@ class Posting : AbstractCommand() { } override fun matches(message: String): Boolean { - return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex()) + return message.matches("${Constants.LINK_CMD}\\d+:.*".toRegex()) } private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) { @@ -141,7 +142,7 @@ class Posting : AbstractCommand() { LinksMgr.pinboard.deletePin(entry) LinksMgr.twitter.removeEntry(index) entries.links.removeAt(index) - event.sendMessage("Entry ${EntriesUtils.buildLinkLabel(index)} removed.") + event.sendMessage("Entry ${index.toLinkLabel()} removed.") entries.save() } else { event.sendMessage("Please ask a channel op to remove this entry for you.") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 023ec86..2b96dac 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -39,18 +39,12 @@ import net.thauvin.erik.mobibot.Utils.green * Entries utilities. */ object EntriesUtils { - /** - * Build link label based on its index. e.g: L1 - */ - @JvmStatic - fun buildLinkLabel(index: Int): String = Constants.LINK_CMD + (index + 1) - /** * Builds an entry's comment for display on the channel. */ @JvmStatic fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${buildLinkLabel(entryIndex)}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** * Builds an entry's link for display on the channel. @@ -58,7 +52,7 @@ object EntriesUtils { @JvmStatic @JvmOverloads fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { - val buff = StringBuilder().append(buildLinkLabel(entryIndex)).append(": ") + val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") .append('[').append(entry.nick).append(']') if (isView && entry.comments.isNotEmpty()) { buff.append("[+").append(entry.comments.size).append(']') @@ -80,5 +74,11 @@ object EntriesUtils { */ @JvmStatic fun buildTags(entryIndex: Int, entry: EntryLink): String = - buildLinkLabel(entryIndex) + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ") + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ") + + /** + * Build link label based on its index. e.g: L1 + */ + @JvmStatic + fun Int.toLinkLabel(): String = Constants.LINK_CMD + (this + 1) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 74f0ba0..3c0f212 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -37,7 +37,7 @@ import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.TwitterTimer import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.commands.links.LinksMgr -import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -135,7 +135,7 @@ class Twitter : ThreadedModule() { launch { try { if (logger.isDebugEnabled) { - logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkLabel(index)) + logger.debug("Posting {} to Twitter.", index.toLinkLabel()) } post(message = msg, isDm = false) } catch (e: ModuleException) { @@ -151,7 +151,7 @@ class Twitter : ThreadedModule() { if (isAutoPost) { addEntry(index) if (logger.isDebugEnabled) { - logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkLabel(index)) + logger.debug("Scheduling {} for posting on Twitter.", index.toLinkLabel()) } timer.schedule(TwitterTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 3b39be4..9f4821d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -38,8 +38,8 @@ import assertk.assertions.isEqualTo import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.entries.EntriesUtils.buildComment import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel import net.thauvin.erik.mobibot.entries.EntriesUtils.buildTags +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import org.testng.annotations.Test class EntriesUtilsTest { @@ -61,7 +61,7 @@ class EntriesUtilsTest { @Test fun buildLinkLabelTest() { - assertThat(buildLinkLabel(1)).isEqualTo("${Constants.LINK_CMD}2") + assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") } @Test From 25d7a74568fd6411efaaa959012ce4f10952056c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 22:25:26 -0800 Subject: [PATCH 615/842] Added unescapeXml extension function. --- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 39 +------------------ .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 20 +++++----- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- 4 files changed, 13 insertions(+), 50 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index b68ee47..d47760a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -340,44 +340,7 @@ object Utils { * Converts XML/XHTML entities to plain text. */ @JvmStatic - fun unescapeXml(str: String): String = Jsoup.parse(str).text() - - /** - * Converts milliseconds to year month week day hour and minutes. - */ - @JvmStatic - fun uptime(uptime: Long): String { - uptime.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, _, _ -> - val years = wholeDays / 365 - var days = wholeDays % 365 - val months = days / 30 - days %= 30 - val weeks = days / 7 - days %= 7 - - with(StringBuffer()) { - if (years > 0) { - append(years).append(" year".plural(years)).append(' ') - } - if (months > 0) { - append(weeks).append(" month".plural(months)).append(' ') - } - if (weeks > 0) { - append(weeks).append(" week".plural(weeks)).append(' ') - } - if (days > 0) { - append(days).append(" day".plural(days)).append(' ') - } - if (hours > 0) { - append(hours).append(" hour".plural(hours.toLong())).append(' ') - } - - append(minutes).append(" minute".plural(minutes.toLong())) - - return toString() - } - } - } + fun String.unescapeXml(): String = Jsoup.parse(this).text() /** * Reads contents of a URL. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 6adffdd..742e6d9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -115,7 +115,7 @@ class GoogleSearch : ThreadedModule() { val ja = json.getJSONArray("items") for (i in 0 until ja.length()) { val j = ja.getJSONObject(i) - results.add(NoticeMessage(unescapeXml(j.getString("title")))) + results.add(NoticeMessage(j.getString("title").unescapeXml())) results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 63d358c..030674f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -102,7 +102,7 @@ class StockQuote : ThreadedModule() { try { val info = json.getString("Information") if (info.isNotEmpty()) { - throw ModuleException(debugMessage, unescapeXml(info)) + throw ModuleException(debugMessage, info.unescapeXml()) } } catch (ignore: JSONException) { // Do nothing @@ -110,11 +110,11 @@ class StockQuote : ThreadedModule() { try { var error = json.getString("Note") if (error.isNotEmpty()) { - throw ModuleException(debugMessage, unescapeXml(error)) + throw ModuleException(debugMessage, error.unescapeXml()) } error = json.getString("Error Message") if (error.isNotEmpty()) { - throw ModuleException(debugMessage, unescapeXml(error)) + throw ModuleException(debugMessage, error.unescapeXml()) } } catch (ignore: JSONException) { // Do nothing @@ -173,8 +173,8 @@ class StockQuote : ThreadedModule() { add( PublicMessage( - "Symbol: " + unescapeXml(quote.getString("01. symbol")) - + " [" + unescapeXml(symbolInfo.getString("2. name")) + ']' + "Symbol: " + quote.getString("01. symbol").unescapeXml() + + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' ) ) @@ -183,13 +183,13 @@ class StockQuote : ThreadedModule() { add( PublicMessage( "Price:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString("05. price")) + + quote.getString("05. price").unescapeXml() ) ) add( PublicMessage( "Previous:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString("08. previous close")) + + quote.getString("08. previous close").unescapeXml() ) ) @@ -205,7 +205,7 @@ class StockQuote : ThreadedModule() { add( NoticeMessage( "${it.first}:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString(it.second)) + + quote.getString(it.second).unescapeXml() ) ) } @@ -213,8 +213,8 @@ class StockQuote : ThreadedModule() { add( NoticeMessage( "Change:".padEnd(pad).prependIndent() - + unescapeXml(quote.getString("09. change")) - + " [" + unescapeXml(quote.getString("10. change percent")) + ']' + + quote.getString("09. change").unescapeXml() + + " [" + quote.getString("10. change percent").unescapeXml() + ']' ) ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 06bb589..b7a4008 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -254,7 +254,7 @@ class UtilsTest { @Test fun testUnescapeXml() { - assertThat(unescapeXml("<a name="test & ''">")).isEqualTo( + assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( "" ) } From 0dd02d7039a06ed3585ebd3b850ec38c5eab9aeb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 22:34:09 -0800 Subject: [PATCH 616/842] Added URL reader and encoder extension funtions. --- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 6 ++--- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../erik/mobibot/modules/GoogleSearch.kt | 6 ++--- .../net/thauvin/erik/mobibot/modules/Joke.kt | 4 ++-- .../erik/mobibot/modules/StockQuote.kt | 24 ++++++++----------- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 10 ++++---- .../net/thauvin/erik/mobibot/UtilsTest.kt | 5 ++-- 8 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index d47760a..20286c1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -145,7 +145,7 @@ object Utils { * URL encodes the given string. */ @JvmStatic - fun encodeUrl(s: String): String = URLEncoder.encode(s, StandardCharsets.UTF_8) + fun String.encodeUrl(): String = URLEncoder.encode(this, StandardCharsets.UTF_8) /** * Returns a property as an int. @@ -347,8 +347,8 @@ object Utils { */ @JvmStatic @Throws(IOException::class) - fun urlReader(url: URL): String { - BufferedReader(InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) + fun URL.reader(): String { + BufferedReader(InputStreamReader(this.openStream(), StandardCharsets.UTF_8)) .use { reader -> return reader.lines().collect(Collectors.joining(System.lineSeparator())) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index cc80595..3cc1b1a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -57,7 +57,7 @@ class ChannelFeed(channel: String) : AbstractCommand() { if (isEnabled()) { runBlocking { launch { - FeedReader(properties[FEED_PROP]!!, event).run() + properties[FEED_PROP]?.let { FeedReader(it, event).run() } } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 742e6d9..09091cb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -34,9 +34,9 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage @@ -108,9 +108,9 @@ class GoogleSearch : ThreadedModule() { try { val url = URL( "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - "&q=${encodeUrl(query)}&filter=1&num=5&alt=json" + "&q=${query.encodeUrl()}&filter=1&num=5&alt=json" ) - val json = JSONObject(urlReader(url)) + val json = JSONObject(url.reader()) if (json.has("items")) { val ja = json.getJSONArray("items") for (i in 0 until ja.length()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 6922698..c2f15b4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -36,8 +36,8 @@ import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import org.json.JSONException @@ -94,7 +94,7 @@ class Joke : ThreadedModule() { fun randomJoke(): Message { return try { val url = URL(JOKE_URL) - val json = JSONObject(urlReader(url)) + val json = JSONObject(url.reader()) PublicMessage( json.getJSONObject("value")["joke"].toString().replace("\\'", "'") .replace("\\\"", "\"") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 030674f..ce53561 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -34,9 +34,9 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.NoticeMessage @@ -144,12 +144,10 @@ class StockQuote : ThreadedModule() { try { with(messages) { // Search for symbol/keywords - response = urlReader( - URL( - "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + encodeUrl(symbol) - + "&apikey=" + encodeUrl(apiKey) - ) - ) + response = URL( + "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + + apiKey.encodeUrl() + ).reader() var json = getJsonResponse(response, debugMessage) val symbols = json.getJSONArray("bestMatches") if (symbols.isEmpty) { @@ -158,13 +156,11 @@ class StockQuote : ThreadedModule() { val symbolInfo = symbols.getJSONObject(0) // Get quote for symbol - response = urlReader( - URL( - "${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" - + encodeUrl(symbolInfo.getString("1. symbol")) - + "&apikey=" + encodeUrl(apiKey) - ) - ) + response = URL( + "${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" + + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + + apiKey.encodeUrl() + ).reader() json = getJsonResponse(response, debugMessage) val quote = json.getJSONObject("Global Quote") if (quote.isEmpty) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 96ae93e..403dc79 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -197,7 +197,7 @@ class Weather2 : ThreadedModule() { messages.add( NoticeMessage( "https://openweathermap.org/find?q=" - + encodeUrl("$city,${code.uppercase()}"), + + "$city,${code.uppercase()}".encodeUrl(), Colors.GREEN ) ) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 70c6b28..4705f11 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -32,6 +32,8 @@ package net.thauvin.erik.mobibot +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.reader import net.thauvin.erik.mobibot.entries.EntryLink import org.testng.Assert.assertFalse import org.testng.Assert.assertTrue @@ -66,12 +68,8 @@ class PinboardTest : LocalProperties() { } private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { - val response = Utils.urlReader( - URL( - "https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" - + Utils.encodeUrl(url) - ) - ) + val response = + URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader() matches.forEach { if (!response.contains(it)) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index b7a4008..29ae070 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -49,6 +49,7 @@ import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.reader import net.thauvin.erik.mobibot.Utils.red import net.thauvin.erik.mobibot.Utils.replaceEach import net.thauvin.erik.mobibot.Utils.reverseColor @@ -153,7 +154,7 @@ class UtilsTest { @Test fun testEncodeUrl() { - assertThat(encodeUrl("Hello Günter")).isEqualTo("Hello+G%C3%BCnter") + assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello+G%C3%BCnter") } @Test @@ -274,7 +275,7 @@ class UtilsTest { @Test @Throws(IOException::class) fun testUrlReader() { - assertThat(urlReader(URL("https://postman-echo.com/status/200")), "urlReader()") + assertThat(URL("https://postman-echo.com/status/200").reader(), "urlReader()") .isEqualTo("{\"status\":200}") } From ea40d71f1440cca6198933b98372919123367935 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 22:36:52 -0800 Subject: [PATCH 617/842] Changed buildCmdSyntax to helpCmdSyntax --- .../net/thauvin/erik/mobibot/Mobibot.kt | 12 +++---- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 10 ++++++ .../erik/mobibot/commands/AbstractCommand.kt | 9 ++--- .../thauvin/erik/mobibot/commands/Ignore.kt | 4 +-- .../erik/mobibot/commands/links/View.kt | 8 ++--- .../erik/mobibot/commands/tell/Tell.kt | 8 ++--- .../erik/mobibot/modules/AbstractModule.kt | 4 +-- .../erik/mobibot/modules/CurrencyConverter.kt | 15 ++++---- .../net/thauvin/erik/mobibot/UtilsTest.kt | 34 ++++++------------- 9 files changed, 44 insertions(+), 60 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index d8ff578..cdc7a8f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -35,6 +35,8 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.sendList @@ -134,13 +136,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event.sendMessage("Type a URL on $channel to post it.") event.sendMessage("For more information on a specific command, type:") event.sendMessage( - Utils.helpFormat( - Utils.buildCmdSyntax( - "%c ${Constants.HELP_CMD} ", - event.bot().nick, - event is PrivateMessageEvent - ) - ), + helpFormat( + helpCmdSyntax("%c ${Constants.HELP_CMD} ", event.bot().nick, event is PrivateMessageEvent) + ) ) event.sendMessage("The commands are:") event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 20286c1..6becd6e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -161,6 +161,16 @@ object Utils { @JvmStatic fun String?.green(): String = colorize(Colors.DARK_GREEN) + /** + * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's + * nick. + */ + @JvmStatic + fun helpCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { + val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) + return text.replaceEach(searchFlags, replace) + } + /** * Returns a formatted help string. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 674fe0f..f773cdd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -33,12 +33,11 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendMessage import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent -import java.util.concurrent.ConcurrentHashMap abstract class AbstractCommand { abstract val name: String @@ -47,16 +46,14 @@ abstract class AbstractCommand { abstract val isPublic: Boolean abstract val isVisible: Boolean - val properties: MutableMap = ConcurrentHashMap() + val properties: MutableMap = mutableMapOf() abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent) open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { if (!isOpOnly || isOpOnly == isChannelOp(channel, event)) { for (h in help) { - event.sendMessage( - buildCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic), - ) + event.sendMessage(helpCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic)) } return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index afd9bf4..df56e34 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.commands import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendList @@ -90,7 +90,7 @@ class Ignore : AbstractCommand() { override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { return if (isChannelOp(channel, event)) { for (h in helpOp) { - event.sendMessage(buildCmdSyntax(h, event.bot().nick, true)) + event.sendMessage(helpCmdSyntax(h, event.bot().nick, true)) } true } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index f853cae..dd22c0e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.commands.links import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.sendMessage @@ -109,11 +109,7 @@ class View : AbstractCommand() { event.sendMessage("To view more, try: ") event.sendMessage( helpFormat( - buildCmdSyntax( - "%c $name ${index + 1} $query", - event.bot().nick, - event is PrivateMessageEvent - ) + helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) ) ) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 1100c99..a581cd5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.commands.tell import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.plural @@ -256,11 +256,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { event.sendMessage("To delete one or all delivered messages:") event.sendMessage( helpFormat( - buildCmdSyntax( - "%c $name $TELL_DEL_KEYWORD ", - event.bot().nick, - true - ) + helpCmdSyntax("%c $name $TELL_DEL_KEYWORD ", event.bot().nick, true) ) ) event.sendMessage(help.last()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 25e7c9d..83eafcf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.sendMessage import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent @@ -79,7 +79,7 @@ abstract class AbstractModule { */ open fun helpResponse(event: GenericMessageEvent): Boolean { for (h in help) { - event.sendMessage(buildCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent)) + event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent)) } return true } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index f6de896..e597df7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -33,7 +33,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendMessage @@ -111,11 +111,11 @@ class CurrencyConverter : ThreadedModule() { } else { val nick = event.bot().nick event.sendMessage("To convert from one currency to another:") - event.sendMessage(helpFormat(buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) + event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage("For a listing of current reference rates:") event.sendMessage( helpFormat( - buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) + helpCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) ) ) event.sendMessage("The supported currencies are: ") @@ -161,13 +161,14 @@ class CurrencyConverter : ThreadedModule() { } else { val to = cmds[1].uppercase() val from = cmds[3].uppercase() - if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) { + val toRate = EXCHANGE_RATES[to] + val fromRate = EXCHANGE_RATES[from] + if (!toRate.isNullOrBlank() && !fromRate.isNullOrBlank()) { try { val amt = cmds[0].replace(",", "").toDouble() - val doubleFrom = EXCHANGE_RATES[to]!!.toDouble() - val doubleTo = EXCHANGE_RATES[from]!!.toDouble() PublicMessage( - amt.formatCurrency(to) + " = " + (amt * doubleTo / doubleFrom).formatCurrency(from) + amt.formatCurrency(to) + " = " + + (amt * toRate.toDouble() / fromRate.toDouble()).formatCurrency(from) ) } catch (e: NumberFormatException) { ErrorMessage("Let's try with some real numbers next time, okay?") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 29ae070..2cd92ba 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -37,7 +37,6 @@ import assertk.assertions.isEqualTo import assertk.assertions.length import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.capitalizeWords import net.thauvin.erik.mobibot.Utils.colorize @@ -45,6 +44,7 @@ import net.thauvin.erik.mobibot.Utils.cyan import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.obfuscate @@ -58,8 +58,6 @@ import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.toUtcDateTime import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.Utils.uptime -import net.thauvin.erik.mobibot.Utils.urlReader import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR import org.pircbotx.Colors import org.testng.annotations.BeforeClass @@ -105,15 +103,6 @@ class UtilsTest { assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) } - @Test - fun testBuildCmdSyntax() { - val bot = "mobibot" - assertThat(buildCmdSyntax("%c $test %n $test", bot, false), "public") - .isEqualTo("$bot: $test $bot $test") - assertThat(buildCmdSyntax("%c %n $test %c $test %n", bot, true), "public") - .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") - } - @Test fun testCapitalise() { @@ -172,6 +161,15 @@ class UtilsTest { assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) } + @Test + fun testHelpCmdSyntax() { + val bot = "mobibot" + assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "public") + .isEqualTo("$bot: $test $bot $test") + assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "public") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + } + @Test fun testHelpFormat() { assertThat(helpFormat(test, isBold = true, isIndent = false), "bold") @@ -260,18 +258,6 @@ class UtilsTest { ) } - @Test - fun testUptime() { - assertThat(uptime(547800300076L), "full") - .isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes") - assertThat(uptime(2700000L), "minutes").isEqualTo("45 minutes") - assertThat(uptime(24300000L), "hours minutes").isEqualTo("6 hours 45 minutes") - assertThat(uptime(110700000L), "days hours minutes").isEqualTo("1 day 6 hours 45 minutes") - assertThat(uptime(1320300000L), "weeks days hours minutes") - .isEqualTo("2 weeks 1 day 6 hours 45 minutes") - assertThat(uptime(0L), "0 minutes").isEqualTo("0 minute") - } - @Test @Throws(IOException::class) fun testUrlReader() { From 4abdb268ef23de445ece6096699e42d1872fdb33 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 22:38:51 -0800 Subject: [PATCH 618/842] Code cleanup. --- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 15 ++------------- .../net/thauvin/erik/mobibot/commands/Die.kt | 3 --- .../thauvin/erik/mobibot/commands/links/Tags.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Weather2.kt | 10 ++++++---- .../thauvin/erik/mobibot/ExceptionSanitizer.kt | 4 ++-- 5 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 6becd6e..b218892 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -50,8 +50,6 @@ import java.time.format.DateTimeFormatter import java.util.Date import java.util.Properties import java.util.stream.Collectors -import kotlin.time.DurationUnit -import kotlin.time.toDuration /** * Miscellaneous utilities. @@ -97,16 +95,6 @@ object Utils { return getBot() as PircBotX } - /** - * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's - * nick. - */ - @JvmStatic - fun buildCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { - val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) - return text.replaceEach(searchFlags, replace) - } - /** * Capitalize a string. */ @@ -116,9 +104,9 @@ object Utils { /** * Capitalize words */ + @JvmStatic fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() } - /** * Colorize a string. */ @@ -254,6 +242,7 @@ object Utils { * Send a formatted commands/modules, etc. list. */ @JvmStatic + @JvmOverloads fun GenericMessageEvent.sendList( list: List, maxPerLine: Int, diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index 26f05c3..9ccbcf3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -54,9 +54,6 @@ class Die : AbstractCommand() { } companion object { - /** - * Max days property. - */ const val DIE_PROP = "die" } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 7c0d904..842866e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -83,6 +83,6 @@ class Tags : AbstractCommand() { } override fun matches(message: String): Boolean { - return message.matches("^${Constants.LINK_CMD}[0-9]+${Constants.TAG_CMD}:.*".toRegex()) + return message.matches("^${Constants.LINK_CMD}\\d+${Constants.TAG_CMD}:.*".toRegex()) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 403dc79..8dcdc15 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -96,8 +96,8 @@ class Weather2 : ThreadedModule() { /** * Converts and rounds temperature from °F to °C. */ - fun ftoC(d: Double?): Pair { - val c = (d!! - 32) * 5 / 9 + fun ftoC(d: Double): Pair { + val c = (d - 32) * 5 / 9 return d.roundToInt() to c.roundToInt() } @@ -154,8 +154,10 @@ class Weather2 : ThreadedModule() { cwd.mainData?.let { with(it) { if (hasTemp()) { - val t = ftoC(temp) - messages.add(PublicMessage("Temperature: ${t.first}°F, ${t.second}°C")) + temp?.let { t -> + val (f, c) = ftoC(t) + messages.add(PublicMessage("Temperature: ${f}°F, ${c}°C")) + } } if (hasHumidity()) { humidity?.let { h -> diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index ba486f0..b1c6dba 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -48,11 +48,11 @@ object ExceptionSanitizer { if (!cause?.message.isNullOrBlank()) { return ModuleException( debugMessage, - cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(search, obfuscate), + cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), this ) } else if (!message.isNullOrBlank()) { - return ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this) + return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) } } } From cbc69be517b41846f13df8a80585d989a87856b3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 22:39:24 -0800 Subject: [PATCH 619/842] Code cleanup and added test. --- .../net/thauvin/erik/mobibot/commands/Info.kt | 45 +++++++++++++++- .../thauvin/erik/mobibot/commands/InfoTest.kt | 51 +++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 27521ef..d176448 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -36,13 +36,15 @@ import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.green import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.plural import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.commands.links.LinksMgr import net.thauvin.erik.mobibot.commands.tell.Tell import org.pircbotx.hooks.types.GenericMessageEvent import java.lang.management.ManagementFactory +import kotlin.time.DurationUnit +import kotlin.time.toDuration class Info(private val tell: Tell) : AbstractCommand() { private val allVersions = listOf( @@ -55,11 +57,50 @@ class Info(private val tell: Tell) : AbstractCommand() { override val isPublic = true override val isVisible = true + companion object { + /** + * Converts milliseconds to year month week day hour and minutes. + */ + @JvmStatic + fun Long.toUptime(): String { + this.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, _, _ -> + val years = wholeDays / 365 + var days = wholeDays % 365 + val months = days / 30 + days %= 30 + val weeks = days / 7 + days %= 7 + + with(StringBuffer()) { + if (years > 0) { + append(years).append(" year".plural(years)).append(' ') + } + if (months > 0) { + append(weeks).append(" month".plural(months)).append(' ') + } + if (weeks > 0) { + append(weeks).append(" week".plural(weeks)).append(' ') + } + if (days > 0) { + append(days).append(" day".plural(days)).append(' ') + } + if (hours > 0) { + append(hours).append(" hour".plural(hours.toLong())).append(' ') + } + + append(minutes).append(" minute".plural(minutes.toLong())) + + return toString() + } + } + } + } + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { event.sendList(allVersions, 1) val info = StringBuilder() info.append("Uptime: ") - .append(uptime(ManagementFactory.getRuntimeMXBean().uptime)) + .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) .append(" [Entries: ") .append(LinksMgr.entries.links.size) if (isChannelOp(channel, event)) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt new file mode 100644 index 0000000..f2036da --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -0,0 +1,51 @@ +/* + * InfoTest.kt + * + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import assertk.assertThat +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime +import org.testng.annotations.Test + +class InfoTest { + @Test + fun testToUptime() { + assertThat(547800300076L.toUptime(), "full").isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes") + assertThat(24300000L.toUptime(), "hours minutes").isEqualTo("6 hours 45 minutes") + assertThat(110700000L.toUptime(), "days hours minutes").isEqualTo("1 day 6 hours 45 minutes") + assertThat(1320300000L.toUptime(), "weeks days hours minutes").isEqualTo("2 weeks 1 day 6 hours 45 minutes") + assertThat(2700000L.toUptime(), "45 minutes").isEqualTo("45 minutes") + assertThat(60000L.toUptime(), "1 minute").isEqualTo("1 minute") + assertThat(0L.toUptime(), "0 minute").isEqualTo("0 minute") + } +} From 7aaa46e9f40f19063b1e57d42779fb5b7bb39c19 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 22:40:06 -0800 Subject: [PATCH 620/842] Updated dependencies. --- .idea/runConfigurations.xml | 10 ++++++++++ README.md | 3 ++- build.gradle | 25 ++++++++++++++---------- config/detekt/baseline.xml | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- version.properties | 6 +++--- 6 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 .idea/runConfigurations.xml diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 1a28679..91b6a46 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) + [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: diff --git a/build.gradle b/build.gradle index 96e8940..5527079 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.41.0' + id 'com.github.ben-manes.versions' version '0.42.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.19.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.6.10' id 'org.jetbrains.kotlin.kapt' version '1.6.10' - id 'org.jetbrains.kotlinx.kover' version '0.4.4' + id 'org.jetbrains.kotlinx.kover' version '0.5.0' id 'org.sonarqube' version '3.3' id 'pmd' } @@ -28,7 +28,7 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j: '2.17.1', - pmd : '6.41.0', + pmd : '6.42.0', ] repositories { @@ -42,22 +42,27 @@ dependencies { kapt(semverProcessor) compileOnly(semverProcessor) + // PircBotX implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' + // Commons implementation 'org.apache.commons:commons-lang3:3.12.0' implementation 'org.apache.commons:commons-text:1.9' - implementation 'commons-cli:commons-cli:1.5.0' implementation 'commons-codec:commons-codec:1.15' implementation 'commons-net:commons-net:3.8.0' - implementation 'com.google.code.gson:gson:2.8.9' + // Google + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:31.0.1-jre' - implementation(platform("org.jetbrains.kotlin:kotlin-bom")) + // Kotlin + implementation platform('org.jetbrains.kotlin:kotlin-bom') + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0' - implementation 'org.slf4j:slf4j-api:1.7.33' + // Logging + implementation 'org.slf4j:slf4j-api:1.7.36' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" @@ -117,9 +122,9 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { } tasks.named("dependencyUpdates").configure { - rejectVersionIf { - isNonStable(it.candidate.version) - } + rejectVersionIf { + isNonStable(it.candidate.version) + } } pmd { diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 5f0626e..148cb7e 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -20,6 +20,9 @@ MagicNumber:Cycle.kt$Cycle$10 MagicNumber:Cycle.kt$Cycle$1000L MagicNumber:Ignore.kt$Ignore$8 + MagicNumber:Info.kt$Info.Companion$30 + MagicNumber:Info.kt$Info.Companion$365 + MagicNumber:Info.kt$Info.Companion$7 MagicNumber:Mobibot.kt$Mobibot$8 MagicNumber:Modules.kt$Modules$7 MagicNumber:StockQuote.kt$StockQuote.Companion$10 @@ -29,9 +32,6 @@ MagicNumber:Twitter.kt$Twitter$60L MagicNumber:TwitterOAuth.kt$TwitterOAuth$401 MagicNumber:Users.kt$Users$8 - MagicNumber:Utils.kt$Utils$30 - MagicNumber:Utils.kt$Utils$365 - MagicNumber:Utils.kt$Utils$7 MagicNumber:Weather2.kt$Weather2.Companion$1.60934 MagicNumber:Weather2.kt$Weather2.Companion$32 MagicNumber:Weather2.kt$Weather2.Companion$404 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e6e589..41dfb87 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/version.properties b/version.properties index 2f05c8d..8627346 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Jan 19 15:20:26 PST 2022 -version.buildmeta=001 +#Sat Feb 12 23:41:45 PST 2022 +version.buildmeta=023 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+001 +version.semver=0.8.0-rc+023 From 4972e8e5d888417959f2fa8105a1ecc1c75f9b2f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 14 Feb 2022 23:02:06 -0800 Subject: [PATCH 621/842] Added random URLs to help with CIs. --- src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt | 4 ++-- version.properties | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 4705f11..076ef1d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -46,7 +46,7 @@ class PinboardTest : LocalProperties() { @Test fun testPinboard() { val apiToken = getProperty("pinboard-api-token") - val url = "https://www.example.com/" + val url = "https://www.example.com/${(1000..5000).random()}" val ircServer = "irc.test.com" val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) @@ -55,7 +55,7 @@ class PinboardTest : LocalProperties() { pinboard.addPin(ircServer, entry) assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "validate add") - entry.link = "https://www.foo.com/" + entry.link = "https://www.example.com/${(5001..9999).random()}" pinboard.updatePin(ircServer, url, entry) assertTrue(validatePin(apiToken, url = entry.link, ircServer), "validate update") diff --git a/version.properties b/version.properties index 8627346..a8ed375 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Feb 12 23:41:45 PST 2022 -version.buildmeta=023 +#Mon Feb 14 23:01:11 PST 2022 +version.buildmeta=024 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+023 +version.semver=0.8.0-rc+024 From fa34f47dabb3425d0e01b3e5910c3b500259b7a9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 23 Mar 2022 11:58:18 -0700 Subject: [PATCH 622/842] Moved to JDK 18. --- .github/workflows/gradle.yml | 2 +- .idea/misc.xml | 2 +- .idea/runConfigurations.xml | 10 ---------- bitbucket-pipelines.yml | 2 +- build.gradle | 12 ++++++------ gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 6 +++--- .../mobibot/modules/CurrencyConverterTest.kt | 3 ++- version.properties | 6 +++--- 11 files changed, 19 insertions(+), 28 deletions(-) delete mode 100644 .idea/runConfigurations.xml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c814746..7f27aec 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - java-version: [ 11, 17 ] + java-version: [ 11, 17, 18 ] steps: - uses: actions/checkout@v2 diff --git a/.idea/misc.xml b/.idea/misc.xml index 76aaf0c..bd2fb6f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,5 +5,5 @@
    - + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 797acea..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 7bd4e82..3b82903 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,4 +1,4 @@ -image: openjdk:17 +image: openjdk:18 pipelines: default: diff --git a/build.gradle b/build.gradle index 5527079..846da30 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.19.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.6.10' - id 'org.jetbrains.kotlin.kapt' version '1.6.10' + id 'org.jetbrains.kotlin.jvm' version '1.6.20-RC' + id 'org.jetbrains.kotlin.kapt' version '1.6.20-RC' id 'org.jetbrains.kotlinx.kover' version '0.5.0' id 'org.sonarqube' version '3.3' id 'pmd' @@ -27,8 +27,8 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.17.1', - pmd : '6.42.0', + log4j: '2.17.2', + pmd : '6.43.0', ] repositories { @@ -54,7 +54,7 @@ dependencies { // Google implementation 'com.google.code.gson:gson:2.9.0' - implementation 'com.google.guava:guava:31.0.1-jre' + implementation 'com.google.guava:guava:31.1-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') @@ -71,7 +71,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20211205' + implementation 'org.json:json:20220320' implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.twitter4j:twitter4j-core:4.0.7' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 8958 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87..00e33ed 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 5565ecb..4204e37 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -43,7 +43,7 @@ import static net.thauvin.erik.mobibot.Utils.bold; /** * The War module. * - * @author Erik C. Thauvin + * @author Erik C. Thauvin * @since 1.0 */ public final class War extends AbstractModule { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index e0e6aa3..d05d9fd 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -39,7 +39,7 @@ import assertk.assertions.isInstanceOf import com.rometools.rome.io.FeedException import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed import org.testng.annotations.Test -import java.io.FileNotFoundException +import java.io.IOException import java.net.MalformedURLException import java.net.UnknownHostException @@ -64,8 +64,8 @@ class FeedReaderTest { assertThat { readFeed("https://www.example.com") }.isFailure().isInstanceOf(FeedException::class.java) - assertThat { readFeed("https://www.examples.com/foo") }.isFailure() - .isInstanceOf(FileNotFoundException::class.java) + assertThat { readFeed("https://www.thauvin.net/foo") }.isFailure() + .isInstanceOf(IOException::class.java) assertThat { readFeed("https://www.examplesfoo.com/") }.isFailure() .isInstanceOf(UnknownHostException::class.java) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 8ba5c3b..90e3e4e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -36,6 +36,7 @@ import assertk.assertThat import assertk.assertions.any import assertk.assertions.contains import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThan import assertk.assertions.isInstanceOf import assertk.assertions.matches import assertk.assertions.prop @@ -79,7 +80,7 @@ class CurrencyConverterTest { fun testCurrencyRates() { val rates = currencyRates() assertThat(rates).all { - size().isEqualTo(33) + size().isGreaterThan(30) any { it.matches("[A-Z]{3}: +[\\d.]+".toRegex()) } contains("EUR: 1") } diff --git a/version.properties b/version.properties index a8ed375..1c4029c 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Feb 14 23:01:11 PST 2022 -version.buildmeta=024 +#Wed Mar 23 11:53:53 PDT 2022 +version.buildmeta=035 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+024 +version.semver=0.8.0-rc+035 From c26d5f53d2626346fc91e510931f1b6e2b6e988a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 27 Mar 2022 12:35:55 -0700 Subject: [PATCH 623/842] Simplified games. --- config/detekt/baseline.xml | 2 ++ .../net/thauvin/erik/mobibot/modules/War.java | 20 ++++++------------- .../thauvin/erik/mobibot/commands/Versions.kt | 9 +++++---- .../net/thauvin/erik/mobibot/modules/Dice.kt | 10 ++++------ .../erik/mobibot/modules/RockPaperScissors.kt | 16 ++++----------- .../mobibot/modules/CurrencyConverterTest.kt | 1 - version.properties | 6 +++--- 7 files changed, 24 insertions(+), 40 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 148cb7e..fa308ea 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -19,6 +19,7 @@ MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8 MagicNumber:Cycle.kt$Cycle$10 MagicNumber:Cycle.kt$Cycle$1000L + MagicNumber:Dice.kt$Dice$6 MagicNumber:Ignore.kt$Ignore$8 MagicNumber:Info.kt$Info.Companion$30 MagicNumber:Info.kt$Info.Companion$365 @@ -72,5 +73,6 @@ TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException TooManyFunctions:Tell.kt$Tell : AbstractCommand + UnusedPrivateMember:Dice.kt$Dice$private val sides = 6 diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 4204e37..8b15438 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -52,16 +52,8 @@ public final class War extends AbstractModule { // War command private static final String WAR_CMD = "war"; - private static final String[] HEARTS = - {"🂱", "🂾", "🂽", "🂼", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲"}; - private static final String[] SPADES = - {"🂡", "🂮", "🂭", "🂬", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢"}; - private static final String[] DIAMONDS = - {"🃁", "🃎", "🃍", "🃌", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂"}; - private static final String[] CLUBS = - {"🃑", "🃞", "🃝", "🃜", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒"}; - - private static final String[][] DECK = {HEARTS, SPADES, DIAMONDS, CLUBS}; + private static final String[] DECK = {"A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3", "2"}; + private static final String[] SUITS = {"♥", "♠", "♦", "♣"}; /** * The default constructor. @@ -91,11 +83,11 @@ public final class War extends AbstractModule { int y; while (true) { - i = RANDOM.nextInt(HEARTS.length); - y = RANDOM.nextInt(HEARTS.length); + i = RANDOM.nextInt(DECK.length); + y = RANDOM.nextInt(DECK.length); - event.respond("you drew " + DECK[RANDOM.nextInt(DECK.length)][i]); - event.getBot().sendIRC().action(channel, "drew " + DECK[RANDOM.nextInt(DECK.length)][y]); + event.respond("you drew " + bold(DECK[i]) + SUITS[RANDOM.nextInt(SUITS.length)]); + event.getBot().sendIRC().action(channel, "drew " + bold(DECK[y]) + SUITS[RANDOM.nextInt(SUITS.length)]); if (i != y) { break; diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 2875e06..786f5fe 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -40,10 +40,11 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Versions : AbstractCommand() { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", - "Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") - + " (" + System.getProperty("os.arch") + ')', - "Runtime: " + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version") + "Version : ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "Platform : ${System.getProperty("os.name")} ${System.getProperty("os.version")}" + + " (${System.getProperty("os.arch")})", + "Runtime : ${System.getProperty("java.runtime.name")} ${System.getProperty("java.runtime.version")}" + + " (${System.getProperty("java.vendor")})" ) override val name = "versions" override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 9e7725d..7d471e5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -41,6 +41,7 @@ import org.pircbotx.hooks.types.GenericMessageEvent */ class Dice : AbstractModule() { override val name = "Dice" + private val sides = 6 override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val botRoll = roll() @@ -49,11 +50,11 @@ class Dice : AbstractModule() { val total = roll.first + roll.second with(event.bot()) { event.respond( - "you rolled ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]} for a total of ${total.bold()}" + "you rolled ${roll.first.bold()} and ${roll.second.bold()} for a total of ${total.bold()}" ) sendIRC().action( channel, - "rolled ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]} for a total of ${botTotal.bold()}" + "rolled ${botRoll.first.bold()} and ${botRoll.second.bold()} for a total of ${botTotal.bold()}" ) when (winLoseOrTie(botTotal, total)) { Result.WIN -> sendIRC().action(channel, "wins.") @@ -68,16 +69,13 @@ class Dice : AbstractModule() { } private fun roll(): Pair { - return (1..DICE_FACES.size).random() to (1..DICE_FACES.size).random() + return (1..6).random() to (1..6).random() } companion object { // Dice command private const val DICE_CMD = "dice" - // Dice faces - private val DICE_FACES = arrayOf("", "⚀", "⚁", "⚂", "⚃", "⚄", "⚅") - @JvmStatic fun winLoseOrTie(bot: Int, player: Int): Result { return when { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index c092548..a3e7045 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -32,8 +32,8 @@ package net.thauvin.erik.mobibot.modules +import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.helpFormat import org.pircbotx.hooks.types.GenericMessageEvent @@ -67,26 +67,19 @@ class RockPaperScissors : AbstractModule() { override fun beats(hand: Hands): Boolean { return hand == SCISSORS } - - override var emoji = "\u270A" }, PAPER("covers") { override fun beats(hand: Hands): Boolean { return hand == ROCK } - - override var emoji = "\u270B" }, SCISSORS("cuts") { override fun beats(hand: Hands): Boolean { return hand == PAPER } - - override var emoji = "\u270C" }; abstract fun beats(hand: Hands): Boolean - abstract var emoji: String } companion object { @@ -107,21 +100,20 @@ class RockPaperScissors : AbstractModule() { val hand = Hands.valueOf(cmd.uppercase()) val botHand = Hands.values()[(0..Hands.values().size).random()] with(event.bot()) { - sendIRC().message(channel, "${hand.emoji} vs. ${botHand.emoji}") when { hand == botHand -> { - sendIRC().action(channel, "tied.") + sendIRC().action(channel, "tied: ${hand.name} vs. ${botHand.name}") } hand.beats(botHand) -> { sendIRC().action( channel, - "lost. ${hand.name.capitalise()} ${hand.action} ${botHand.name.lowercase()}." + "lost: ${hand.name.bold()} ${hand.action} ${botHand.name}" ) } else -> { sendIRC().action( channel, - "wins. ${botHand.name.capitalise()} ${botHand.action} ${hand.name.lowercase()}." + "wins: ${botHand.name.bold()} ${botHand.action} ${hand.name}" ) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 90e3e4e..468a947 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -35,7 +35,6 @@ import assertk.all import assertk.assertThat import assertk.assertions.any import assertk.assertions.contains -import assertk.assertions.isEqualTo import assertk.assertions.isGreaterThan import assertk.assertions.isInstanceOf import assertk.assertions.matches diff --git a/version.properties b/version.properties index 1c4029c..c4d9f71 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Mar 23 11:53:53 PDT 2022 -version.buildmeta=035 +#Sun Mar 27 12:29:09 PDT 2022 +version.buildmeta=090 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+035 +version.semver=0.8.0-rc+090 From 61248387d8e11081f2d60fc78fc381603736bfb8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 27 Mar 2022 13:37:16 -0700 Subject: [PATCH 624/842] Simplified games output even more. --- .idea/codeStyles/Project.xml | 1 + .../net/thauvin/erik/mobibot/modules/War.java | 26 +++++++++---------- .../net/thauvin/erik/mobibot/modules/Dice.kt | 19 ++++++++------ version.properties | 6 ++--- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 3ad4dc5..36a760f 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/build.gradle b/build.gradle index d211984..80c727b 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j: '2.18.0', - pmd : '6.48.0', + pmd : '6.49.0', ] repositories { @@ -43,7 +43,9 @@ dependencies { compileOnly(semverProcessor) // PircBotX - implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' + //implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' + implementation fileTree(dir: 'lib', include: '*.jar') + // Commons implementation 'org.apache.commons:commons-lang3:3.12.0' @@ -76,7 +78,7 @@ dependencies { implementation 'org.twitter4j:twitter4j-core:4.0.7' // Thauvin - implementation 'net.thauvin.erik:cryptoprice:0.9.0' + implementation 'net.thauvin.erik:cryptoprice:1.0.0-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' diff --git a/deploy.sh b/deploy.sh index 11bbc7b..385b6bd 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,12 +1,11 @@ #!/bin/bash -DEPLOYDIR=/home/erik/mobitopia/mobibot - -if [ -f "deploy/mobibot.jar" ]; then - /bin/cp deploy/mobibot.jar $DEPLOYDIR - rm -rf $DEPLOYDIR/lib/*.jar - cp deploy/lib/*.jar $DEPLOYDIR/lib - chmod 755 $DEPLOYDIR/*.jar $DEPLOYDIR/lib/*.jar -else - echo "mobibot.jar not found." -fi +./gradlew deploy +[ $? -eq 0 ] && sftp nix3.thauvin.us <R^#6M36cdEr8W;#@ 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 vQ-eQTz$o74CJYm5j; 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 y`(mPBuM=|q{xbH> Date: Sat, 10 Sep 2022 10:29:17 -0700 Subject: [PATCH 649/842] Switched to CryptoPrice 1.0 --- build.gradle | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 80c727b..996fcf4 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,7 @@ dependencies { implementation 'org.twitter4j:twitter4j-core:4.0.7' // Thauvin - implementation 'net.thauvin.erik:cryptoprice:1.0.0-SNAPSHOT' + implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' diff --git a/version.properties b/version.properties index 66bd4d1..e5a4fa9 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Sep 03 13:51:38 PDT 2022 -version.buildmeta=356 +#Fri Sep 09 17:09:52 PDT 2022 +version.buildmeta=359 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+356 +version.semver=0.8.0-rc+359 From aa0c3dd81c6f4e224ec450f42c5e90ce31195d01 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 13 Sep 2022 13:00:17 -0700 Subject: [PATCH 650/842] Cleaned up usage and README --- README.md | 12 ++++++------ .../kotlin/net/thauvin/erik/mobibot/Constants.kt | 5 +++++ src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 9 ++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 91ae8bf..3dc3626 100644 --- a/README.md +++ b/README.md @@ -6,25 +6,25 @@ Some very basic instructions: ```sh - { clone with git or download the ZIP } + # clone with git or download the ZIP git clone https://github.com/ethauvin/mobibot.git cd mobibot - { build with gradle } + # build with gradle ./gradlew cd deploy - { configure the properties } + # configure the properties vi *.properties *.xml - { help } + # help java -jar mobibot.jar -h - { twitter oauth token request } + # twitter oauth token request java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth - { launch } + # launch /usr/bin/nohup java -jar mobibot.jar & ``` diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index f96e30b..6b82d55 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -55,6 +55,11 @@ object Constants { */ const val DEFAULT_SERVER = "irc.libera.chat" + /** + * CLI command for usage. + */ + const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar [OPTIONS]" + /** * Help command line argument. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 01d2548..10df273 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.helpFormat @@ -274,18 +275,20 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro try { commandLine = parser.parse(options, args) } catch (e: ParseException) { - System.err.println("CLI Parsing failed. Reason: ${e.message}") + System.err.println(e.message) + HelpFormatter().printHelp(Constants.CLI_CMD, options) exitProcess(1) } when { commandLine.hasOption(Constants.HELP_ARG[0]) -> { // Output the usage - HelpFormatter().printHelp(Mobibot::class.java.name, options) + HelpFormatter().printHelp(Constants.CLI_CMD, options) } commandLine.hasOption(Constants.VERSION_ARG[0]) -> { // Output the version - println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})") + println("${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})") println(ReleaseInfo.WEBSITE) } From af839918a1003a8d6b1cdeeb69464bdb1a6edc99 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 14 Sep 2022 01:43:43 -0700 Subject: [PATCH 651/842] Switched to kotlinx-cli from Apache Commons CLI --- build.gradle | 8 +- .../net/thauvin/erik/mobibot/Constants.kt | 7 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 166 ++++++++---------- website/index.html | 7 +- 4 files changed, 78 insertions(+), 110 deletions(-) diff --git a/build.gradle b/build.gradle index 996fcf4..adade90 100644 --- a/build.gradle +++ b/build.gradle @@ -46,12 +46,7 @@ dependencies { //implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' implementation fileTree(dir: 'lib', include: '*.jar') - // Commons - implementation 'org.apache.commons:commons-lang3:3.12.0' - implementation 'org.apache.commons:commons-text:1.9' - implementation 'commons-cli:commons-cli:1.5.0' - implementation 'commons-codec:commons-codec:1.15' implementation 'commons-net:commons-net:3.8.0' // Google @@ -62,12 +57,13 @@ dependencies { implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' + implementation'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' // Logging implementation 'org.slf4j:slf4j-api:2.0.0' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" - implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" + implementation "org.apache.logging.log4j:log4j-slf4j18-impl:$versions.log4j" implementation 'com.rometools:rome:1.18.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 6b82d55..543511f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -58,12 +58,7 @@ object Constants { /** * CLI command for usage. */ - const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar [OPTIONS]" - - /** - * Help command line argument. - */ - const val HELP_ARG = "help" + const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 10df273..2e686af 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -32,6 +32,9 @@ package net.thauvin.erik.mobibot +import kotlinx.cli.ArgParser +import kotlinx.cli.ArgType +import kotlinx.cli.default import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.capitalise @@ -77,13 +80,6 @@ import net.thauvin.erik.mobibot.modules.War import net.thauvin.erik.mobibot.modules.Weather2 import net.thauvin.erik.mobibot.modules.WorldTime import net.thauvin.erik.semver.Version -import org.apache.commons.cli.CommandLine -import org.apache.commons.cli.CommandLineParser -import org.apache.commons.cli.DefaultParser -import org.apache.commons.cli.HelpFormatter -import org.apache.commons.cli.Option -import org.apache.commons.cli.Options -import org.apache.commons.cli.ParseException import org.pircbotx.Configuration import org.pircbotx.PircBotX import org.pircbotx.hooks.ListenerAdapter @@ -245,104 +241,86 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro @Throws(Exception::class) fun main(args: Array) { // Set up the command line options - val options = Options() - .addOption( - Constants.HELP_ARG.substring(0, 1), - Constants.HELP_ARG, - false, - "print this help message" - ) - .addOption( - Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG, false, - "print debug & logging data directly to the console" - ) - .addOption( - Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg() - .argName("file") - .desc("use alternate properties file") - .longOpt(Constants.PROPS_ARG).build() - ) - .addOption( - Constants.VERSION_ARG.substring(0, 1), - Constants.VERSION_ARG, - false, - "print version info" - ) + val parser = ArgParser(Constants.CLI_CMD) + val debug by parser.option( + ArgType.Boolean, + Constants.DEBUG_ARG, + Constants.DEBUG_ARG.substring(0, 1), + "Print debug & logging data directly to the console" + ).default(false) + val property by parser.option( + ArgType.String, + Constants.PROPS_ARG, + Constants.PROPS_ARG.substring(0, 1), + "Use alternate properties file" + ).default("./mobibot.properties") + val version by parser.option( + ArgType.Boolean, + Constants.VERSION_ARG, + Constants.VERSION_ARG.substring(0, 1), + "Print version info" + ).default(false) // Parse the command line - val parser: CommandLineParser = DefaultParser() - val commandLine: CommandLine - try { - commandLine = parser.parse(options, args) - } catch (e: ParseException) { - System.err.println(e.message) - HelpFormatter().printHelp(Constants.CLI_CMD, options) - exitProcess(1) - } - when { - commandLine.hasOption(Constants.HELP_ARG[0]) -> { - // Output the usage - HelpFormatter().printHelp(Constants.CLI_CMD, options) - } + parser.parse(args) - commandLine.hasOption(Constants.VERSION_ARG[0]) -> { - // Output the version - println("${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})") - println(ReleaseInfo.WEBSITE) + if (version) { + // Output the version + println( + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + ) + println(ReleaseInfo.WEBSITE) + } else { + // Load the properties + val p = Properties() + try { + Files.newInputStream( + Paths.get(property) + ).use { fis -> + p.load(fis) + } + } catch (ignore: FileNotFoundException) { + System.err.println("Unable to find properties file.") + exitProcess(1) + } catch (ignore: IOException) { + System.err.println("Unable to open properties file.") + exitProcess(1) } + val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) + val channel = p.getProperty("channel") + val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) - else -> { - // Load the properties - val p = Properties() + // Redirect stdout and stderr + if (!debug) { try { - Files.newInputStream( - Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties")) - ).use { fis -> - p.load(fis) - } - } catch (ignore: FileNotFoundException) { - System.err.println("Unable to find properties file.") - exitProcess(1) + val stdout = PrintStream( + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true + ) + ), true + ) + System.setOut(stdout) } catch (ignore: IOException) { - System.err.println("Unable to open properties file.") + System.err.println("Unable to open output (stdout) log file.") exitProcess(1) } - val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) - val channel = p.getProperty("channel") - val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) - - // Redirect stdout and stderr - if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { - try { - val stdout = PrintStream( - BufferedOutputStream( - FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true - ) - ), true - ) - System.setOut(stdout) - } catch (ignore: IOException) { - System.err.println("Unable to open output (stdout) log file.") - exitProcess(1) - } - try { - val stderr = PrintStream( - BufferedOutputStream( - FileOutputStream("$logsDir$nickname.err", true) - ), true - ) - System.setErr(stderr) - } catch (ignore: IOException) { - System.err.println("Unable to open error (stderr) log file.") - exitProcess(1) - } + try { + val stderr = PrintStream( + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true + ) + System.setErr(stderr) + } catch (ignore: IOException) { + System.err.println("Unable to open error (stderr) log file.") + exitProcess(1) } - - // Start the bot - Mobibot(nickname, channel, logsDir, p).connect() } + + // Start the bot + Mobibot(nickname, channel, logsDir, p).connect() } } } diff --git a/website/index.html b/website/index.html index 979f5c8..acab3b3 100644 --- a/website/index.html +++ b/website/index.html @@ -36,12 +36,11 @@

    mobibot is making extensive use of various open source libraries, including:

    Some of the internal features include RSS feed backlogs, rolling logs, debugging toggle and much more.

    From 2b2d04683541b0661945f7b6e37e7e34f13473c0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 6 Dec 2022 15:38:14 -0800 Subject: [PATCH 688/842] Fixed Mastodon auto-post --- src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 858676e..41de68e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -52,7 +52,6 @@ class Mastodon : SocialModule() { get() = properties[HANDLE_PROP] override val isAutoPost: Boolean - get() = isEnabled && properties[Twitter.AUTO_POST_PROP].toBoolean() override val isValidProperties: Boolean get() { From e510a77af1c9d2b11c2f4fe0afbeb404aa4c0a25 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 7 Dec 2022 11:50:19 -0800 Subject: [PATCH 689/842] Added tags to Mastodon post --- config/detekt/baseline.xml | 1 + .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 4 +-- .../net/thauvin/erik/mobibot/Pinboard.kt | 21 ++++++------ .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 8 ++--- .../mobibot/commands/links/LinksManager.kt | 18 +++++------ .../erik/mobibot/commands/links/Posting.kt | 16 +++++----- .../erik/mobibot/commands/links/Tags.kt | 4 +-- .../erik/mobibot/commands/links/View.kt | 4 +-- .../erik/mobibot/entries/EntriesUtils.kt | 16 +++++----- .../thauvin/erik/mobibot/entries/EntryLink.kt | 20 ++++-------- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 17 +++++----- .../thauvin/erik/mobibot/modules/Twitter.kt | 10 ++---- .../erik/mobibot/entries/EntriesUtilsTest.kt | 32 +++++++++---------- .../erik/mobibot/entries/EntryLinkTest.kt | 19 +++++++---- version.properties | 6 ++-- 18 files changed, 99 insertions(+), 103 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index d0fbcc9..33a9f22 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -86,6 +86,7 @@ ThrowsCount:WolframAlpha.kt$WolframAlpha.Companion$@JvmStatic @Throws(ModuleException::class) fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException + TooManyFunctions:EntryLink.kt$EntryLink : Serializable TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter TooManyFunctions:Tell.kt$Tell : AbstractCommand diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index db5fb25..8720e95 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -46,8 +46,8 @@ import java.util.Properties */ class Addons(private val props: Properties) { private val logger: Logger = LoggerFactory.getLogger(Addons::class.java) - private val disabledModules = props.getProperty("disabled-modules", "").split(LinksManager.TAG_MATCH.toRegex()) - private val disableCommands = props.getProperty("disabled-commands", "").split(LinksManager.TAG_MATCH.toRegex()) + private val disabledModules = props.getProperty("disabled-modules", "").split(LinksManager.TAG_MATCH) + private val disableCommands = props.getProperty("disabled-commands", "").split(LinksManager.TAG_MATCH) val commands: MutableList = mutableListOf() val modules: MutableList = mutableListOf() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 9d46188..ee9b020 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -55,13 +55,9 @@ class Pinboard { if (poster.apiToken.isNotBlank()) { runBlocking { launch { - poster.addPin( - entry.link, - entry.title, - entry.postedBy(ircServer), - entry.pinboardTags, - entry.date.toTimestamp() - ) + with(entry) { + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) + } } } } @@ -98,7 +94,7 @@ class Pinboard { if (oldUrl != link) { poster.deletePin(oldUrl) } - poster.addPin(link, title, entry.postedBy(ircServer), pinboardTags, date.toTimestamp()) + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) } } } @@ -106,7 +102,7 @@ class Pinboard { } /** - * Format a date to a UTC timestamp. + * Formats a date to a UTC timestamp. */ private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( @@ -115,6 +111,13 @@ class Pinboard { ).format(DateTimeFormatter.ISO_INSTANT) } + /** + * Formats the tags for pinboard. + */ + private fun EntryLink.formatTags(): String { + return nick + formatTags(",", ",") + } + /** * Returns the pinboard.in extended attribution line. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 7306cea..5d03cb8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -141,7 +141,7 @@ class Ignore : AbstractCommand() { override fun setProperty(key: String, value: String) { super.setProperty(key, value) if (IGNORE_PROP == key) { - ignored.addAll(value.split(LinksManager.TAG_MATCH.toRegex())) + ignored.addAll(value.split(LinksManager.TAG_MATCH) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index e60955a..5a09836 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -38,7 +38,7 @@ import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import org.pircbotx.hooks.types.GenericMessageEvent @@ -111,7 +111,7 @@ class Comment : AbstractCommand() { if (event.isChannelOp(channel) && cmd.length > 1) { val comment = entry.getComment(commentIndex) comment.nick = cmd.substring(1) - event.sendMessage(buildComment(entryIndex, commentIndex, comment)) + event.sendMessage(printComment(entryIndex, commentIndex, comment)) LinksManager.entries.save() } else { event.sendMessage("Please ask a channel op to change the author of this comment for you.") @@ -142,11 +142,11 @@ class Comment : AbstractCommand() { event: GenericMessageEvent ) { entry.setComment(commentIndex, cmd, event.user.nick) - event.sendMessage(buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) LinksManager.entries.save() } private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) { - event.sendMessage(buildComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index d0c9769..af6adf4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -42,7 +42,7 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored import net.thauvin.erik.mobibot.entries.Entries -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.mobibot.social.SocialManager @@ -65,10 +65,10 @@ class LinksManager : AbstractCommand() { } companion object { - const val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*" + val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex() const val KEYWORDS_PROP = "tags-keywords" const val TAGS_PROP = "tags" - const val TAG_MATCH = ", *| +" + val TAG_MATCH = ", *| +".toRegex() /** * Entries array @@ -116,7 +116,7 @@ class LinksManager : AbstractCommand() { val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) title = data[0].trim() if (data.size > 1) { - tags.addAll(data[1].split(TAG_MATCH.toRegex())) + tags.addAll(data[1].split(TAG_MATCH)) } } @@ -136,7 +136,7 @@ class LinksManager : AbstractCommand() { val entry = EntryLink(link, title, sender, login, channel, tags) entries.links.add(entry) val index = entries.links.lastIndexOf(entry) - event.sendMessage(buildLink(index, entry)) + event.sendMessage(printLink(index, entry)) pinboard.addPin(event.bot().serverHostname, entry) @@ -156,7 +156,7 @@ class LinksManager : AbstractCommand() { override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false override fun matches(message: String): Boolean { - return message.matches(LINK_MATCH.toRegex()) + return message.matches(LINK_MATCH) } internal fun fetchTitle(link: String): String { @@ -179,7 +179,7 @@ class LinksManager : AbstractCommand() { return try { val match = entries.links.single { it.link == link } event.sendMessage( - "Duplicate".bold() + " >> " + buildLink(entries.links.indexOf(match), match) + "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) ) true } catch (ignore: NoSuchElementException) { @@ -200,9 +200,9 @@ class LinksManager : AbstractCommand() { override fun setProperty(key: String, value: String) { super.setProperty(key, value) if (KEYWORDS_PROP == key) { - keywords.addAll(value.split(TAG_MATCH.toRegex())) + keywords.addAll(value.split(TAG_MATCH)) } else if (TAGS_PROP == key) { - defaultTags.addAll(value.split(TAG_MATCH.toRegex())) + defaultTags.addAll(value.split(TAG_MATCH)) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index a0224b9..9368d37 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -94,7 +94,7 @@ class Posting : AbstractCommand() { val entry: EntryLink = entries.links[entryIndex] val commentIndex = entry.addComment(cmd, event.user.nick) val comment = entry.getComment(commentIndex) - event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment)) + event.sendMessage(EntriesUtils.printComment(entryIndex, commentIndex, comment)) entries.save() } @@ -103,7 +103,7 @@ class Posting : AbstractCommand() { val entry: EntryLink = entries.links[entryIndex] entry.title = cmd.substring(1).trim() LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.buildLink(entryIndex, entry)) + event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) entries.save() } } @@ -112,11 +112,11 @@ class Posting : AbstractCommand() { val entry: EntryLink = entries.links[entryIndex] if (entry.login == event.user.login || event.isChannelOp(channel)) { val link = cmd.substring(1) - if (link.matches(LinksManager.LINK_MATCH.toRegex())) { + if (link.matches(LinksManager.LINK_MATCH)) { val oldLink = entry.link entry.link = link LinksManager.pinboard.updatePin(event.bot().serverHostname, oldLink, entry) - event.sendMessage(EntriesUtils.buildLink(entryIndex, entry)) + event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) entries.save() } } @@ -128,7 +128,7 @@ class Posting : AbstractCommand() { val entry: EntryLink = entries.links[index] entry.nick = cmd.substring(1) LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.buildLink(index, entry)) + event.sendMessage(EntriesUtils.printLink(index, entry)) entries.save() } } else { @@ -151,14 +151,14 @@ class Posting : AbstractCommand() { private fun showEntry(index: Int, event: GenericMessageEvent) { val entry: EntryLink = entries.links[index] - event.sendMessage(EntriesUtils.buildLink(index, entry)) + event.sendMessage(EntriesUtils.printLink(index, entry)) if (entry.tags.isNotEmpty()) { - event.sendMessage(EntriesUtils.buildTags(index, entry)) + event.sendMessage(EntriesUtils.printTags(index, entry)) } if (entry.comments.isNotEmpty()) { val comments = entry.comments for (i in comments.indices) { - event.sendMessage(EntriesUtils.buildComment(index, i, comments[i])) + event.sendMessage(EntriesUtils.printComment(index, i, comments[i])) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 8146e88..785e3a0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -67,14 +67,14 @@ class Tags : AbstractCommand() { if (entry.login == event.user.login || event.isChannelOp(channel)) { entry.setTags(cmd) LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.buildTags(index, entry)) + event.sendMessage(EntriesUtils.printTags(index, entry)) LinksManager.entries.save() } else { event.sendMessage("Please ask a channel op to change the tags for you.") } } else { if (entry.tags.isNotEmpty()) { - event.sendMessage(EntriesUtils.buildTags(index, entry)) + event.sendMessage(EntriesUtils.printTags(index, entry)) } else { event.sendMessage("The entry has no tags. Why don't add some?") } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index ee817a0..008ccad 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -97,11 +97,11 @@ class View : AbstractCommand() { entry = entries.links[index] if (query.isNotBlank()) { if (entry.matches(query)) { - event.sendMessage(EntriesUtils.buildLink(index, entry, true)) + event.sendMessage(EntriesUtils.printLink(index, entry, true)) sent++ } } else { - event.sendMessage(EntriesUtils.buildLink(index, entry, true)) + event.sendMessage(EntriesUtils.printLink(index, entry, true)) sent++ } index++ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 2b96dac..ac2c259 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -40,18 +40,18 @@ import net.thauvin.erik.mobibot.Utils.green */ object EntriesUtils { /** - * Builds an entry's comment for display on the channel. + * Prints an entry's comment for display on the channel. */ @JvmStatic - fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = + fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** - * Builds an entry's link for display on the channel. + * Prints an entry's link for display on the channel. */ @JvmStatic @JvmOverloads - fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { + fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") .append('[').append(entry.nick).append(']') if (isView && entry.comments.isNotEmpty()) { @@ -70,14 +70,14 @@ object EntriesUtils { } /** - * Build an entry's tags/categories for display on the channel. + * Prints an entry's tags/categories for display on the channel. e.g. L1T: tag1, tag2 */ @JvmStatic - fun buildTags(entryIndex: Int, entry: EntryLink): String = - entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ") + fun printTags(entryIndex: Int, entry: EntryLink): String = + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") /** - * Build link label based on its index. e.g: L1 + * Builds link label based on its index. e.g: L1 */ @JvmStatic fun Int.toLinkLabel(): String = Constants.LINK_CMD + (this + 1) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index b70fa9d..59f4e73 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -127,24 +127,18 @@ class EntryLink( return comments.remove(entryComment) } + /** + * Formats the tags. + */ + fun formatTags(sep: String, prefix: String = "") : String { + return tags.joinToString(separator = sep, prefix = prefix){it.name} + } + /** * Returns a comment. */ fun getComment(index: Int): EntryComment = comments[index] - /** - * Returns the tags formatted for pinboard.in - */ - val pinboardTags: String - get() { - val pinboardTags = StringBuilder(nick) - for (tag in tags) { - pinboardTags.append(',') - pinboardTags.append(tag.name) - } - return pinboardTags.toString() - } - /** * Returns true if a string is contained in the link, title, or nick. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index 32ba16d..1ae2690 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -160,7 +160,7 @@ class FeedsManager private constructor() { item.description = SyndContentImpl().apply { value = buff.toString() } item.title = title item.publishedDate = date - item.author = "${channel.substring(1)}@${entries.ircServer} ($nick)" + item.author = "${channel.removePrefix("#")}@${entries.ircServer} ($nick)" item.categories = tags items.add(item) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 83eafcf..61e2eaf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -113,7 +113,7 @@ abstract class AbstractModule { */ open val isValidProperties: Boolean get() { - for (s in propertyKeys) { + for (s in properties.keys) { if (properties[s].isNullOrBlank()) { return false } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 41de68e..b355fdc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -52,22 +52,21 @@ class Mastodon : SocialModule() { get() = properties[HANDLE_PROP] override val isAutoPost: Boolean + get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() override val isValidProperties: Boolean - get() { - for (s in propertyKeys) { - if (AUTO_POST_PROP != s && HANDLE_PROP != s && properties[s].isNullOrBlank()) { - return false - } - } - return true - } + get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank()) /** * Formats the entry for posting. */ override fun formatEntry(entry: EntryLink): String { - return "${entry.title} via ${entry.nick} on ${entry.channel}\n\n${entry.link}" + return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}" + } + + private fun formatTags(entry: EntryLink): String { + return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } + .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } } /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 91cca2a..2253494 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -49,14 +49,8 @@ class Twitter : SocialModule() { get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() override val isValidProperties: Boolean - get() { - for (s in propertyKeys) { - if (AUTO_POST_PROP != s && HANDLE_PROP != s && properties[s].isNullOrBlank()) { - return false - } - } - return true - } + get() = !(properties[CONSUMER_KEY_PROP].isNullOrBlank() || properties[CONSUMER_SECRET_PROP].isNullOrBlank() + || properties[TOKEN_PROP].isNullOrBlank() || properties[TOKEN_SECRET_PROP].isNullOrBlank()) /** * Formats the entry for posting. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 0de9d5e..23ba01e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -36,9 +36,9 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildComment -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink -import net.thauvin.erik.mobibot.entries.EntriesUtils.buildTags +import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import org.testng.annotations.Test @@ -60,33 +60,33 @@ class EntriesUtilsTest { } @Test(groups = ["entries"]) - fun buildLinkLabelTest() { - assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") + fun printCommentTest() { + assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") } @Test(groups = ["entries"]) - fun buildCommentTest() { - assertThat(buildComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") - } - - @Test(groups = ["entries"]) - fun buildLinkTest() { + fun printLinkTest() { for (i in links.indices) { assertThat( - buildLink(i - 1, links[i]), "link $i" + printLink(i - 1, links[i]), "link $i" ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") } assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0) - assertThat(buildLink(0, links.first(), isView = true), "buildLink(isView=true)").contains("[+1]") + assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]") } @Test(groups = ["entries"]) - fun buildTagsTest() { + fun printTagsTest() { for (i in links.indices) { assertThat( - buildTags(i - 1, links[i]), "tag $i" - ).isEqualTo("L${i}T: Skynx$i, tag1, tag2, tag3, tag4, tag5") + printTags(i - 1, links[i]), "tag $i" + ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") } } + + @Test(groups = ["entries"]) + fun toLinkLabelTest() { + assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 5c9ef99..b36d167 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -40,6 +40,7 @@ import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.assertions.prop import assertk.assertions.size +import assertk.assertions.startsWith import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import org.testng.annotations.Test @@ -94,13 +95,11 @@ class EntryLinkTest { @Test(groups = ["entries"]) fun testConstructor() { - val tag = "test" - val tags = listOf(SyndCategoryImpl().apply { name = tag }) + val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" }) val link = EntryLink("link", "title", "nick", "channel", Date(), tags) assertThat(link, "link").all { prop(EntryLink::tags).size().isEqualTo(tags.size) - prop(EntryLink::tags).index(0).prop(SyndCategory::getName).isEqualTo(tag) - prop(EntryLink::pinboardTags).isEqualTo("nick,$tag") + prop(EntryLink::tags).index(0).prop(SyndCategory::getName).isEqualTo("tag1") } } @@ -122,11 +121,17 @@ class EntryLinkTest { assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}") } assertThat(entryLink::tags).size().isEqualTo(5) - entryLink.setTags("-tag5") + entryLink.setTags("-tag5, tag4") entryLink.setTags("+mobitopia") - entryLink.setTags("tag4") entryLink.setTags("-mobitopia") - assertThat(entryLink::pinboardTags).isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia") + assertThat( + entryLink.formatTags(","), + "formatTags(',')" + ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") + entryLink.setTags("-tag4 tag5") + assertThat( + entryLink.formatTags(" ", ","), "formatTag(' ',',')" + ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") val size = entryLink.tags.size entryLink.setTags("") assertThat(entryLink.tags, "setTags('')").size().isEqualTo(size) diff --git a/version.properties b/version.properties index d91b88d..edb9391 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Dec 05 21:57:24 PST 2022 -version.buildmeta=814 +#Wed Dec 07 02:53:01 PST 2022 +version.buildmeta=857 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+814 +version.semver=0.8.0-rc+857 From 0fba4445f891c017323dde10d276bdc68dc0d03a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 7 Dec 2022 12:08:15 -0800 Subject: [PATCH 690/842] Removed unnecessary threading (ThreadedModule, etc.) --- config/detekt/baseline.xml | 4 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 20 +++---- .../net/thauvin/erik/mobibot/Pinboard.kt | 32 +++------- .../erik/mobibot/commands/ChannelFeed.kt | 8 +-- .../thauvin/erik/mobibot/commands/Cycle.kt | 11 ++-- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 10 +--- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 4 +- .../erik/mobibot/modules/CryptoPrices.kt | 4 +- .../erik/mobibot/modules/CurrencyConverter.kt | 4 +- .../erik/mobibot/modules/GoogleSearch.kt | 4 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 18 +----- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 4 +- .../erik/mobibot/modules/ThreadedModule.kt | 58 ------------------- .../thauvin/erik/mobibot/modules/Twitter.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 4 +- .../erik/mobibot/modules/WolframAlpha.kt | 4 +- .../erik/mobibot/social/SocialManager.kt | 4 +- .../erik/mobibot/social/SocialModule.kt | 44 ++++++-------- .../erik/mobibot/entries/EntryLinkTest.kt | 1 - version.properties | 6 +- 24 files changed, 73 insertions(+), 181 deletions(-) delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 33a9f22..19b4dbd 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -54,7 +54,7 @@ NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>) NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml) - NestedBlockDepth:GoogleSearch.kt$GoogleSearch$override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) + NestedBlockDepth:GoogleSearch.kt$GoogleSearch$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> NestedBlockDepth:LinksManager.kt$LinksManager$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) @@ -66,7 +66,7 @@ NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$@JvmStatic fun main(args: Array<String>) NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadData(file: String, default: Any, logger: Logger, description: String): Any NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveData(file: String, data: Any, logger: Logger, description: String) - NestedBlockDepth:Weather2.kt$Weather2$override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) + NestedBlockDepth:Weather2.kt$Weather2$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$ioe PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$te diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index eb00137..fc43dbc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -65,7 +65,7 @@ class FeedReader(private val url: String, val event: GenericMessageEvent) : Runn event.sendMessage("An error has occurred while parsing the feed: ${e.message}") } catch (e: IOException) { if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e) - event.sendMessage("An error has occurred while fetching the feed: ${e.message}") + event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}") } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index bae8403..fcd2d08 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -176,7 +176,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro override fun onDisconnect(event: DisconnectEvent?) { event?.let { with(event.getBot()) { - LinksManager.socialManager.notification("$nick disconnected from irc://$serverHostname") + LinksManager.socialManager.notification("$nick disconnected from $serverHostname") seen.add(userChannelDao.getChannel(channel).users) } } @@ -202,7 +202,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has joined ${event.channel.name} on irc://$serverHostname" + "$nick has joined ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -215,12 +215,10 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro override fun onMessage(event: MessageEvent?) { event?.user?.let { user -> - val sender = user.nick - val message = event.message tell.send(event) - if (message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: - if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") - val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2) + if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: + if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") + val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2) val cmd = cmds[0].lowercase() val args = cmds.lastOrEmpty().trim() if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help @@ -230,10 +228,10 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.exec(channel, cmd, args, event) } } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. - if (logger.isTraceEnabled) logger.trace(">>> $sender: $message") + if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") } - storeRecap(sender, message, false) - seen.add(sender) + storeRecap(user.nick, event.message, false) + seen.add(user.nick) } } @@ -252,7 +250,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has left ${event.channel.name} on irc://$serverHostname" + "$nick has left ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index ee9b020..1bc719b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -32,8 +32,6 @@ package net.thauvin.erik.mobibot -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.pinboard.PinboardPoster import java.time.ZoneId @@ -53,12 +51,8 @@ class Pinboard { */ fun addPin(ircServer: String, entry: EntryLink) { if (poster.apiToken.isNotBlank()) { - runBlocking { - launch { - with(entry) { - poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) - } - } + with(entry) { + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) } } } @@ -75,12 +69,9 @@ class Pinboard { */ fun deletePin(entry: EntryLink) { if (poster.apiToken.isNotBlank()) { - runBlocking { - launch { - poster.deletePin(entry.link) - } - } + poster.deletePin(entry.link) } + } /** @@ -88,15 +79,11 @@ class Pinboard { */ fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) { if (poster.apiToken.isNotBlank()) { - runBlocking { - launch { - with(entry) { - if (oldUrl != link) { - poster.deletePin(oldUrl) - } - poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) - } + with(entry) { + if (oldUrl != link) { + poster.deletePin(oldUrl) } + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) } } } @@ -106,8 +93,7 @@ class Pinboard { */ private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( - toInstant().truncatedTo(ChronoUnit.SECONDS), - ZoneId.systemDefault() + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() ).format(DateTimeFormatter.ISO_INSTANT) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 3cc1b1a..8412af0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -32,8 +32,6 @@ package net.thauvin.erik.mobibot.commands -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.FeedReader import net.thauvin.erik.mobibot.Utils.helpFormat import org.pircbotx.hooks.types.GenericMessageEvent @@ -55,11 +53,7 @@ class ChannelFeed(channel: String) : AbstractCommand() { override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isEnabled()) { - runBlocking { - launch { - properties[FEED_PROP]?.let { FeedReader(it, event).run() } - } - } + properties[FEED_PROP]?.let { FeedReader(it, event).run() } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 20a49d2..31a9c65 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot.commands import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat @@ -51,10 +52,12 @@ class Cycle : AbstractCommand() { with(event.bot()) { if (event.isChannelOp(channel)) { runBlocking { - sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!") - userChannelDao.getChannel(channel).send().part() - delay(wait * 1000L) - sendIRC().joinChannel(channel) + launch { + sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!") + userChannelDao.getChannel(channel).send().part() + delay(wait * 1000L) + sendIRC().joinChannel(channel) + } } } else { helpResponse(channel, args, event) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 5d03cb8..f47f057 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -141,7 +141,7 @@ class Ignore : AbstractCommand() { override fun setProperty(key: String, value: String) { super.setProperty(key, value) if (IGNORE_PROP == key) { - ignored.addAll(value.split(LinksManager.TAG_MATCH) + ignored.addAll(value.split(LinksManager.TAG_MATCH)) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index fddb9be..66c4ebf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -45,15 +45,7 @@ class Users : AbstractCommand() { override val isVisible = true override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val nicks = mutableListOf() val ch = event.bot().userChannelDao.getChannel(channel) - ch.users.forEach { - if (it.channelsOpIn.contains(ch)) { - nicks.add("@${it.nick}") - } else { - nicks.add(it.nick) - } - } - event.sendList(nicks, 8) + event.sendList(ch.users.map { if (it.channelsOpIn.contains(ch)) "@${it.nick}" else it.nick }, 8) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index a504c92..7d8aaed 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -37,7 +37,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter /** - * The `TellMessage` class. + * Tell Message. */ class TellMessage( /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 40021e3..c464928 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -46,12 +46,12 @@ import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse -class ChatGpt : ThreadedModule() { +class ChatGpt : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) override val name = "ChatGPT" - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { event.sendMessage( diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 7f9e4b7..66a33ae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -46,7 +46,7 @@ import java.io.IOException /** * The Cryptocurrency Prices module. */ -class CryptoPrices : ThreadedModule() { +class CryptoPrices : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) override val name = "CryptoPrices" @@ -55,7 +55,7 @@ class CryptoPrices : ThreadedModule() { * Returns the cryptocurrency market price from * [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price). */ - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (CURRENCIES.isEmpty()) { try { loadCurrencies() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 712aedf..3d0bf0a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -52,7 +52,7 @@ import java.util.TreeMap /** * The CurrencyConverter module. */ -class CurrencyConverter : ThreadedModule() { +class CurrencyConverter : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) override val name = "CurrencyConverter" @@ -71,7 +71,7 @@ class CurrencyConverter : ThreadedModule() { /** * Converts the specified currencies. */ - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { reload() if (SYMBOLS.isEmpty()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 7499ec8..76451db 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -54,7 +54,7 @@ import java.net.URL /** * The GoogleSearch module. */ -class GoogleSearch : ThreadedModule() { +class GoogleSearch : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) override val name = "GoogleSearch" @@ -62,7 +62,7 @@ class GoogleSearch : ThreadedModule() { /** * Searches Google. */ - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { val results = searchGoogle( diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index e85a914..202dc68 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -31,8 +31,6 @@ */ package net.thauvin.erik.mobibot.modules -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import net.thauvin.erik.jokeapi.exceptions.HttpErrorException import net.thauvin.erik.jokeapi.exceptions.JokeException import net.thauvin.erik.jokeapi.getJoke @@ -52,21 +50,15 @@ import java.io.IOException /** * The Joke module. */ -class Joke : ThreadedModule() { +class Joke : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) override val name = "Joke" - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - runBlocking { - launch { run(channel, cmd, args, event) } - } - } - /** * Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/). */ - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { with(event.bot()) { try { randomJoke().forEach { @@ -92,12 +84,8 @@ class Joke : ThreadedModule() { @Throws(ModuleException::class) fun randomJoke(): List { return try { - val messages = mutableListOf() val joke = getJoke(safe = true, type = Type.SINGLE, splitNewLine = true) - joke.joke.forEach { - messages.add(PublicMessage(it, Colors.CYAN)) - } - messages + joke.joke.map { PublicMessage(it, Colors.CYAN) } } catch (e: JokeException) { throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e) } catch (e: HttpErrorException) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index b355fdc..ae805cd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -93,7 +93,7 @@ class Mastodon : SocialModule() { private const val MASTODON_CMD = "mastodon" /** - * Toots on Mastodon. + * Post on Mastodon. */ @JvmStatic @Throws(ModuleException::class) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 4c2859f..ffca08b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -52,7 +52,7 @@ import java.net.URL /** * The StockQuote module. */ -class StockQuote : ThreadedModule() { +class StockQuote : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) override val name = "StockQuote" @@ -60,7 +60,7 @@ class StockQuote : ThreadedModule() { /** * Returns the specified stock quote from Alpha Vantage. */ - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt deleted file mode 100644 index 269b24d..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ThreadedModule.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * ThreadedModule.kt - * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.pircbotx.hooks.types.GenericMessageEvent - -/** - * The `ThreadedModule` class. - */ -abstract class ThreadedModule : AbstractModule() { - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (isEnabled && event.message.isNotEmpty()) { - runBlocking { - launch { - run(channel, cmd, args, event) - } - } - } else { - helpResponse(event) - } - } - - /** - * Runs the thread. - */ - abstract fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 2253494..28f9cde 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -88,7 +88,7 @@ class Twitter : SocialModule() { private const val TWITTER_CMD = "twitter" /** - * Tweets on Twitter. + * Post on Twitter. */ @JvmStatic @Throws(ModuleException::class) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 8dcdc15..5c5ae67 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -54,7 +54,7 @@ import kotlin.math.roundToInt /** * The `Weather2` module. */ -class Weather2 : ThreadedModule() { +class Weather2 : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) override val name = "Weather" @@ -62,7 +62,7 @@ class Weather2 : ThreadedModule() { /** * Fetches the weather data from a specific city. */ - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { val messages = getWeather(args, properties[OWM_API_KEY_PROP]) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 1d5abd3..56d3a3d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -43,7 +43,7 @@ import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL -class WolframAlpha : ThreadedModule() { +class WolframAlpha : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java) override val name = "WolframAlpha" @@ -56,7 +56,7 @@ class WolframAlpha : ThreadedModule() { } } - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { val query = args.trim().split("units=", limit = 2, ignoreCase = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index c991140..f3a86fe 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -110,8 +110,8 @@ class SocialManager { */ fun shutdown() { timer.cancel() - for (index in entries) { - postEntry(index) + entries.forEach { + postEntry(it) } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index c15dd55..e31ccdc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -32,18 +32,16 @@ package net.thauvin.erik.mobibot.social -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import net.thauvin.erik.mobibot.commands.links.LinksManager import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.modules.AbstractModule import net.thauvin.erik.mobibot.modules.ModuleException -import net.thauvin.erik.mobibot.modules.ThreadedModule import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory -abstract class SocialModule : ThreadedModule() { +abstract class SocialModule : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) abstract val handle: String? @@ -56,15 +54,11 @@ abstract class SocialModule : ThreadedModule() { */ fun notification(msg: String) { if (isEnabled && !handle.isNullOrBlank()) { - runBlocking { - launch { - try { - post(message = msg, isDm = true) - if (logger.isDebugEnabled) logger.debug("Notified $handle on $name: $msg") - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn("Failed to notify $handle on $name: $msg", e) - } - } + try { + post(message = msg, isDm = true) + if (logger.isDebugEnabled) logger.debug("Notified $handle on $name: $msg") + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn("Failed to notify $handle on $name: $msg", e) } } } @@ -76,25 +70,21 @@ abstract class SocialModule : ThreadedModule() { */ fun postEntry(index: Int) { if (isAutoPost && LinksManager.entries.links.size >= index) { - runBlocking { - launch { - try { - if (logger.isDebugEnabled) { - logger.debug("Posting {} to $name.", index.toLinkLabel()) - } - post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn( - "Failed to post entry ${index.toLinkLabel()} on $name.", - e - ) - } + try { + if (logger.isDebugEnabled) { + logger.debug("Posting {} to $name.", index.toLinkLabel()) } + post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn( + "Failed to post entry ${index.toLinkLabel()} on $name.", + e + ) } } } - override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { try { event.respond(post("$args (by ${event.user.nick} on $channel)", false)) } catch (e: ModuleException) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index b36d167..8c1a862 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -40,7 +40,6 @@ import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.assertions.prop import assertk.assertions.size -import assertk.assertions.startsWith import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import org.testng.annotations.Test diff --git a/version.properties b/version.properties index edb9391..4eb40f3 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Dec 07 02:53:01 PST 2022 -version.buildmeta=857 +#Sat Dec 10 10:16:56 PST 2022 +version.buildmeta=874 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+857 +version.semver=0.8.0-rc+874 From db931f1f23a1dfd7c418b587aaf9b29c615383c7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 13 Dec 2022 23:14:52 -0800 Subject: [PATCH 691/842] Added stumped response from ChatGPT --- .../net/thauvin/erik/mobibot/modules/ChatGpt.kt | 12 ++++++------ version.properties | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index c464928..8e8423a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -54,12 +54,12 @@ class ChatGpt : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - event.sendMessage( - chat( - args.trim(), - properties[CHATGPT_API_KEY] - ) - ) + val answer = chat(args.trim(), properties[CHATGPT_API_KEY]) + if (answer.isNotBlank()) { + event.sendMessage(answer) + } else { + event.respond("ChatGPT is stumped.") + } } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) e.message?.let { diff --git a/version.properties b/version.properties index 4eb40f3..2090357 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Dec 10 10:16:56 PST 2022 -version.buildmeta=874 +#Tue Dec 13 01:22:17 PST 2022 +version.buildmeta=876 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+874 +version.semver=0.8.0-rc+876 From 7f7fd43b8da74db08ced20cd504ff159af4ff800 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 4 Jan 2023 02:54:13 -0800 Subject: [PATCH 692/842] Added tweet and toot commands --- .../kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt | 6 ++++-- src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt | 6 ++++-- website/index.html | 6 +++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index ae805cd..9a4e914 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -91,6 +91,7 @@ class Mastodon : SocialModule() { const val INSTANCE_PROP = "mastodon-instance" private const val MASTODON_CMD = "mastodon" + private const val TOOT_CMD = "toot" /** * Post on Mastodon. @@ -140,8 +141,9 @@ class Mastodon : SocialModule() { init { commands.add(MASTODON_CMD) - help.add("To post to Mastodon:") - help.add(Utils.helpFormat("%c $MASTODON_CMD ")) + commands.add(TOOT_CMD) + help.add("To toot on Mastodon:") + help.add(Utils.helpFormat("%c $TOOT_CMD ")) properties[AUTO_POST_PROP] = "false" initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 28f9cde..4fc3770 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -86,6 +86,7 @@ class Twitter : SocialModule() { // Twitter command private const val TWITTER_CMD = "twitter" + private const val TWEET_CMD = "tweet" /** * Post on Twitter. @@ -124,8 +125,9 @@ class Twitter : SocialModule() { init { commands.add(TWITTER_CMD) - help.add("To post to Twitter:") - help.add(helpFormat("%c $TWITTER_CMD ")) + commands.add(TWEET_CMD) + help.add("To tweet on Twitter:") + help.add(helpFormat("%c $TWEET_CMD ")) properties[AUTO_POST_PROP] = "false" initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) } diff --git a/website/index.html b/website/index.html index 721e840..2d134f2 100644 --- a/website/index.html +++ b/website/index.html @@ -48,6 +48,7 @@
  1. PircBotX
  2. Rome
  3. Twitter4J
  4. +
  5. UrlEncoder
  6. mobibot was written by Erik C. Thauvin as a replacement for the channel's @@ -122,7 +123,10 @@

    mobibot: paper
    mobibot: rock
    -
  7. Posting to Twitter and Mastodon
  8. +
  9. Posting to Twitter and Mastodon +
    mobibot: tweet hello twitter
    +
    mobibot: toot hello mastodon
    +
  10. Some of the internal features include RSS feed backlogs, rolling logs, debugging toggle and much more.

    From b20783a18e68d5605795c7053a8cc1b9da7db44b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 4 Jan 2023 02:55:12 -0800 Subject: [PATCH 693/842] Added UrlEncoder library --- .idea/kotlinc.xml | 2 +- build.gradle | 11 ++++++----- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 7 ++----- version.properties | 6 +++--- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 4251b72..2b8a50f 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle b/build.gradle index e3d2267..d05bf6e 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.7.22' - id 'org.jetbrains.kotlin.kapt' version '1.7.22' + id 'org.jetbrains.kotlin.jvm' version '1.8.0' + id 'org.jetbrains.kotlin.kapt' version '1.8.0' id 'org.jetbrains.kotlinx.kover' version '0.6.1' id 'org.sonarqube' version '3.5.0.2730' id 'pmd' @@ -45,7 +45,7 @@ dependencies { compileOnly(semverProcessor) // PircBotX - implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' + implementation 'com.github.pircbotx:pircbotx:master-SNAPSHOT' // implementation fileTree(dir: 'lib', include: '*.jar') // Commons (mostly for PircBotX) @@ -65,7 +65,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' // Logging - implementation 'org.slf4j:slf4j-api:2.0.5' + implementation 'org.slf4j:slf4j-api:2.0.6' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" @@ -82,11 +82,12 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' + implementation 'net.thauvin.erik:urlencoder:1.0.0' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation "org.mockito:mockito-core:4.0.0" - testImplementation 'org.testng:testng:7.6.1' + testImplementation 'org.testng:testng:7.7.1' } test { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 113a0ab..bc9860d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import net.thauvin.erik.urlencoder.UrlEncoder import org.jsoup.Jsoup import org.pircbotx.Colors import org.pircbotx.PircBotX @@ -46,7 +47,6 @@ import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.net.HttpURLConnection import java.net.URL -import java.net.URLEncoder import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths @@ -152,10 +152,7 @@ object Utils { * URL encodes the given string. */ @JvmStatic - fun String.encodeUrl(): String = URLEncoder.encode(this, StandardCharsets.UTF_8) - .replace("+", "%20") - .replace("*", "%2A") - .replace("%7E", "~") + fun String.encodeUrl(): String = UrlEncoder.encode(this) /** * Returns a property as an int. diff --git a/version.properties b/version.properties index 2090357..61222a3 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Dec 13 01:22:17 PST 2022 -version.buildmeta=876 +#Fri Dec 30 11:11:04 PST 2022 +version.buildmeta=936 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+876 +version.semver=0.8.0-rc+936 From d9e50166899696b31a2336dc90c0dd2070d07a86 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 4 Jan 2023 03:00:07 -0800 Subject: [PATCH 694/842] Fixed nix3 IP --- .../kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt | 4 ++-- version.properties | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 734bcb3..5ab97d7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -48,8 +48,8 @@ class LookupTest { var result = nslookup("apple.com") assertThat(result, "lookup(apple.com)").contains("17.253.144.10") - result = nslookup("204.122.17.9") - assertThat(result, "lookup(204.122.17.9)").contains("nix3.thauvin.us") + result = nslookup("204.122.16.136") + assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") } @Test(groups = ["modules"]) diff --git a/version.properties b/version.properties index 61222a3..593b23f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Dec 30 11:11:04 PST 2022 -version.buildmeta=936 +#Wed Jan 04 02:59:39 PST 2023 +version.buildmeta=939 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+936 +version.semver=0.8.0-rc+939 From 8c1327655661967752286238eb1389c367b8c3c0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 5 Jan 2023 17:07:01 -0800 Subject: [PATCH 695/842] Allowed longer response from ChatGPT --- build.gradle | 2 +- .../kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt | 7 +++---- version.properties | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index d05bf6e..178326f 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,7 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'net.thauvin.erik:urlencoder:1.0.0' + implementation 'net.thauvin.erik:urlencoder:1.0.1' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 8e8423a..3a03d03 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -98,11 +98,10 @@ class ChatGpt : AbstractModule() { "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, - "max_tokens": 100, + "max_tokens": 1024, "top_p": 1, "frequency_penalty": 0, - "presence_penalty": 0, - "stop": ["\n"] + "presence_penalty": 0 }""".trimIndent() ) ) @@ -127,7 +126,7 @@ class ChatGpt : AbstractModule() { } catch (e: IOException) { throw ModuleException( "chatgpt($query): IO", - "An IO error has occurred while conversing with GhatGPT.", + "An IO error has occurred while conversing with ChatGPT.", e ) } diff --git a/version.properties b/version.properties index 593b23f..c234d1f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Jan 04 02:59:39 PST 2023 -version.buildmeta=939 +#Thu Jan 05 16:20:43 PST 2023 +version.buildmeta=948 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+939 +version.semver=0.8.0-rc+948 From 6894e05610612b50a3445aba8c87c24af75e9dca Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 00:57:12 -0800 Subject: [PATCH 696/842] Added max-token property --- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 19 ++++++++++++++----- .../erik/mobibot/modules/ChatGptTest.kt | 13 +++++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 3a03d03..e2bccb0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage +import org.apache.commons.text.WordUtils import org.json.JSONException import org.json.JSONObject import org.json.JSONWriter @@ -54,9 +55,9 @@ class ChatGpt : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[CHATGPT_API_KEY]) + val answer = chat(args.trim(), properties[CHATGPT_API_KEY], properties[CHATGPT_MAX_TOKENS]!!.toInt()) if (answer.isNotBlank()) { - event.sendMessage(answer) + event.sendMessage(WordUtils.wrap(answer, 400)) } else { event.respond("ChatGPT is stumped.") } @@ -65,6 +66,9 @@ class ChatGpt : AbstractModule() { e.message?.let { event.respond(it) } + } catch (e: NumberFormatException) { + if (logger.isErrorEnabled) logger.error("Invalid $CHATGPT_MAX_TOKENS property.", e) + event.respond("The $name module is misconfigured.") } } else { helpResponse(event) @@ -77,6 +81,11 @@ class ChatGpt : AbstractModule() { */ const val CHATGPT_API_KEY = "chatgpt-api-key" + /** + * The ChatGPT max tokens property. + */ + const val CHATGPT_MAX_TOKENS = "chatgpt-max-tokens" + // ChatGPT command private const val CHATGPT_CMD = "chatgpt" @@ -85,7 +94,7 @@ class ChatGpt : AbstractModule() { @JvmStatic @Throws(ModuleException::class) - fun chat(query: String, apiKey: String?): String { + fun chat(query: String, apiKey: String?, maxTokens: Int): String { if (!apiKey.isNullOrEmpty()) { val prompt = JSONWriter.valueToString("Q:$query\nA:") val request = HttpRequest.newBuilder() @@ -98,7 +107,7 @@ class ChatGpt : AbstractModule() { "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, - "max_tokens": 1024, + "max_tokens": $maxTokens, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 @@ -145,6 +154,6 @@ class ChatGpt : AbstractModule() { add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) } - initProperties(CHATGPT_API_KEY) + initProperties(CHATGPT_API_KEY, CHATGPT_MAX_TOKENS) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index f230831..b861c55 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -34,15 +34,17 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause +import assertk.assertions.isEqualTo import assertk.assertions.isFailure import assertk.assertions.isInstanceOf +import assertk.assertions.message import net.thauvin.erik.mobibot.LocalProperties import org.testng.annotations.Test class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { - assertThat { ChatGpt.chat("1 gallon to liter", "") } + assertThat { ChatGpt.chat("1 gallon to liter", "", 0) } .isFailure() .isInstanceOf(ModuleException::class.java) .hasNoCause() @@ -52,7 +54,14 @@ class ChatGptTest : LocalProperties() { fun testChat() { val apiKey = getProperty(ChatGpt.CHATGPT_API_KEY) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey) + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") + assertThat( + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ).contains("URLEncoder") + + assertThat { ChatGpt.chat("1 liter to gallon", apiKey, 0) } + .isFailure() + .isInstanceOf(ModuleException::class.java) } } From 66ca972b9b64ea309cd957f33a053369ab82cb61 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 00:57:20 -0800 Subject: [PATCH 697/842] Minor cleanup --- .github/workflows/gradle.yml | 2 +- README.md | 8 +++++--- build.gradle | 8 ++++---- config/detekt/baseline.xml | 9 +++++---- properties/mobibot.properties | 11 ++++++----- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 13 ++++++------- .../net/thauvin/erik/mobibot/commands/seen/Seen.kt | 8 ++++---- .../erik/mobibot/commands/tell/TellManager.kt | 8 ++++---- .../erik/mobibot/commands/tell/TellMessage.kt | 4 ++-- .../net/thauvin/erik/mobibot/entries/EntryLink.kt | 4 ++-- version.properties | 6 +++--- website/index.html | 5 ++--- 12 files changed, 44 insertions(+), 42 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 4a41aff..94687dc 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -68,7 +68,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew sonarqube + run: ./gradlew sonar - name: Cleanup Gradle Cache run: | diff --git a/README.md b/README.md index 2459b1b..4dee7b5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # mobibot -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) - -[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.0-blue)](https://kotlinlang.org/) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) +[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) +[![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: diff --git a/build.gradle b/build.gradle index 178326f..3515d31 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ dependencies { implementation 'commons-net:commons-net:3.9.0' // Google - implementation 'com.google.code.gson:gson:2.10' + implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.google.guava:guava:31.1-jre' // Kotlin @@ -82,7 +82,7 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'net.thauvin.erik:urlencoder:1.0.1' + implementation 'net.thauvin.erik:urlencoder:1.3.0' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' @@ -188,7 +188,7 @@ sonarqube { } } -tasks.sonarqube { +tasks.sonar { dependsOn 'koverReport' } @@ -219,6 +219,6 @@ task deploy { task release { group = 'Publishing' description = 'Releases new version.' - dependsOn(clean, wrapper, check, deploy) + dependsOn(clean, check, deploy) mustRunAfter clean } diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 19b4dbd..57c71a4 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -11,6 +11,7 @@ LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent ) LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean ) + MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 MagicNumber:Comment.kt$Comment$3 MagicNumber:CryptoPrices.kt$CryptoPrices$10 @@ -48,7 +49,7 @@ MaxLineLength:TwitterOAuth.kt$TwitterOAuth$* NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean - NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?): String + NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(query: String): Message NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>) @@ -64,8 +65,8 @@ NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> NestedBlockDepth:Tell.kt$Tell$fun send(event: GenericUserEvent) NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$@JvmStatic fun main(args: Array<String>) - NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadData(file: String, default: Any, logger: Logger, description: String): Any - NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveData(file: String, data: Any, logger: Logger, description: String) + NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any + NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveSerialData(file: String, data: Any, logger: Logger, description: String) NestedBlockDepth:Weather2.kt$Weather2$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$ioe @@ -76,7 +77,7 @@ SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException - ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?): String + ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> ThrowsCount:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 5551173..e82910c 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -28,12 +28,12 @@ tell-max-size=50 #disabled-modules=dice, joke # -# Credentials for: http://pinboard.in/ +# API Token for: https://pinboard.in/settings/password # #pinboard-api-token=user\:TOKEN # -# Configure app at: https://developer.twitter.com/en/apps +# Configure app at: https://developer.twitter.com/ # and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth # #twitter-consumerKey= @@ -61,14 +61,14 @@ tell-max-size=50 #mastodon-auto-post=true # -# Create custom search engine at: https://cse.google.com/ -# and get API key from: https://console.developers.google.com/ +# Create custom search engine at: https://programmablesearchengine.google.com/ +# and get API key from: https://console.cloud.google.com/apis # #google-api= #google-cse-cx= # -# Get OpenWeatherMap API key from: https://openweathermap.org/ +# Get OpenWeatherMap API key from: https://openweathermap.org/api # #owm-api-key= @@ -87,3 +87,4 @@ tell-max-size=50 # ChatGPT/OpenAI API key from: https://beta.openai.com/account/api-keys # #chatgpt-api-key= +#chatgpt-max-tokens=1024 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index bc9860d..359de74 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -47,7 +47,6 @@ import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.net.HttpURLConnection import java.net.URL -import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths import java.time.LocalDateTime @@ -189,7 +188,7 @@ object Utils { } /** - * Returns {@code true} if the specified user is an operator on the [channel]. + * Returns `true` if the specified user is an operator on the [channel]. */ @JvmStatic fun GenericMessageEvent.isChannelOp(channel: String): Boolean { @@ -197,7 +196,7 @@ object Utils { } /** - * Returns {@code true} if a HTTP status code indicates a successful response. + * Returns `true` if a HTTP status code indicates a successful response. */ @JvmStatic fun Int.isHttpSuccess() = this in 200..399 @@ -214,10 +213,10 @@ object Utils { } /** - * Load data. + * Load serial data from file. */ @JvmStatic - fun loadData(file: String, default: Any, logger: Logger, description: String): Any { + fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any { val serialFile = Paths.get(file) if (serialFile.exists() && serialFile.fileSize() > 0) { try { @@ -237,7 +236,7 @@ object Utils { } /** - * Returns {@code true} if the list does not contain the given string. + * Returns `true` if the list does not contain the given string. */ @JvmStatic fun List.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } @@ -290,7 +289,7 @@ object Utils { * Save data */ @JvmStatic - fun saveData(file: String, data: Any, logger: Logger, description: String) { + fun saveSerialData(file: String, data: Any, logger: Logger, description: String) { try { BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> ObjectOutputStream(bos).use { output -> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 66a3259..2b97f5a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -35,8 +35,8 @@ package net.thauvin.erik.mobibot.commands.seen import com.google.common.collect.ImmutableSortedSet import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.loadData -import net.thauvin.erik.mobibot.Utils.saveData +import net.thauvin.erik.mobibot.Utils.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime @@ -107,7 +107,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") seenNicks.putAll( - loadData( + loadSerialData( serialObject, TreeMap(), logger, @@ -118,7 +118,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { } fun save() { - saveData(serialObject, seenNicks, logger, "seen nicknames") + saveSerialData(serialObject, seenNicks, logger, "seen nicknames") } init { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index 3c1f6b5..74ed301 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -31,8 +31,8 @@ */ package net.thauvin.erik.mobibot.commands.tell -import net.thauvin.erik.mobibot.Utils.loadData -import net.thauvin.erik.mobibot.Utils.saveData +import net.thauvin.erik.mobibot.Utils.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData import org.slf4j.Logger import org.slf4j.LoggerFactory import java.time.Clock @@ -60,7 +60,7 @@ object TellManager { @JvmStatic fun load(file: String): List { @Suppress("UNCHECKED_CAST") - return loadData(file, emptyList(), logger, "message queue") as List + return loadSerialData(file, emptyList(), logger, "message queue") as List } /** @@ -69,7 +69,7 @@ object TellManager { @JvmStatic fun save(file: String, messages: List?) { if (messages != null) { - saveData(file, messages, logger, "messages") + saveSerialData(file, messages, logger, "messages") } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 7d8aaed..c9333c9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -66,12 +66,12 @@ class TellMessage( var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) /** - * Returns {@code true} if a notification was sent. + * Returns `true` if a notification was sent. */ var isNotified = false /** - * Returns {@code true} if the message was received. + * Returns `true` if the message was received. */ var isReceived = false set(value) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 59f4e73..ab0fee4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -130,8 +130,8 @@ class EntryLink( /** * Formats the tags. */ - fun formatTags(sep: String, prefix: String = "") : String { - return tags.joinToString(separator = sep, prefix = prefix){it.name} + fun formatTags(sep: String, prefix: String = ""): String { + return tags.joinToString(separator = sep, prefix = prefix) { it.name } } /** diff --git a/version.properties b/version.properties index c234d1f..c049177 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jan 05 16:20:43 PST 2023 -version.buildmeta=948 +#Thu Jan 12 00:57:56 PST 2023 +version.buildmeta=975 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+948 +version.semver=0.8.0-rc+975 diff --git a/website/index.html b/website/index.html index 2d134f2..ea78552 100644 --- a/website/index.html +++ b/website/index.html @@ -1,6 +1,5 @@ - - + + mobibot From 4fcdce522938dff23090504b733a7c5ebe2b4bbe Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 10:27:01 -0800 Subject: [PATCH 698/842] Added to no-ci group to avoid rate limits --- src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index b861c55..98f0052 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -50,7 +50,7 @@ class ChatGptTest : LocalProperties() { .hasNoCause() } - @Test(groups = ["modules"]) + @Test(groups = ["modules", "no-ci"]) fun testChat() { val apiKey = getProperty(ChatGpt.CHATGPT_API_KEY) assertThat( From c3d1930592a0b6289e0ecccdd6b19c777fb55601 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 23:06:07 -0800 Subject: [PATCH 699/842] Added rate limit error --- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index e2bccb0..d62d364 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -50,16 +50,16 @@ import java.net.http.HttpResponse class ChatGpt : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) - override val name = "ChatGPT" + override val name = CHATGPT_NAME override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[CHATGPT_API_KEY], properties[CHATGPT_MAX_TOKENS]!!.toInt()) + val answer = chat(args.trim(), properties[API_KEY_PROP], properties[MAX_TOKENS_PROP]!!.toInt()) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) } else { - event.respond("ChatGPT is stumped.") + event.respond("$name is stumped.") } } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) @@ -67,7 +67,7 @@ class ChatGpt : AbstractModule() { event.respond(it) } } catch (e: NumberFormatException) { - if (logger.isErrorEnabled) logger.error("Invalid $CHATGPT_MAX_TOKENS property.", e) + if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e) event.respond("The $name module is misconfigured.") } } else { @@ -77,20 +77,26 @@ class ChatGpt : AbstractModule() { companion object { /** - * The ChatGPT API Key property. + * The service name. */ - const val CHATGPT_API_KEY = "chatgpt-api-key" + const val CHATGPT_NAME = "ChatGPT" /** - * The ChatGPT max tokens property. + * The API Key property. */ - const val CHATGPT_MAX_TOKENS = "chatgpt-max-tokens" + const val API_KEY_PROP = "chatgpt-api-key" + + /** + * The max tokens property. + */ + const val MAX_TOKENS_PROP = "chatgpt-max-tokens" + + // ChatGPT API URL + private const val API_URL = "https://api.openai.com/v1/completions" // ChatGPT command private const val CHATGPT_CMD = "chatgpt" - // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/completions" @JvmStatic @Throws(ModuleException::class) @@ -124,23 +130,28 @@ class ChatGpt : AbstractModule() { return choices.getJSONObject(0).getString("text").trim() } catch (e: JSONException) { throw ModuleException( - "chatgpt($query): JSON", - "A JSON error has occurred while conversing with ChatGPT.", + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", e ) } } else { - throw IOException("Status Code: " + response.statusCode()) + if (response.statusCode() == 429) { + throw ModuleException("$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later.") + } else { + throw IOException("HTTP Status Code: " + response.statusCode()) + } } } catch (e: IOException) { throw ModuleException( - "chatgpt($query): IO", - "An IO error has occurred while conversing with ChatGPT.", + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", e ) } } else { - throw ModuleException("chatgpt($query)", "No ChatGPT API key specified.") + throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.") } } } @@ -148,12 +159,12 @@ class ChatGpt : AbstractModule() { init { commands.add(CHATGPT_CMD) with(help) { - add("To get answers from ChatGPT:") + add("To get answers from $name:") add(Utils.helpFormat("%c $CHATGPT_CMD ")) add("For example:") add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) } - initProperties(CHATGPT_API_KEY, CHATGPT_MAX_TOKENS) + initProperties(API_KEY_PROP, MAX_TOKENS_PROP) } } From a1ea25ae74ea2981f23b0fc6699464eb21c11b6f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 23:06:21 -0800 Subject: [PATCH 700/842] Minor cleanup --- config/detekt/baseline.xml | 1 + .../thauvin/erik/mobibot/modules/CryptoPrices.kt | 6 +++--- .../thauvin/erik/mobibot/modules/GoogleSearch.kt | 10 +++++----- .../thauvin/erik/mobibot/modules/StockQuote.kt | 16 ++++++++-------- .../net/thauvin/erik/mobibot/modules/Twitter.kt | 6 +++--- .../net/thauvin/erik/mobibot/modules/Weather2.kt | 6 +++--- .../thauvin/erik/mobibot/modules/WolframAlpha.kt | 11 ++++++----- .../thauvin/erik/mobibot/modules/ChatGptTest.kt | 4 +--- .../erik/mobibot/modules/GoogleSearchTest.kt | 4 ++-- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2Test.kt | 10 +++++----- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- version.properties | 6 +++--- 13 files changed, 42 insertions(+), 42 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 57c71a4..da082db 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -13,6 +13,7 @@ LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean ) MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 + MagicNumber:ChatGpt.kt$ChatGpt.Companion$429 MagicNumber:Comment.kt$Comment$3 MagicNumber:CryptoPrices.kt$CryptoPrices$10 MagicNumber:CurrencyConverter.kt$CurrencyConverter$11 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 66a33ae..05647bd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -65,7 +65,7 @@ class CryptoPrices : AbstractModule() { } val debugMessage = "crypto($cmd $args)" - if (args == CURRENCY_CODES_KEYWORD) { + if (args == CODES_KEYWORD) { event.sendMessage("The supported currencies are:") event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true) } else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { @@ -100,7 +100,7 @@ class CryptoPrices : AbstractModule() { private val CURRENCIES: MutableMap = mutableMapOf() // Currency codes keyword - private const val CURRENCY_CODES_KEYWORD = "codes" + private const val CODES_KEYWORD = "codes" /** * Get current market price. @@ -153,7 +153,7 @@ class CryptoPrices : AbstractModule() { add(helpFormat("%c $CRYPTO_CMD ETH EUR")) add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) add("To list the supported currencies:") - add(helpFormat("%c $CRYPTO_CMD $CURRENCY_CODES_KEYWORD")) + add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD")) } loadCurrencies() } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 76451db..02af2b6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -67,8 +67,8 @@ class GoogleSearch : AbstractModule() { try { val results = searchGoogle( args, - properties[GOOGLE_API_KEY_PROP], - properties[GOOGLE_CSE_KEY_PROP], + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], event.user.nick ) for (msg in results) { @@ -91,10 +91,10 @@ class GoogleSearch : AbstractModule() { companion object { // Google API Key property - const val GOOGLE_API_KEY_PROP = "google-api-key" + const val API_KEY_PROP = "google-api-key" // Google Custom Search Engine ID property - const val GOOGLE_CSE_KEY_PROP = "google-cse-cx" + const val CSE_KEY_PROP = "google-cse-cx" // Google command private const val GOOGLE_CMD = "google" @@ -158,6 +158,6 @@ class GoogleSearch : AbstractModule() { commands.add(GOOGLE_CMD) help.add("To search Google:") help.add(helpFormat("%c $GOOGLE_CMD ")) - initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP) + initProperties(API_KEY_PROP, CSE_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index ffca08b..6df2317 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -63,7 +63,7 @@ class StockQuote : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) + val messages = getQuote(args, properties[API_KEY_PROP]) for (msg in messages) { event.sendMessage(channel, msg) } @@ -80,17 +80,17 @@ class StockQuote : AbstractModule() { companion object { /** - * The Alpha Advantage property key. + * The API property key. */ - const val ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key" + const val API_KEY_PROP = "alphavantage-api-key" /** * The Invalid Symbol error string. */ const val INVALID_SYMBOL = "Invalid symbol." - // Alpha Advantage URL - private const val ALPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=" + // API URL + private const val API_URL = "https://www.alphavantage.co/query?function=" // Quote command private const val STOCK_CMD = "stock" @@ -145,7 +145,7 @@ class StockQuote : AbstractModule() { with(messages) { // Search for symbol/keywords response = URL( - "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + apiKey.encodeUrl() ).reader().body var json = getJsonResponse(response, debugMessage) @@ -157,7 +157,7 @@ class StockQuote : AbstractModule() { // Get quote for symbol response = URL( - "${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" + "${API_URL}GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + apiKey.encodeUrl() ).reader().body @@ -232,6 +232,6 @@ class StockQuote : AbstractModule() { commands.add(STOCK_CMD) help.add("To retrieve a stock quote:") help.add(helpFormat("%c $STOCK_CMD ")) - initProperties(ALPHAVANTAGE_API_KEY_PROP) + initProperties(API_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 4fc3770..ddb82b2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -84,7 +84,7 @@ class Twitter : SocialModule() { const val TOKEN_PROP = "twitter-token" const val TOKEN_SECRET_PROP = "twitter-tokenSecret" - // Twitter command + // Twitter commands private const val TWITTER_CMD = "twitter" private const val TWEET_CMD = "tweet" @@ -118,7 +118,7 @@ class Twitter : SocialModule() { dm.text } } catch (e: TwitterException) { - throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e) + throw ModuleException("tweet($message)", "An error has occurred: ${e.message}", e) } } } @@ -126,7 +126,7 @@ class Twitter : SocialModule() { init { commands.add(TWITTER_CMD) commands.add(TWEET_CMD) - help.add("To tweet on Twitter:") + help.add("To $TWEET_CMD on $name:") help.add(helpFormat("%c $TWEET_CMD ")) properties[AUTO_POST_PROP] = "false" initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 5c5ae67..ec963fe 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -65,7 +65,7 @@ class Weather2 : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val messages = getWeather(args, properties[OWM_API_KEY_PROP]) + val messages = getWeather(args, properties[API_KEY_PROP]) if (messages[0].isError) { helpResponse(event) } else { @@ -88,7 +88,7 @@ class Weather2 : AbstractModule() { /** * The OpenWeatherMap API Key property. */ - const val OWM_API_KEY_PROP = "owm-api-key" + const val API_KEY_PROP = "owm-api-key" // Weather command private const val WEATHER_CMD = "weather" @@ -246,6 +246,6 @@ class Weather2 : AbstractModule() { add(helpFormat("%c $WEATHER_CMD paris, fr")) add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") } - initProperties(OWM_API_KEY_PROP) + initProperties(API_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 56d3a3d..204ad17 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -66,9 +66,9 @@ class WolframAlpha : AbstractModule() { units = if (query.size == 2) { getUnits(query[1].trim()) } else { - getUnits(properties[WOLFRAM_UNITS_PROP]) + getUnits(properties[UNITS_PROP]) }, - appId = properties[WOLFRAM_APPID_KEY] + appId = properties[APPID_KEY_PROP] ) ) } catch (e: ModuleException) { @@ -86,12 +86,13 @@ class WolframAlpha : AbstractModule() { /** * The Wolfram Alpha API Key property. */ - const val WOLFRAM_APPID_KEY = "wolfram-appid" + const val APPID_KEY_PROP = "wolfram-appid" /** * The Wolfram units properties */ - const val WOLFRAM_UNITS_PROP = "wolfram-units" + const val UNITS_PROP = "wolfram-units" + const val METRIC = "metric" const val IMPERIAL = "imperial" @@ -137,6 +138,6 @@ class WolframAlpha : AbstractModule() { add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas")) add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric")) } - initProperties(WOLFRAM_APPID_KEY, WOLFRAM_UNITS_PROP) + initProperties(APPID_KEY_PROP, UNITS_PROP) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 98f0052..6c30297 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -34,10 +34,8 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause -import assertk.assertions.isEqualTo import assertk.assertions.isFailure import assertk.assertions.isInstanceOf -import assertk.assertions.message import net.thauvin.erik.mobibot.LocalProperties import org.testng.annotations.Test @@ -52,7 +50,7 @@ class ChatGptTest : LocalProperties() { @Test(groups = ["modules", "no-ci"]) fun testChat() { - val apiKey = getProperty(ChatGpt.CHATGPT_API_KEY) + val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 8d12ab4..9d052cf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -75,8 +75,8 @@ class GoogleSearchTest : LocalProperties() { @Test(groups = ["no-ci", "modules"]) @Throws(ModuleException::class) fun testSearchGoogle() { - val apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP) - val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) + val apiKey = getProperty(GoogleSearch.API_KEY_PROP) + val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP) try { var query = "mobibot" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 162550f..2721469 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -59,7 +59,7 @@ class StockQuoteTest : LocalProperties() { @Test(groups = ["modules"]) @Throws(ModuleException::class) fun testGetQuote() { - val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP) + val apiKey = getProperty(StockQuote.API_KEY_PROP) try { var symbol = "apple inc" val messages = getQuote(symbol, apiKey) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 6c67536..f1949d6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -46,7 +46,7 @@ import assertk.assertions.prop import net.aksingh.owmjapis.api.APIException import net.aksingh.owmjapis.core.OWM import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Weather2.Companion.OWM_API_KEY_PROP +import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather @@ -86,7 +86,7 @@ class Weather2Test : LocalProperties() { @Throws(ModuleException::class) fun testWeather() { var query = "98204" - var messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) + var messages = getWeather(query, getProperty(API_KEY_PROP)) assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { contains("Everett, United States") contains("US") @@ -94,7 +94,7 @@ class Weather2Test : LocalProperties() { assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS") query = "San Francisco" - messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) + messages = getWeather(query, getProperty(API_KEY_PROP)) assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { contains("San Francisco") contains("US") @@ -102,7 +102,7 @@ class Weather2Test : LocalProperties() { assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959") query = "London, GB" - messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) + messages = getWeather(query, getProperty(API_KEY_PROP)) assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { contains("London, United Kingdom") contains("GB") @@ -111,7 +111,7 @@ class Weather2Test : LocalProperties() { try { query = "Foo, US" - getWeather(query, getProperty(OWM_API_KEY_PROP)) + getWeather(query, getProperty(API_KEY_PROP)) } catch (e: ModuleException) { assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index f0fb424..0d9ed9a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -58,7 +58,7 @@ class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules", "no-ci"]) @Throws(ModuleException::class) fun queryWolframTest() { - val apiKey = getProperty(WolframAlpha.WOLFRAM_APPID_KEY) + val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) try { var query = "SFO to SEA" assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles") diff --git a/version.properties b/version.properties index c049177..8d6a471 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jan 12 00:57:56 PST 2023 -version.buildmeta=975 +#Thu Jan 12 23:03:48 PST 2023 +version.buildmeta=982 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+975 +version.semver=0.8.0-rc+982 From 6ec39c6d6df7367760f64de1c3f79286268ef3e6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 28 Jan 2023 23:19:12 -0800 Subject: [PATCH 701/842] Updated copyright --- .idea/copyright/Erik_s_Copyright_Notice.xml | 2 +- LICENSE.txt | 3 +- build.gradle | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 3 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 3 +- .../net/thauvin/erik/mobibot/Constants.kt | 3 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 3 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 3 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 3 +- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 3 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 3 +- .../erik/mobibot/commands/AbstractCommand.kt | 3 +- .../erik/mobibot/commands/ChannelFeed.kt | 3 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 3 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 3 +- .../thauvin/erik/mobibot/commands/Modules.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 3 +- .../thauvin/erik/mobibot/commands/Recap.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 3 +- .../thauvin/erik/mobibot/commands/Users.kt | 3 +- .../thauvin/erik/mobibot/commands/Versions.kt | 3 +- .../erik/mobibot/commands/links/Comment.kt | 3 +- .../mobibot/commands/links/LinksManager.kt | 3 +- .../erik/mobibot/commands/links/Posting.kt | 3 +- .../erik/mobibot/commands/links/Tags.kt | 3 +- .../erik/mobibot/commands/links/View.kt | 3 +- .../mobibot/commands/seen/NickComparator.kt | 5 ++- .../erik/mobibot/commands/seen/Seen.kt | 3 +- .../erik/mobibot/commands/seen/SeenNick.kt | 3 +- .../erik/mobibot/commands/tell/Tell.kt | 3 +- .../erik/mobibot/commands/tell/TellManager.kt | 3 +- .../erik/mobibot/commands/tell/TellMessage.kt | 3 +- .../thauvin/erik/mobibot/entries/Entries.kt | 3 +- .../erik/mobibot/entries/EntriesUtils.kt | 3 +- .../erik/mobibot/entries/EntryComment.kt | 3 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 3 +- .../erik/mobibot/entries/FeedsManager.kt | 3 +- .../erik/mobibot/modules/AbstractModule.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 3 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 3 +- .../erik/mobibot/modules/CryptoPrices.kt | 3 +- .../erik/mobibot/modules/CurrencyConverter.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 3 +- .../erik/mobibot/modules/GoogleSearch.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 3 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 3 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 3 +- .../erik/mobibot/modules/ModuleException.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 3 +- .../erik/mobibot/modules/RockPaperScissors.kt | 3 +- .../erik/mobibot/modules/StockQuote.kt | 3 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 3 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 3 +- .../erik/mobibot/modules/WolframAlpha.kt | 3 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 3 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 3 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 3 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 3 +- .../erik/mobibot/msg/PrivateMessage.kt | 3 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 3 +- .../erik/mobibot/social/SocialManager.kt | 3 +- .../erik/mobibot/social/SocialModule.kt | 3 +- .../erik/mobibot/social/SocialTimer.kt | 3 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 3 +- .../erik/mobibot/ExceptionSanitizer.kt | 3 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 3 +- .../thauvin/erik/mobibot/LocalProperties.kt | 3 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 3 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 3 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 3 +- .../erik/mobibot/commands/RecapTest.kt | 3 +- .../commands/links/LinksManagerTest.kt | 5 ++- .../erik/mobibot/commands/links/ViewTest.kt | 3 +- .../erik/mobibot/commands/seen/SeenTest.kt | 3 +- .../mobibot/commands/tell/TellMessageTest.kt | 3 +- .../commands/tell/TellMessagesMgrTest.kt | 3 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 3 +- .../erik/mobibot/entries/EntryLinkTest.kt | 3 +- .../erik/mobibot/entries/FeedMgrTest.kt | 3 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 3 +- .../erik/mobibot/modules/ChatGptTest.kt | 3 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 3 +- .../mobibot/modules/CurrencyConverterTest.kt | 3 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 3 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 3 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 3 +- .../erik/mobibot/modules/LookupTest.kt | 3 +- .../erik/mobibot/modules/MastodonTest.kt | 3 +- .../mobibot/modules/ModuleExceptionTest.kt | 3 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 3 +- .../mobibot/modules/RockPaperScissorsTest.kt | 3 +- .../erik/mobibot/modules/StockQuoteTest.kt | 3 +- .../erik/mobibot/modules/TwitterTest.kt | 3 +- .../erik/mobibot/modules/Weather2Test.kt | 3 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 3 +- .../erik/mobibot/modules/WordTimeTest.kt | 3 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 5 ++- src/test/resources/current.xml | 31 +++++++++++++++++++ version.properties | 6 ++-- 103 files changed, 138 insertions(+), 206 deletions(-) diff --git a/.idea/copyright/Erik_s_Copyright_Notice.xml b/.idea/copyright/Erik_s_Copyright_Notice.xml index b9f5293..055999a 100644 --- a/.idea/copyright/Erik_s_Copyright_Notice.xml +++ b/.idea/copyright/Erik_s_Copyright_Notice.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index 085f7c7..f5fe66b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,4 @@ -Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) -All rights reserved. +Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/build.gradle b/build.gradle index 3515d31..70e3d46 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j: '2.19.0', - pmd : '6.52.0', + pmd : '6.54.0', ] repositories { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 9193072..d1d7882 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,8 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 8720e95..020edf4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,8 +1,7 @@ /* * Addons.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 543511f..6766f62 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,8 +1,7 @@ /* * Constants.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index fc43dbc..f56af2e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,8 +1,7 @@ /* * FeedReader.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index fcd2d08..f811764 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,8 +1,7 @@ /* * Mobibot.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 1bc719b..b829bab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,8 +1,7 @@ /* * Pinboard.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt index 2b5ecfb..ab078db 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt @@ -1,8 +1,7 @@ /* * TwitterOAuth.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 359de74..165647a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,8 +1,7 @@ /* * Utils.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index c404b89..5f79472 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,8 +1,7 @@ /* * AbstractCommand.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 8412af0..038e378 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,8 +1,7 @@ /* * ChannelFeed.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 31a9c65..9608ca8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,8 +1,7 @@ /* * Cycle.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index 97ca12e..f271bfa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,8 +1,7 @@ /* * Die.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index f47f057..a696fa8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,8 +1,7 @@ /* * Ignore.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 9f7e855..ed0b6ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,8 +1,7 @@ /* * Info.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index bbd5479..ec7823b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,8 +1,7 @@ /* * Me.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 456fa7f..f64178d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,8 +1,7 @@ /* * Modules.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index a758996..20a6635 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,8 +1,7 @@ /* * Msg.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index e4cdf34..85a03ab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,8 +1,7 @@ /* * Nick.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 3647bcc..77154c7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,8 +1,7 @@ /* * Recap.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 0141206..7f76d35 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,8 +1,7 @@ /* * Say.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 66c4ebf..33d6fef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,8 +1,7 @@ /* * Users.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 9513aef..896c569 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,8 +1,7 @@ /* * Versions.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 5a09836..1443d44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,8 +1,7 @@ /* * Comment.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index af6adf4..fba6b99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,8 +1,7 @@ /* * LinksManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 9368d37..ff4278d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,8 +1,7 @@ /* * Posting.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 785e3a0..1662857 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,8 +1,7 @@ /* * Tags.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 008ccad..825e374 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,8 +1,7 @@ /* * View.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index 8d068c2..d29b30d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,8 +1,7 @@ /* - * SeenComparator.kt + * NickComparator.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 2b97f5a..4a45598 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,8 +1,7 @@ /* * Seen.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index b53414d..b09cbf4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,8 +1,7 @@ /* * SeenNick.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index f591283..e073184 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,8 +1,7 @@ /* * Tell.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index 74ed301..b65a4da 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,8 +1,7 @@ /* * TellManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index c9333c9..6d2f313 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,8 +1,7 @@ /* * TellMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index a81a737..e8676ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,8 +1,7 @@ /* * Entries.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index ac2c259..9c09626 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,8 +1,7 @@ /* * EntriesUtils.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index b0b138a..bc64191 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,8 +1,7 @@ /* * EntryComment.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index ab0fee4..7bf003d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,8 +1,7 @@ /* * EntryLink.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index 1ae2690..bb3838a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,8 +1,7 @@ /* * FeedsManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 61e2eaf..8c8e736 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,8 +1,7 @@ /* * AbstractModule.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 76f3786..b7aae28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,8 +1,7 @@ /* * Calc.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index d62d364..8847bef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,8 +1,7 @@ /* * ChatGpt.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 05647bd..d14056e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,8 +1,7 @@ /* * CryptoPrices.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 3d0bf0a..d41e7a1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,8 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index c32f781..8420fb1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,8 +1,7 @@ /* * Dice.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 02af2b6..f426d1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,8 +1,7 @@ /* * GoogleSearch.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 202dc68..a0e8fd4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,8 +1,7 @@ /* * Joke.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 90a0316..9ab2ead 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,8 +1,7 @@ /* * Lookup.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 9a4e914..3be3a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,8 +1,7 @@ /* * Mastodon.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 2f39854..a569d21 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,8 +1,7 @@ /* * ModuleException.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 0818120..944dbc1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,8 +1,7 @@ /* * Ping.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index bc02a4d..d698888 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,8 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 6df2317..dcae5e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,8 +1,7 @@ /* * StockQuote.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index ddb82b2..d4c02e1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -1,8 +1,7 @@ /* * Twitter.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index ec963fe..567728e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,8 +1,7 @@ /* * Weather2.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 204ad17..a72efab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,8 +1,7 @@ /* * WolframAlpha.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index fc0edf6..18072bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,8 +1,7 @@ /* * WorldTime.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index ae5651c..0607936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,8 +1,7 @@ /* * ErrorMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 20f8725..23a33b9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,8 +1,7 @@ /* * Message.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index f88b8dd..037d504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,8 +1,7 @@ /* * NoticeMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 827b682..842fee5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,8 +1,7 @@ /* * PrivateMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 71b2a5b..9c5e088 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,8 +1,7 @@ /* * PublicMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index f3a86fe..cbc1936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,8 +1,7 @@ /* * SocialManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index e31ccdc..b594670 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,8 +1,7 @@ /* * SocialModule.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 3edb06b..267a59d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,8 +1,7 @@ /* * SocialTimer.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 826e784..8662392 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,8 +1,7 @@ /* * AddonsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index b1c6dba..a3994ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,8 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 2fa58b6..0d66a0d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,8 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 7dba11a..e4af75a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,8 +1,7 @@ /* * LocalProperties.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 3ee0f26..87617e8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,8 +1,7 @@ /* * PinboardTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index d6fe27d..22e37cb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,8 +1,7 @@ /* * UtilsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index ba5af62..265009b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,8 +1,7 @@ /* * InfoTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 6894b3d..f1fbe11 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,8 +1,7 @@ /* * RecapTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index fa4a099..8e49b5e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,8 +1,7 @@ /* - * LinksMgrTest.kt + * LinksManagerTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index e7c07f0..c28090d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,8 +1,7 @@ /* * ViewTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 46090e5..4298a16 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,8 +1,7 @@ /* * SeenTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 2e4ef6e..f7239e0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,8 +1,7 @@ /* * TellMessageTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index a07eb68..cff11f2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,8 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 23ba01e..6eef16e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,8 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 8c1a862..ab8c71c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,8 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index 68be3f7..cd2ebb8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,8 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 0873e7b..fb0402e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,8 +1,7 @@ /* * CalcTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 6c30297..5e0e1d2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,8 +1,7 @@ /* * ChatGptTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 3bef92b..e3475f1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,8 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 66ae5c7..8c1d745 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,8 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index e391f2a..cdc04f0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,8 +1,7 @@ /* * DiceTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 9d052cf..c2bb833 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,8 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 618cd36..fa063f4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,8 +1,7 @@ /* * JokeTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 5ab97d7..9c21f7c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,8 +1,7 @@ /* * LookupTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 74b5a9a..d464f03 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,8 +1,7 @@ /* * MastodonTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index eef610a..c7dbfc0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,8 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index 50f7fda..e1e79f3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,8 +1,7 @@ /* * PingTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 4bd8025..8dc90ba 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,8 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 2721469..d35a3d3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,8 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 126fb4c..9651ba0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -1,8 +1,7 @@ /* * TwitterTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index f1949d6..4c7f7e6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,8 +1,7 @@ /* * Weather2Test.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 0d9ed9a..4aaf620 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,8 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 049d315..f17ed1d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,8 +1,7 @@ /* * WordTimeTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 7219a1b..e856112 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,8 +1,7 @@ /* - * TestMessage.kt + * MessageTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/resources/current.xml b/src/test/resources/current.xml index 8552a9a..535a400 100644 --- a/src/test/resources/current.xml +++ b/src/test/resources/current.xml @@ -1,4 +1,35 @@ + + #mobibot IRC Links diff --git a/version.properties b/version.properties index 8d6a471..fb36533 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jan 12 23:03:48 PST 2023 -version.buildmeta=982 +#Sat Jan 28 23:17:39 PST 2023 +version.buildmeta=983 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+982 +version.semver=0.8.0-rc+983 From 6f3b84a7c0c4634b2b2a5f2abfb54268e0ff6c62 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 31 Jan 2023 22:03:33 -0800 Subject: [PATCH 702/842] Fixed potential resource leak --- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 165647a..f61c56c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -420,12 +420,12 @@ object Utils { val connection = this.openConnection() as HttpURLConnection connection.setRequestProperty( "User-Agent", - "Mozilla/5.0 (Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0" + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" ) return if (connection.responseCode.isHttpSuccess()) { - UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().readText()) + UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) } else { - UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().readText()) + UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) } } From a43d4e0b5dbd75c1e998810dbe6f46e8d27c112d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 31 Jan 2023 23:24:34 -0800 Subject: [PATCH 703/842] Updated workflow --- .github/workflows/gradle.yml | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 94687dc..fa46754 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -15,12 +15,12 @@ jobs: java-version: [ 11, 18 ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: java-version: ${{ matrix.java-version }} @@ -29,23 +29,14 @@ jobs: - name: Cache SonarCloud packages if: matrix.java-version == env.SONAR_JDK - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle-${{ matrix.java-version }}- - - name: Test with Gradle + uses: gradle/gradle-build-action@v2 env: CI_NAME: "GitHub CI" ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -60,8 +51,8 @@ jobs: MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} - - run: ./gradlew build check --stacktrace + with: + arguments: build check --stacktrace - name: SonarCloud if: success() && matrix.java-version == env.SONAR_JDK @@ -69,8 +60,3 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: ./gradlew sonar - - - name: Cleanup Gradle Cache - run: | - rm -f ~/.gradle/caches/modules-2/modules-2.lock - rm -f ~/.gradle/caches/modules-2/gc.properties From 1b55736ee189ddd3f1663f38be4af6651372d6b2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 31 Jan 2023 23:31:12 -0800 Subject: [PATCH 704/842] Added Java distribution --- .github/workflows/gradle.yml | 1 + build.gradle | 2 +- version.properties | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index fa46754..2e4df52 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -22,6 +22,7 @@ jobs: - name: Set up JDK ${{ matrix.java-version }} uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: ${{ matrix.java-version }} - name: Grant execute permission for gradlew diff --git a/build.gradle b/build.gradle index 70e3d46..8b7827f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.44.0' + id 'com.github.ben-manes.versions' version '0.45.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' diff --git a/version.properties b/version.properties index fb36533..693db4f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jan 28 23:17:39 PST 2023 -version.buildmeta=983 +#Mon Jan 30 22:08:48 PST 2023 +version.buildmeta=986 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+983 +version.semver=0.8.0-rc+986 From f06ba4a6f55d769d1a5dd872ab394f86f047b6d9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 1 Feb 2023 02:37:04 -0800 Subject: [PATCH 705/842] Added disabled modules and commands listing --- src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt | 10 ++++++++++ src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 6 +++++- .../net/thauvin/erik/mobibot/commands/Modules.kt | 8 ++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 020edf4..1127f02 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -74,7 +74,10 @@ class Addons(private val props: Properties) { if (logger.isDebugEnabled) { logger.debug("Module $name is disabled.") } + names.disabledModules.add(name) } + } else { + names.disabledModules.add(name) } } return enabled @@ -106,7 +109,10 @@ class Addons(private val props: Properties) { if (logger.isDebugEnabled) { logger.debug("Command $name is disabled.") } + names.disabledCommands.add(name) } + } else { + names.disabledCommands.add(name) } } return enabled @@ -168,12 +174,16 @@ class Addons(private val props: Properties) { */ object Names { val modules: MutableList = mutableListOf() + val disabledModules: MutableList = mutableListOf() val commands: MutableList = mutableListOf() + val disabledCommands: MutableList = mutableListOf() val ops: MutableList = mutableListOf() fun sort() { modules.sort() + disabledModules.sort() commands.sort() + disabledCommands.sort() ops.sort() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index f811764..2dff959 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -148,6 +148,10 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event.sendMessage("The commands are:") event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) if (event.isChannelOp(channel)) { + if (addons.names.disabledCommands.isNotEmpty()) { + event.sendMessage("The disabled commands are:") + event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true) + } event.sendMessage("The op commands are:") event.sendList(addons.names.ops, 8, isBold = true, isIndent = true) } @@ -412,7 +416,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(Ignore()) addons.add(LinksManager()) addons.add(Me()) - addons.add(Modules(addons.names.modules)) + addons.add(Modules(addons.names.modules, addons.names.disabledModules)) addons.add(Msg()) addons.add(Nick()) addons.add(Posting()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index f64178d..b2293b0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -36,9 +36,9 @@ import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendList import org.pircbotx.hooks.types.GenericMessageEvent -class Modules(private val modules: List) : AbstractCommand() { +class Modules(private val modules: List, private val disabledModules: List) : AbstractCommand() { override val name = "modules" - override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name")) + override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name")) override val isOpOnly = true override val isPublic = false override val isVisible = true @@ -51,6 +51,10 @@ class Modules(private val modules: List) : AbstractCommand() { event.respondPrivateMessage("The enabled modules are: ") event.sendList(modules, 7, isIndent = true) } + if (disabledModules.isNotEmpty()) { + event.respondPrivateMessage("The disabled modules are: ") + event.sendList(disabledModules, 7, isIndent = true) + } } else { helpResponse(channel, args, event) } From d9d5dd2e47f9d198a3cc55961247ba4e45958222 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 1 Feb 2023 02:37:39 -0800 Subject: [PATCH 706/842] Updated to latest workflow actions --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 2e4df52..6ad0035 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -60,4 +60,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew sonar + run: ./gradlew sonar --info From 1d85a1c51683073aa1453be757716751064a0a61 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 3 Feb 2023 00:58:16 -0800 Subject: [PATCH 707/842] Disabled Twitter module by default --- .idea/kotlinc.xml | 2 +- README.md | 7 ++----- build.gradle | 9 +++++---- properties/mobibot.properties | 2 +- .../net/thauvin/erik/mobibot/modules/TwitterTest.kt | 2 +- website/index.html | 3 +-- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 2b8a50f..0fc3113 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/README.md b/README.md index 4dee7b5..53bcfe4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.0-blue)](https://kotlinlang.org/) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-blue)](https://kotlinlang.org/) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) @@ -24,10 +24,7 @@ Some very basic instructions: # help java -jar mobibot.jar -h - - # twitter oauth token request - java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth - + # launch /usr/bin/nohup java -jar mobibot.jar & ``` diff --git a/build.gradle b/build.gradle index 8b7827f..f09cdb1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.0' - id 'org.jetbrains.kotlin.kapt' version '1.8.0' + id 'org.jetbrains.kotlin.jvm' version '1.8.10' + id 'org.jetbrains.kotlin.kapt' version '1.8.10' id 'org.jetbrains.kotlinx.kover' version '0.6.1' id 'org.sonarqube' version '3.5.0.2730' id 'pmd' @@ -92,10 +92,11 @@ dependencies { test { useTestNG() { + excludeGroups.add('twitter') if (isCI) { - excludeGroups('no-ci') - println "Excluded test groups: ${excludeGroups}" + excludeGroups.add('no-ci') } + println "Excluded test groups: ${excludeGroups}" } } diff --git a/properties/mobibot.properties b/properties/mobibot.properties index e82910c..0f97f3c 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -25,7 +25,7 @@ tell-max-days=5 tell-max-size=50 #disabled-commands=die, ignore -#disabled-modules=dice, joke +disabled-modules=twitter # # API Token for: https://pinboard.in/settings/password diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 9651ba0..6e4ab27 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -41,7 +41,7 @@ import org.testng.annotations.Test * The `TwitterTest` class. */ class TwitterTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test(groups = ["modules", "twitter"]) @Throws(ModuleException::class) fun testTweet() { val msg = "Testing Twitter API from ${getHostName()}" diff --git a/website/index.html b/website/index.html index ea78552..be6c33e 100644 --- a/website/index.html +++ b/website/index.html @@ -122,8 +122,7 @@
    mobibot: paper
    mobibot: rock
    -
  11. Posting to Twitter and Mastodon -
    mobibot: tweet hello twitter
    +
  12. Automatic and manual posting to Mastodon
    mobibot: toot hello mastodon
  13. From d67cf4ace765ca2cb842f0edc6afd9d7acd947ca Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 15 Feb 2023 22:15:50 -0800 Subject: [PATCH 708/842] Minor cleanup --- README.md | 3 +-- src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 5 +++-- website/index.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 53bcfe4..8105ed3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-blue)](https://kotlinlang.org/) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-7f52ff.svg)](https://kotlinlang.org)[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 22e37cb..ef0eaaf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -266,8 +266,9 @@ class UtilsTest { @Test @Throws(IOException::class) fun testUrlReader() { - assertThat(URL("https://postman-echo.com/status/200").reader().body, "urlReader()") - .isEqualTo("{\"status\":200}") + val reader = URL("https://postman-echo.com/status/200").reader() + assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}") + assertThat(reader.responseCode).isEqualTo(200) } @Test diff --git a/website/index.html b/website/index.html index be6c33e..97e337d 100644 --- a/website/index.html +++ b/website/index.html @@ -69,7 +69,7 @@
    mobibot: view
  14. Performing calculations -
    mobibot: calc (floor(sqrt(3)) + 3.14) * 3^2
    +
    mobibot: calc (floor(sqrt(3)) + π) * 3^2
  15. Crypto currencies prices
    mobibot: cryto btc
    From db8469169144beb7a5b7f0758f62956a08ed6db9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 15 Feb 2023 22:27:08 -0800 Subject: [PATCH 709/842] Upgraded to Gradle 8.0 --- build.gradle | 17 ++++++++++++++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index f09cdb1..f7da86e 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" - implementation 'com.rometools:rome:1.18.0' + implementation 'com.rometools:rome:1.19.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' @@ -92,11 +92,13 @@ dependencies { test { useTestNG() { - excludeGroups.add('twitter') + // excludeGroups.add('twitter') if (isCI) { excludeGroups.add('no-ci') } - println "Excluded test groups: ${excludeGroups}" + if (!excludeGroups.isEmpty()) { + println "Excluded test groups: ${excludeGroups}" + } } } @@ -153,6 +155,15 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } +tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + +tasks.withType(io.gitlab.arturbosch.detekt.DetektCreateBaselineTask).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + + jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f398c33..42defcc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From c2c5f8beaeb5ce48483209d29dcef9eee3f7da0a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 17 Feb 2023 22:09:57 -0800 Subject: [PATCH 710/842] Upgraded sonar plugin --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index f7da86e..55c1a38 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version '1.8.10' id 'org.jetbrains.kotlin.kapt' version '1.8.10' id 'org.jetbrains.kotlinx.kover' version '0.6.1' - id 'org.sonarqube' version '3.5.0.2730' + id 'org.sonarqube' version '4.0.0.2929' id 'pmd' } @@ -75,7 +75,7 @@ dependencies { implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' implementation 'org.json:json:20220924' - implementation 'org.jsoup:jsoup:1.15.3' + implementation 'org.jsoup:jsoup:1.15.4' implementation 'org.twitter4j:twitter4j-core:4.1.2' // Thauvin From 207c1b7e022b5a7ebc4458d4e9c9aa12912a6049 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 00:01:01 -0700 Subject: [PATCH 711/842] Removed twitter module --- .github/workflows/gradle.yml | 5 - .idea/codeStyles/Project.xml | 275 ++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 1 + .idea/kotlinc.xml | 2 +- build.gradle | 58 ++-- config/detekt/baseline.xml | 7 +- gradle/wrapper/gradle-wrapper.jar | Bin 61574 -> 62076 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 11 +- properties/mobibot.properties | 17 +- .../net/thauvin/erik/mobibot/Constants.kt | 6 + .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 3 +- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 118 -------- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 8 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 133 --------- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 - .../thauvin/erik/mobibot/FeedReaderTest.kt | 17 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 3 +- .../erik/mobibot/modules/ChatGptTest.kt | 7 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 8 +- .../erik/mobibot/modules/MastodonTest.kt | 5 +- .../erik/mobibot/modules/StockQuoteTest.kt | 3 +- .../erik/mobibot/modules/TwitterTest.kt | 60 ---- .../erik/mobibot/modules/Weather2Test.kt | 5 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 8 +- .../erik/mobibot/modules/WordTimeTest.kt | 3 +- version.properties | 6 +- website/index.html | 1 - 29 files changed, 360 insertions(+), 416 deletions(-) delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt delete mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 6ad0035..0034945 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -44,11 +44,6 @@ jobs: CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} OWM_API_KEY: ${{ secrets.OWM_API_KEY }} PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} - TWITTER_CONSUMERKEY: ${{ secrets.TWITTER_CONSUMERKEY }} - TWITTER_CONSUMERSECRET: ${{ secrets.TWITTER_CONSUMERSECRET }} - TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} - TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} - TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 1bec35e..76b5425 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,6 +3,281 @@ + + diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index a55e7a1..6e6eec1 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 0fc3113..217e5c5 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle b/build.gradle index 55c1a38..6a131ab 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,16 @@ +import io.gitlab.arturbosch.detekt.Detekt +import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask + plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.45.0' + id 'com.github.ben-manes.versions' version '0.46.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.10' - id 'org.jetbrains.kotlin.kapt' version '1.8.10' - id 'org.jetbrains.kotlinx.kover' version '0.6.1' + id 'org.jetbrains.kotlin.jvm' version '1.8.21' + id 'org.jetbrains.kotlin.kapt' version '1.8.21' + id 'org.jetbrains.kotlinx.kover' version '0.7.0' id 'org.sonarqube' version '4.0.0.2929' id 'pmd' } @@ -29,8 +32,8 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.19.0', - pmd : '6.54.0', + log4j: '2.20.0', + pmd : '6.55.0', ] repositories { @@ -61,22 +64,21 @@ dependencies { // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' // Logging - implementation 'org.slf4j:slf4j-api:2.0.6' + implementation 'org.slf4j:slf4j-api:2.0.7' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" - implementation 'com.rometools:rome:1.19.0' - implementation 'com.squareup.okhttp3:okhttp:4.10.0' + implementation 'com.rometools:rome:2.1.0' + implementation 'com.squareup.okhttp3:okhttp:4.11.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20220924' - implementation 'org.jsoup:jsoup:1.15.4' - implementation 'org.twitter4j:twitter4j-core:4.1.2' + implementation 'org.json:json:20230227' + implementation 'org.jsoup:jsoup:1.16.1' // Thauvin implementation 'net.thauvin.erik:cryptoprice:1.0.0' @@ -84,10 +86,10 @@ dependencies { implementation 'net.thauvin.erik:pinboard-poster:1.0.3' implementation 'net.thauvin.erik:urlencoder:1.3.0' - testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' + testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.26.1' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation "org.mockito:mockito-core:4.0.0" - testImplementation 'org.testng:testng:7.7.1' + testImplementation 'org.testng:testng:7.8.0' } test { @@ -114,6 +116,12 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + kapt { includeCompileClasspath = false arguments { @@ -121,15 +129,10 @@ kapt { } } -tasks.withType(JavaCompile) { +tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { - kotlinOptions { - jvmTarget = java.targetCompatibility.toString() - } -} compileJava { dependsOn 'incrementBuildMeta' @@ -155,15 +158,14 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } -tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { +tasks.withType(Detekt).configureEach { jvmTarget = java.targetCompatibility.toString() } -tasks.withType(io.gitlab.arturbosch.detekt.DetektCreateBaselineTask).configureEach { +tasks.withType(DetektCreateBaselineTask).configureEach { jvmTarget = java.targetCompatibility.toString() } - jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) @@ -204,19 +206,19 @@ tasks.sonar { dependsOn 'koverReport' } -task copyToDeploy(type: Copy) { +tasks.register('copyToDeploy', Copy) { from('properties', jar) into deployDir } -task copyToDeployLib(type: Copy) { +tasks.register('copyToDeployLib', Copy) { from(configurations.runtimeClasspath) { exclude 'annotations-*.jar' } into(deployDir + '/lib') } -task deploy { +tasks.register('deploy') { description = "Copies all needed files to the ${deployDir} directory." group = 'Publishing' dependsOn(assemble, jar) @@ -228,7 +230,7 @@ task deploy { mustRunAfter(clean) } -task release { +tasks.register('release') { group = 'Publishing' description = 'Releases new version.' dependsOn(clean, check, deploy) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index da082db..db7e772 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -10,7 +10,6 @@ LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent ) LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) - LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean ) MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 MagicNumber:ChatGpt.kt$ChatGpt.Companion$429 @@ -33,7 +32,6 @@ MagicNumber:StockQuote.kt$StockQuote.Companion$10 MagicNumber:Tell.kt$Tell$50 MagicNumber:Tell.kt$Tell$7 - MagicNumber:TwitterOAuth.kt$TwitterOAuth$401 MagicNumber:Users.kt$Users$8 MagicNumber:Utils.kt$Utils$200 MagicNumber:Utils.kt$Utils$399 @@ -47,7 +45,6 @@ MagicNumber:WorldTime.kt$WorldTime.Companion$3600 MagicNumber:WorldTime.kt$WorldTime.Companion$60 MagicNumber:WorldTime.kt$WorldTime.Companion$86.4 - MaxLineLength:TwitterOAuth.kt$TwitterOAuth$* NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String @@ -65,13 +62,10 @@ NestedBlockDepth:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> NestedBlockDepth:Tell.kt$Tell$fun send(event: GenericUserEvent) - NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$@JvmStatic fun main(args: Array<String>) NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveSerialData(file: String, data: Any, logger: Logger, description: String) NestedBlockDepth:Weather2.kt$Weather2$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> - PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$ioe - PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$te ReturnCount:Addons.kt$Addons$fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException @@ -91,5 +85,6 @@ TooManyFunctions:EntryLink.kt$EntryLink : Serializable TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter TooManyFunctions:Tell.kt$Tell : AbstractCommand + WildcardImport:FeedReaderTest.kt$import assertk.assertions.* diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cbfa754578e88a3dae77fce6e3dea56edbf..c1962a79e29d3e0ab67b14947c167a862655af9b 100644 GIT binary patch delta 13895 zcmZ8|Wmp``)-~=Hdu)0n3Y-8OvyK$p9^s9MM|Aj$miotNhy-{udLczZyd9uWtD)X_{|!LhIEF9y8(e*Z zW>^w$u&x|i9OjL=#6Nl~*ERulzX>8C-}o;iSMRYdfCU5d`~U{V4>HCg0HG4Xg2uP;fn!>S9+>LbuWbc0bETMQfo9~h}yI*TSv;Oikl~t-+xqI-`P$Rj@yi{mr2zC~s1snMT3!OPBdJ%IDnPXq+pl*Z>=+?qo${lkCSKmwTlVjfb3thU6B8yFjr!tphOs*G6 zwL`RyVAUXj4p=9&@PpWK)m+REuvHaq838TEhY^7W+JAp$ zZ^y;8`Z*@VqJ{sFFj?<|7SKS@G`$Yi)gx%nOi@Lr zCv0IJlFz0bP(eDIW(uWNq?;8zEAb+uGgnkLk;y!4XhA6=Eoa<`+|;6mOq>z`%ir@z$4)Mkd3 zF=hFo zyd{*bRQ4YUe^bU*Y`__)Uhu5NIjVJ~a}{lHp-_7wI?#EB11XcqmdY>pk`JJ) zW9Rt!tK=N>fZ!UDomwMnb`0EOvTjcNl=yW@$c!OAg*=l()GjZwSyJ+o^;Zi#I5*uP z$6qeih8&g8E(pNSneK>93A(8*%gvwv!0V|SqGcj55Y7`=N*@pJx_ig3uVuf-G~LJbm`7nxNcZ>Jgqy(LTHu_C2e>STp^Pm{}f&^)XU}vzuU`UV&>e& zqsXNXSs;Wri|?NhCq0vXC5$>9Cag$adyWz^x@NCiy2${9Dc)Y;J8k1Z933W$3$H}g zCQFU1XwzGm_WUheXvnDisH_%BdzMgNwk2^mHcQu*x>U%iN*B^8U(eVz1~(%`kV1Vb z=9T0xmN?bQMyrrd?u}jer}zV&sCK6zSm!zV8A8dP6THF=4*V{_K*E*K<)I(Q^(eV!m!vu##-2g|G z{RB;{gJB_X-w{ANq?ft_n!@=O8_gj6FxW&zO$7L3@NjWt@R{NxMbpHLk6;=2$0P5P=kKc1_85inX z#s$&s0zhV1cz>nRb#|D#N8Z-=Tphm)sGH>9cz3K3I)6XpimJW0(6$GtLzN(YPu9%R zdFXG9|30AZME4r@joC0IdvBBe08mF@+5Dd97p$h=n|pi80Cn2n{ev!S$llPGLqHva zZ3*OmW%!Qj>C$F!Ffafl7#I_1(gz!aa)b{ebU*=yH%^kr=~N?|2&2Df2o9X=2B?U!#R#+Cj45=f@=EcQx+9J z=X3~A=zbX29Fqn23m3dm}0Voj^Q9BjI=MiG+NZ)YCYn@r^qv(xE3=)&i z=(ML301=rNTptvUt2tnsPb1~G*DWFWoZfv)wV|uNW%?!)jju`jN(K-0$JYi!ofNup z9K%_ucHwutbZsl~vDQ!Jtj8uI6WA6K--@?8+_=t>g|kgUeC=w`IP9m&*fuoO3#A;t z&3@=3;J0>yjM89?h5MG$S`wW+=vyYOWQGhIP`^vScM8^JL{mGan5uTJPvAg$0z}8; z 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 J*WbtZ{{Z#P!zlm& delta 13442 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 42defcc..37aef8d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68..aeb74cb 100755 --- a/gradlew +++ b/gradlew @@ -85,9 +85,6 @@ done APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +141,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +149,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 0f97f3c..fa8ce50 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -25,28 +25,13 @@ tell-max-days=5 tell-max-size=50 #disabled-commands=die, ignore -disabled-modules=twitter +disabled-modules=mastodon # # API Token for: https://pinboard.in/settings/password # #pinboard-api-token=user\:TOKEN -# -# Configure app at: https://developer.twitter.com/ -# and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth -# -#twitter-consumerKey= -#twitter-consumerSecret= -#twitter-token= -#twitter-tokenSecret= - -# Twitter handle to receive channel join/leave notifications -#twitter-handle= - -# Automatically post links to Mastodon -#twitter-auto-post=true - # # Create a Mastodon application access token at: https//SERVER_INSTANCE/settings/applications # Make sure the 'write:statuses' scope is enabled. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 6766f62..98ef74a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -59,6 +59,12 @@ object Constants { */ const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar" + /** + * User-Agent + */ + const val USER_AGENT = + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + /** * The help command. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index f56af2e..d82f011 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -74,7 +74,7 @@ class FeedReader(private val url: String, val event: GenericMessageEvent) : Runn fun readFeed(url: String, maxItems: Int = 5): List { val messages = mutableListOf() val input = SyndFeedInput() - XmlReader(URL(url)).use { reader -> + XmlReader(URL(url).openStream()).use { reader -> val feed = input.build(reader) val items = feed.entries if (items.isEmpty()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 2dff959..dabb7c8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -78,7 +78,6 @@ import net.thauvin.erik.mobibot.modules.Mastodon import net.thauvin.erik.mobibot.modules.Ping import net.thauvin.erik.mobibot.modules.RockPaperScissors import net.thauvin.erik.mobibot.modules.StockQuote -import net.thauvin.erik.mobibot.modules.Twitter import net.thauvin.erik.mobibot.modules.War import net.thauvin.erik.mobibot.modules.Weather2 import net.thauvin.erik.mobibot.modules.WolframAlpha @@ -438,7 +437,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(View()) // Load social modules - LinksManager.socialManager.add(addons, Twitter(), Mastodon()) + LinksManager.socialManager.add(addons, Mastodon()) // Load the modules addons.add(Calc()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt deleted file mode 100644 index ab078db..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * TwitterOAuth.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -import twitter4j.AccessToken -import twitter4j.OAuthAuthorization -import twitter4j.TwitterException -import java.io.BufferedReader -import java.io.IOException -import java.io.InputStreamReader -import kotlin.system.exitProcess - - -/** - * The `TwitterOAuth` class. - * - * Go to [https://developer.twitter.com/en/apps](https://developer.twitter.com/en/apps) to register your bot. - * - * Then execute: - * - * `java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth ` - * - * and follow the prompts/instructions. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net) - * @author [Yusuke Yamamoto](https://github.com/Twitter4J/Twitter4J/blob/main/twitter4j-examples/src/main/java/examples/oauth/GetAccessToken.java) - */ -object TwitterOAuth { - /** - * Twitter OAuth Client Registration. - * - * @param args The consumerKey and consumerSecret should be passed as arguments. - */ - @JvmStatic - fun main(args: Array) { - if (args.size == 2) { - try { - val oAuthAuthorization = OAuthAuthorization.getInstance(args[0], args[1]) - val requestToken = oAuthAuthorization.oAuthRequestToken - var accessToken: AccessToken? = null - val br = BufferedReader(InputStreamReader(System.`in`)) - while (null == accessToken) { - print( - """ - Open the following URL and grant access to your account: - - ${requestToken.authorizationURL} - - Enter the PIN (if available) or just hit enter. [PIN]: """.trimIndent() - ) - val pin = br.readLine() - try { - accessToken = if (!pin.isNullOrEmpty()) { - oAuthAuthorization.getOAuthAccessToken(requestToken, pin) - } else { - oAuthAuthorization.getOAuthAccessToken(requestToken) - } - } catch (te: TwitterException) { - if (401 == te.statusCode) { - println("Unable to get the access token.") - } else { - te.printStackTrace() - } - } - } - println( - """ - Please add the following to the bot's property file: - - twitter-consumerKey=${args[0]} - twitter-consumerSecret=${args[1]} - twitter-token=${accessToken.token} - twitter-tokenSecret=${accessToken.tokenSecret} - """.trimIndent() - ) - } catch (te: TwitterException) { - te.printStackTrace() - println("Failed to get accessToken: " + te.message) - exitProcess(-1) - } catch (ioe: IOException) { - ioe.printStackTrace() - println("Failed to read the system input.") - exitProcess(-1) - } - } else { - println("Usage: ${TwitterOAuth::class.java.name} ") - } - exitProcess(0) - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 8847bef..3647b31 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules +import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage import org.apache.commons.text.WordUtils @@ -106,6 +107,7 @@ class ChatGpt : AbstractModule() { .uri(URI.create(API_URL)) .header("Content-Type", "application/json") .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) .POST( HttpRequest.BodyPublishers.ofString( """{ @@ -136,8 +138,10 @@ class ChatGpt : AbstractModule() { } } else { if (response.statusCode() == 429) { - throw ModuleException("$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later.") + throw ModuleException( + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." + ) } else { throw IOException("HTTP Status Code: " + response.statusCode()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt deleted file mode 100644 index d4c02e1..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Twitter.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.social.SocialModule -import twitter4j.TwitterException - -/** - * The Twitter module. - */ -class Twitter : SocialModule() { - override val name = "Twitter" - - override val handle: String? - get() = properties[HANDLE_PROP] - - override val isAutoPost: Boolean - get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() - - override val isValidProperties: Boolean - get() = !(properties[CONSUMER_KEY_PROP].isNullOrBlank() || properties[CONSUMER_SECRET_PROP].isNullOrBlank() - || properties[TOKEN_PROP].isNullOrBlank() || properties[TOKEN_SECRET_PROP].isNullOrBlank()) - - /** - * Formats the entry for posting. - */ - override fun formatEntry(entry: EntryLink): String { - return "${entry.title} ${entry.link} via ${entry.nick} on ${entry.channel}" - } - - /** - * Posts on Twitter. - */ - @Throws(ModuleException::class) - override fun post(message: String, isDm: Boolean): String { - return tweet( - consumerKey = properties[CONSUMER_KEY_PROP], - consumerSecret = properties[CONSUMER_SECRET_PROP], - token = properties[TOKEN_PROP], - tokenSecret = properties[TOKEN_SECRET_PROP], - handle = handle, - message = message, - isDm = isDm - ) - } - - companion object { - // Property keys - const val AUTO_POST_PROP = "twitter-auto-post" - const val CONSUMER_KEY_PROP = "twitter-consumerKey" - const val CONSUMER_SECRET_PROP = "twitter-consumerSecret" - const val HANDLE_PROP = "twitter-handle" - const val TOKEN_PROP = "twitter-token" - const val TOKEN_SECRET_PROP = "twitter-tokenSecret" - - // Twitter commands - private const val TWITTER_CMD = "twitter" - private const val TWEET_CMD = "tweet" - - /** - * Post on Twitter. - */ - @JvmStatic - @Throws(ModuleException::class) - fun tweet( - consumerKey: String?, - consumerSecret: String?, - token: String?, - tokenSecret: String?, - handle: String?, - message: String, - isDm: Boolean - ): String { - return try { - val twitter = twitter4j.Twitter.newBuilder() - .prettyDebugEnabled(true) - .oAuthConsumer(consumerKey, consumerSecret) - .oAuthAccessToken(token, tokenSecret) - .build() - if (!isDm) { - val status = twitter.v1().tweets().updateStatus(message) - "Your message was posted to https://twitter.com/${ - twitter.v1().users().accountSettings.screenName - }/statuses/${status.id}" - } else { - val dm = twitter.v1().directMessages().sendDirectMessage(handle, message) - dm.text - } - } catch (e: TwitterException) { - throw ModuleException("tweet($message)", "An error has occurred: ${e.message}", e) - } - } - } - - init { - commands.add(TWITTER_CMD) - commands.add(TWEET_CMD) - help.add("To $TWEET_CMD on $name:") - help.add(helpFormat("%c $TWEET_CMD ")) - properties[AUTO_POST_PROP] = "false" - initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) - } -} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 8662392..ebc2aa0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -45,7 +45,6 @@ import net.thauvin.erik.mobibot.modules.Dice import net.thauvin.erik.mobibot.modules.Joke import net.thauvin.erik.mobibot.modules.Lookup import net.thauvin.erik.mobibot.modules.RockPaperScissors -import net.thauvin.erik.mobibot.modules.Twitter import net.thauvin.erik.mobibot.modules.War import org.testng.annotations.Test import java.util.Properties @@ -62,7 +61,6 @@ class AddonsTest { // Modules addons.add(Joke()) addons.add(RockPaperScissors()) - addons.add(Twitter()) // no properties, disabled. addons.add(War()) addons.add(Dice()) addons.add(Lookup()) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 0d66a0d..d30977e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -31,14 +31,9 @@ package net.thauvin.erik.mobibot import assertk.all +import assertk.assertFailure import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFailure -import assertk.assertions.isInstanceOf -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import com.rometools.rome.io.FeedException import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed import net.thauvin.erik.mobibot.msg.Message @@ -66,13 +61,13 @@ class FeedReaderTest { assertThat(messages, "messages").size().isEqualTo(84) assertThat(messages.last(), "messages.last").prop(Message::msg).contains("techdigest.tv") - assertThat { readFeed("blah") }.isFailure().isInstanceOf(MalformedURLException::class.java) + assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) - assertThat { readFeed("https://www.example.com") }.isFailure().isInstanceOf(FeedException::class.java) + assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java) - assertThat { readFeed("https://www.thauvin.net/foo") }.isFailure().isInstanceOf(IOException::class.java) + assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) - assertThat { readFeed("https://www.examplesfoo.com/") }.isFailure() + assertFailure { readFeed("https://www.examplesfoo.com/") } .isInstanceOf(UnknownHostException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index fb0402e..b3bd248 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -30,6 +30,7 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFailure @@ -48,6 +49,6 @@ class CalcTest { assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}") - assertThat { calculate("one + one") }.isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java) + assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 5e0e1d2..e4638b9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -30,6 +30,7 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause @@ -41,8 +42,7 @@ import org.testng.annotations.Test class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { - assertThat { ChatGpt.chat("1 gallon to liter", "", 0) } - .isFailure() + assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } @@ -57,8 +57,7 @@ class ChatGptTest : LocalProperties() { ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") - assertThat { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isFailure() + assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index c2bb833..175af47 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage @@ -59,14 +60,13 @@ class GoogleSearchTest : LocalProperties() { "searchGoogle(empty)" ).isInstanceOf(ErrorMessage::class.java) - assertThat { searchGoogle("test", "", "apiKey") }.isFailure() + assertFailure { searchGoogle("test", "", "apiKey") } .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThat { searchGoogle("test", "apiKey", "") }.isFailure() + assertFailure { searchGoogle("test", "apiKey", "") } .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThat { searchGoogle("test", "apiKey", "cssKey") } - .isFailure() + assertFailure { searchGoogle("test", "apiKey", "cssKey") } .isInstanceOf(ModuleException::class.java) .hasMessage("API key not valid. Please pass a valid API key.") } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index d464f03..34f778a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.isSuccess import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot import org.testng.annotations.Test @@ -42,7 +41,7 @@ class MastodonTest : LocalProperties() { @Throws(ModuleException::class) fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" - assertThat { + assertThat( toot( getProperty(Mastodon.ACCESS_TOKEN_PROP), getProperty(Mastodon.INSTANCE_PROP), @@ -50,6 +49,6 @@ class MastodonTest : LocalProperties() { msg, true ) - }.isSuccess().contains(msg) + ).contains(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index d35a3d3..b4a277e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all +import assertk.assertFailure import assertk.assertThat import assertk.assertions.hasNoCause import assertk.assertions.index @@ -78,7 +79,7 @@ class StockQuoteTest : LocalProperties() { isInstanceOf(ErrorMessage::class.java) prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) } - assertThat { getQuote("test", "") }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getQuote("test", "") }.isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI")) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt deleted file mode 100644 index 6e4ab27..0000000 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * TwitterTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isSuccess -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Twitter.Companion.tweet -import org.testng.annotations.Test - -/** - * The `TwitterTest` class. - */ -class TwitterTest : LocalProperties() { - @Test(groups = ["modules", "twitter"]) - @Throws(ModuleException::class) - fun testTweet() { - val msg = "Testing Twitter API from ${getHostName()}" - assertThat { - tweet( - getProperty(Twitter.CONSUMER_KEY_PROP), - getProperty(Twitter.CONSUMER_SECRET_PROP), - getProperty(Twitter.TOKEN_PROP), - getProperty(Twitter.TOKEN_SECRET_PROP), - getProperty(Twitter.HANDLE_PROP), - msg, - true - ) - }.isSuccess().isEqualTo(msg) - } -} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 4c7f7e6..ca650bb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.endsWith @@ -116,8 +117,8 @@ class Weather2Test : LocalProperties() { } query = "test" - assertThat { getWeather(query, "") }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause() - assertThat { getWeather(query, null) }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getWeather(query, "") }.isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getWeather(query, null) }.isInstanceOf(ModuleException::class.java).hasNoCause() messages = getWeather("", "apikey") assertThat(messages, "getWeather(empty)").index(0).prop(Message::isError).isTrue() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 4aaf620..ae1722d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -31,10 +31,10 @@ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage -import assertk.assertions.isFailure import assertk.assertions.isInstanceOf import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties @@ -44,13 +44,11 @@ import org.testng.annotations.Test class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules"]) fun testAppId() { - assertThat { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isFailure() + assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } .isInstanceOf(ModuleException::class.java) .hasMessage("Error 1: Invalid appid") - assertThat { queryWolfram("1 gallon to liter", appId = "") } - .isFailure() + assertFailure { queryWolfram("1 gallon to liter", appId = "") } .isInstanceOf(ModuleException::class.java) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index f17ed1d..46888e3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -30,6 +30,7 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.endsWith import assertk.assertions.isSuccess @@ -65,7 +66,7 @@ class WordTimeTest { @Test(groups = ["modules"]) fun testZones() { COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { - assertThat { ZoneId.of(it.value) }.isSuccess() + assertThat(ZoneId.of(it.value)) } } } diff --git a/version.properties b/version.properties index 693db4f..27ca594 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Jan 30 22:08:48 PST 2023 -version.buildmeta=986 +#Sat May 20 23:54:50 PDT 2023 +version.buildmeta=1077 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+986 +version.semver=0.8.0-rc+1077 diff --git a/website/index.html b/website/index.html index 97e337d..1ffaad2 100644 --- a/website/index.html +++ b/website/index.html @@ -46,7 +46,6 @@
  16. Pinboard Poster
  17. PircBotX
  18. Rome
  19. -
  20. Twitter4J
  21. UrlEncoder
  22. mobibot was written by From e57f80f9d9152d49853b754adb4b3f4b87e394a6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 00:16:46 -0700 Subject: [PATCH 712/842] Moved to JDK 17 --- .circleci/config.yml | 12 ++++++------ .github/workflows/gradle.yml | 4 ++-- .idea/compiler.xml | 2 +- .idea/misc.xml | 2 +- README.md | 3 ++- build.gradle | 6 +++--- version.properties | 6 +++--- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b76d7e8..67e6618 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,19 +31,19 @@ defaults_gradle: &defaults_gradle path: build/reports/ jobs: - build_gradle_jdk18: + build_gradle_jdk17: <<: *defaults docker: - - image: cimg/openjdk:18.0 + - image: cimg/openjdk:17.0 <<: *defaults_gradle - build_gradle_jdk11: + build_gradle_jdk20: <<: *defaults docker: - - image: cimg/openjdk:11.0 + - image: cimg/openjdk:20.0 <<: *defaults_gradle @@ -51,5 +51,5 @@ workflows: version: 2 gradle: jobs: - - build_gradle_jdk11 - - build_gradle_jdk18 + - build_gradle_jdk17 + - build_gradle_jdk20 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 0034945..c917819 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -8,11 +8,11 @@ jobs: env: GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m" - SONAR_JDK: "11" + SONAR_JDK: "17" strategy: matrix: - java-version: [ 11, 18 ] + java-version: [ 17, 20 ] steps: - uses: actions/checkout@v3 diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..b589d56 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 323152a..1bcb8e7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -7,5 +7,5 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index 8105ed3..1c4de30 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-7f52ff.svg)](https://kotlinlang.org)[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.21-7f52ff.svg)](https://kotlinlang.org) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/build.gradle b/build.gradle index 6a131ab..e3d5453 100644 --- a/build.gradle +++ b/build.gradle @@ -112,13 +112,13 @@ tasks.withType(Test).configureEach { } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlin { jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(11)) + languageVersion.set(JavaLanguageVersion.of(17)) } } diff --git a/version.properties b/version.properties index 27ca594..b4770f6 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat May 20 23:54:50 PDT 2023 -version.buildmeta=1077 +#Sun May 21 00:12:53 PDT 2023 +version.buildmeta=1078 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1077 +version.semver=0.8.0-rc+1078 From 37b4eb4343bcb3f6d97842df499544191c882856 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 00:56:47 -0700 Subject: [PATCH 713/842] Added default max token for ChatGPT --- .circleci/config.yml | 6 +++--- build.gradle | 17 ++++++++++++----- .../net/thauvin/erik/mobibot/modules/ChatGpt.kt | 3 ++- version.properties | 6 +++--- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 67e6618..140179a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -39,11 +39,11 @@ jobs: <<: *defaults_gradle - build_gradle_jdk20: + build_gradle_jdk19: <<: *defaults docker: - - image: cimg/openjdk:20.0 + - image: cimg/openjdk:19.0 <<: *defaults_gradle @@ -52,4 +52,4 @@ workflows: gradle: jobs: - build_gradle_jdk17 - - build_gradle_jdk20 + - build_gradle_jdk19 diff --git a/build.gradle b/build.gradle index e3d5453..31ec925 100644 --- a/build.gradle +++ b/build.gradle @@ -193,19 +193,26 @@ incrementBuildMeta { } } +koverReport { + defaults { + xml { + onCheck = true + } + html { + onCheck = true + } + } +} + sonarqube { properties { property('sonar.organization', 'ethauvin-github') property('sonar.projectKey', 'ethauvin_mobibot') property('sonar.host.url', 'https://sonarcloud.io') - property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/xml/report.xml") + property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/report.xml") } } -tasks.sonar { - dependsOn 'koverReport' -} - tasks.register('copyToDeploy', Copy) { from('properties', jar) into deployDir diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 3647b31..c1f660e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -55,7 +55,8 @@ class ChatGpt : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[API_KEY_PROP], properties[MAX_TOKENS_PROP]!!.toInt()) + val answer = chat(args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) } else { diff --git a/version.properties b/version.properties index b4770f6..654c604 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun May 21 00:12:53 PDT 2023 -version.buildmeta=1078 +#Sun May 21 00:52:16 PDT 2023 +version.buildmeta=1085 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1078 +version.semver=0.8.0-rc+1085 From e94b1aa97d74a60a8448ab7d5aa5764af9795ccf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 01:08:05 -0700 Subject: [PATCH 714/842] Potential fix for CircleCI --- build.gradle | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build.gradle b/build.gradle index 31ec925..e6ae710 100644 --- a/build.gradle +++ b/build.gradle @@ -158,14 +158,6 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } -tasks.withType(Detekt).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - -tasks.withType(DetektCreateBaselineTask).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) From 1825b13a23851639fe006cfc71c43d555099bb94 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 01:13:17 -0700 Subject: [PATCH 715/842] Disabled JDK 19 from CircleCI --- .circleci/config.yml | 1 - build.gradle | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 140179a..1868b37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,4 +52,3 @@ workflows: gradle: jobs: - build_gradle_jdk17 - - build_gradle_jdk19 diff --git a/build.gradle b/build.gradle index e6ae710..31ec925 100644 --- a/build.gradle +++ b/build.gradle @@ -158,6 +158,14 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } +tasks.withType(Detekt).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + +tasks.withType(DetektCreateBaselineTask).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) From 945c763768dc1251e1d1d0ba7944e6e88264b706 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 02:03:31 -0700 Subject: [PATCH 716/842] Moved to JDK 17 for GitLab and BitBucket --- .gitlab-ci.yml | 2 +- bitbucket-pipelines.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 490f7b8..48a3396 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: gradle:7-jdk18 +image: gradle:8-jdk17 variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index a9514a0..623b3c3 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,4 +1,4 @@ -image: maven:3-openjdk-18 +image: maven:3-eclipse-temurin-17 pipelines: default: From 66b8adb743e361eb5e848b63aaded63fab30211e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 23 May 2023 08:31:51 -0700 Subject: [PATCH 717/842] Upgraded to PircBotX 2.3.1 --- build.gradle | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 31ec925..d35a393 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ dependencies { compileOnly(semverProcessor) // PircBotX - implementation 'com.github.pircbotx:pircbotx:master-SNAPSHOT' + implementation 'com.github.pircbotx:pircbotx:2.3.1' // implementation fileTree(dir: 'lib', include: '*.jar') // Commons (mostly for PircBotX) diff --git a/version.properties b/version.properties index 654c604..4ce7fe9 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun May 21 00:52:16 PDT 2023 -version.buildmeta=1085 +#Tue May 23 08:26:47 PDT 2023 +version.buildmeta=1087 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1085 +version.semver=0.8.0-rc+1087 From d8291e21562c6010937adab5991dff219bd6a299 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 17 Jun 2023 21:30:55 -0700 Subject: [PATCH 718/842] Added all keyword to seen command --- .idea/kotlinc.xml | 2 +- build.gradle | 16 ++++++------ config/detekt/baseline.xml | 2 ++ .../erik/mobibot/commands/seen/Seen.kt | 26 ++++++++++++++++++- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- version.properties | 6 ++--- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 217e5c5..9a55c2d 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle b/build.gradle index d35a393..ff50536 100644 --- a/build.gradle +++ b/build.gradle @@ -3,15 +3,15 @@ import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.46.0' + id 'com.github.ben-manes.versions' version '0.47.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.22.0' + id 'io.gitlab.arturbosch.detekt' version '1.23.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.21' - id 'org.jetbrains.kotlin.kapt' version '1.8.21' - id 'org.jetbrains.kotlinx.kover' version '0.7.0' - id 'org.sonarqube' version '4.0.0.2929' + id 'org.jetbrains.kotlin.jvm' version '1.8.22' + id 'org.jetbrains.kotlin.kapt' version '1.8.22' + id 'org.jetbrains.kotlinx.kover' version '0.7.1' + id 'org.sonarqube' version '4.2.1.3168' id 'pmd' } @@ -59,11 +59,11 @@ dependencies { // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:31.1-jre' + implementation 'com.google.guava:guava:32.0.1-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + implementation 'org.jetbrains.kotlin:kotlin-stdlib' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index db7e772..356067c 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -27,6 +27,7 @@ MagicNumber:Mastodon.kt$Mastodon.Companion$200 MagicNumber:Mobibot.kt$Mobibot$8 MagicNumber:Modules.kt$Modules$7 + MagicNumber:Seen.kt$Seen$8 MagicNumber:SocialManager.kt$SocialManager$1000L MagicNumber:SocialManager.kt$SocialManager$60L MagicNumber:StockQuote.kt$StockQuote.Companion$10 @@ -69,6 +70,7 @@ ReturnCount:Addons.kt$Addons$fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException + ReturnCount:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 4a45598..fca143a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -32,10 +32,14 @@ package net.thauvin.erik.mobibot.commands.seen import com.google.common.collect.ImmutableSortedSet +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.loadSerialData import net.thauvin.erik.mobibot.Utils.saveSerialData +import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime @@ -43,15 +47,19 @@ import org.pircbotx.User import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.util.TreeMap +import java.util.* class Seen(private val serialObject: String) : AbstractCommand() { private val logger: Logger = LoggerFactory.getLogger(Seen::class.java) + private val allKeyword = "all" val seenNicks = TreeMap(NickComparator()) override val name = "seen" override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name ")) + private val helpOp = help.plus( + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + ) override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -61,6 +69,11 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { if (args.isNotBlank() && !args.contains(' ')) { val ch = event.bot().userChannelDao.getChannel(channel) + if (args.equals(allKeyword) && ch.isOp(event.user) && seenNicks.isNotEmpty()) { + event.sendMessage("The ${"seen".bold()} nicks are:") + event.sendList(seenNicks.keys.toList(), 8, separator = ", ", isIndent = true) + return + } ch.users.forEach { if (args.equals(it.nick, true)) { event.sendMessage("${it.nick} is on ${channel}.") @@ -102,6 +115,17 @@ class Seen(private val serialObject: String) : AbstractCommand() { fun count(): Int = seenNicks.size + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + return if (event.isChannelOp(channel)) { + for (h in helpOp) { + event.sendMessage(Utils.helpCmdSyntax(h, event.bot().nick, true)) + } + true + } else { + super.helpResponse(channel, topic, event) + } + } + fun load() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index bc64191..8de54e4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -46,6 +46,6 @@ data class EntryComment(var comment: String, var nick: String) : Serializable { companion object { // Serial version UID - const val serialVersionUID = 1L + private const val serialVersionUID: Long = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 7bf003d..fc61d18 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -208,6 +208,6 @@ class EntryLink( companion object { // Serial version UID - const val serialVersionUID = 1L + private const val serialVersionUID: Long = 1L } } diff --git a/version.properties b/version.properties index 4ce7fe9..9cd983c 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 23 08:26:47 PDT 2023 -version.buildmeta=1087 +#Sat Jun 17 21:28:53 PDT 2023 +version.buildmeta=1098 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1087 +version.semver=0.8.0-rc+1098 From 5834a23b7e9a7856c3c7c113a109fcc4357c0e22 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 2 Jul 2023 02:23:09 -0700 Subject: [PATCH 719/842] Updated dependencies --- .idea/misc.xml | 5 +++++ README.md | 2 +- build.gradle | 14 ++++++++------ config/detekt/baseline.xml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 62076 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 5 ++++- .../erik/mobibot/commands/seen/Seen.kt | 4 ++-- version.properties | 6 +++--- 9 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 1bcb8e7..f648a66 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,6 +5,11 @@ + diff --git a/README.md b/README.md index 1c4de30..6739889 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.21-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.22-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/build.gradle b/build.gradle index ff50536..a3677ef 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask @@ -10,7 +12,7 @@ plugins { id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.8.22' id 'org.jetbrains.kotlin.kapt' version '1.8.22' - id 'org.jetbrains.kotlinx.kover' version '0.7.1' + id 'org.jetbrains.kotlinx.kover' version '0.7.2' id 'org.sonarqube' version '4.2.1.3168' id 'pmd' } @@ -54,17 +56,17 @@ dependencies { // Commons (mostly for PircBotX) implementation 'org.apache.commons:commons-lang3:3.12.0' implementation 'org.apache.commons:commons-text:1.10.0' - implementation 'commons-codec:commons-codec:1.15' + implementation 'commons-codec:commons-codec:1.16.0' implementation 'commons-net:commons-net:3.9.0' // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.0.1-jre' + implementation 'com.google.guava:guava:32.1.1-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' // Logging @@ -77,7 +79,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.11.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20230227' + implementation 'org.json:json:20230618' implementation 'org.jsoup:jsoup:1.16.1' // Thauvin @@ -188,7 +190,7 @@ incrementBuildMeta { if (isCI) { println 'No increment with CI.' } else { - buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) + buildMeta = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) } } } diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 356067c..0aa592e 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -27,7 +27,7 @@ MagicNumber:Mastodon.kt$Mastodon.Companion$200 MagicNumber:Mobibot.kt$Mobibot$8 MagicNumber:Modules.kt$Modules$7 - MagicNumber:Seen.kt$Seen$8 + MagicNumber:Seen.kt$Seen$7 MagicNumber:SocialManager.kt$SocialManager$1000L MagicNumber:SocialManager.kt$SocialManager$60L MagicNumber:StockQuote.kt$StockQuote.Companion$10 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79e29d3e0ab67b14947c167a862655af9b..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 16170 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 delta 14892 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d..62f495d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cb..fcb6fca 100755 --- a/gradlew +++ b/gradlew @@ -130,10 +130,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index fca143a..c9ee0f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -69,9 +69,9 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { if (args.isNotBlank() && !args.contains(' ')) { val ch = event.bot().userChannelDao.getChannel(channel) - if (args.equals(allKeyword) && ch.isOp(event.user) && seenNicks.isNotEmpty()) { + if (args == allKeyword && ch.isOp(event.user) && seenNicks.isNotEmpty()) { event.sendMessage("The ${"seen".bold()} nicks are:") - event.sendList(seenNicks.keys.toList(), 8, separator = ", ", isIndent = true) + event.sendList(seenNicks.keys.toList(), 7, separator = ", ", isIndent = true) return } ch.users.forEach { diff --git a/version.properties b/version.properties index 9cd983c..b464c4e 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jun 17 21:28:53 PDT 2023 -version.buildmeta=1098 +#Sun Jul 02 02:19:45 PDT 2023 +version.buildmeta=20230702021945 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1098 +version.semver=0.8.0-rc+20230702021945 From 2313d36584fdb363b8eeb93331cf05a60e63f12a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 6 Jul 2023 10:23:04 -0700 Subject: [PATCH 720/842] Upgraded to Koltin 1.9.0 --- .idea/codeStyles/codeStyleConfig.xml | 3 +- .idea/kotlinc.xml | 2 +- README.md | 2 +- build.gradle | 4 +- config/detekt/baseline.xml | 26 ++++ .../net/thauvin/erik/mobibot/modules/War.java | 12 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 115 ++++++------------ .../net/thauvin/erik/mobibot/Pinboard.kt | 4 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 35 +++--- .../thauvin/erik/mobibot/commands/Ignore.kt | 14 +-- .../net/thauvin/erik/mobibot/commands/Info.kt | 10 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 4 +- .../thauvin/erik/mobibot/commands/Recap.kt | 8 +- .../thauvin/erik/mobibot/commands/Versions.kt | 8 +- .../erik/mobibot/commands/links/Comment.kt | 46 +++---- .../mobibot/commands/links/LinksManager.kt | 6 +- .../erik/mobibot/commands/links/Posting.kt | 20 +-- .../erik/mobibot/commands/links/Tags.kt | 4 +- .../erik/mobibot/commands/links/View.kt | 10 +- .../erik/mobibot/commands/seen/Seen.kt | 14 +-- .../erik/mobibot/commands/tell/Tell.kt | 44 +++---- .../erik/mobibot/commands/tell/TellMessage.kt | 24 ++-- .../thauvin/erik/mobibot/entries/Entries.kt | 8 +- .../erik/mobibot/entries/EntriesUtils.kt | 6 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 59 +++++---- .../erik/mobibot/entries/FeedsManager.kt | 48 ++++---- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 40 +++--- .../erik/mobibot/modules/CryptoPrices.kt | 6 +- .../erik/mobibot/modules/CurrencyConverter.kt | 22 ++-- .../erik/mobibot/modules/GoogleSearch.kt | 30 ++--- .../thauvin/erik/mobibot/modules/Lookup.kt | 6 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 40 +++--- .../erik/mobibot/modules/ModuleException.kt | 6 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 24 ++-- .../erik/mobibot/modules/RockPaperScissors.kt | 10 +- .../erik/mobibot/modules/StockQuote.kt | 66 +++++----- .../thauvin/erik/mobibot/modules/Weather2.kt | 36 +++--- .../erik/mobibot/modules/WolframAlpha.kt | 28 ++--- .../thauvin/erik/mobibot/modules/WorldTime.kt | 4 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 10 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 3 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 4 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 18 ++- .../erik/mobibot/ExceptionSanitizer.kt | 6 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 35 +++--- .../thauvin/erik/mobibot/commands/InfoTest.kt | 8 +- .../erik/mobibot/commands/RecapTest.kt | 6 +- .../commands/links/LinksManagerTest.kt | 4 +- .../erik/mobibot/commands/links/ViewTest.kt | 16 +-- .../erik/mobibot/commands/seen/SeenTest.kt | 9 +- .../commands/tell/TellMessagesMgrTest.kt | 8 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 20 +-- .../erik/mobibot/entries/EntryLinkTest.kt | 20 ++- .../erik/mobibot/entries/FeedMgrTest.kt | 10 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 1 - .../erik/mobibot/modules/ChatGptTest.kt | 11 +- .../mobibot/modules/CurrencyConverterTest.kt | 12 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 8 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 22 ++-- .../thauvin/erik/mobibot/modules/JokeTest.kt | 7 +- .../erik/mobibot/modules/MastodonTest.kt | 14 +-- .../mobibot/modules/ModuleExceptionTest.kt | 22 ++-- .../erik/mobibot/modules/StockQuoteTest.kt | 11 +- .../erik/mobibot/modules/Weather2Test.kt | 13 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 10 +- .../erik/mobibot/modules/WordTimeTest.kt | 8 +- version.properties | 6 +- 76 files changed, 549 insertions(+), 645 deletions(-) diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 6e6eec1..d91f848 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,6 +1,5 @@ - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 9a55c2d..fdf8d99 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/README.md b/README.md index 6739889..df026ab 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.22-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.0-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/build.gradle b/build.gradle index a3677ef..fa00b54 100644 --- a/build.gradle +++ b/build.gradle @@ -10,8 +10,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.23.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.22' - id 'org.jetbrains.kotlin.kapt' version '1.8.22' + id 'org.jetbrains.kotlin.jvm' version '1.9.0' + id 'org.jetbrains.kotlin.kapt' version '1.9.0' id 'org.jetbrains.kotlinx.kover' version '0.7.2' id 'org.sonarqube' version '4.2.1.3168' id 'pmd' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 0aa592e..f7cc151 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -46,6 +46,15 @@ MagicNumber:WorldTime.kt$WorldTime.Companion$3600 MagicNumber:WorldTime.kt$WorldTime.Companion$60 MagicNumber:WorldTime.kt$WorldTime.Companion$86.4 + MaxLineLength:DiceTest.kt$DiceTest$. + MaxLineLength:Lookup.kt$Lookup$("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + MaxLineLength:Mastodon.kt$Mastodon.Companion$mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + MaxLineLength:Mobibot.kt$Mobibot$helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) + MaxLineLength:PinboardTest.kt$PinboardTest$URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + MaxLineLength:StockQuote.kt$StockQuote.Companion$+ + MaxLineLength:Utils.kt$Utils$list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = "") + MaxLineLength:View.kt$View$helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + MaxLineLength:Weather2.kt$Weather2.Companion$country.name.replace('_', ' ').capitalizeWords() NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String @@ -87,6 +96,23 @@ TooManyFunctions:EntryLink.kt$EntryLink : Serializable TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter TooManyFunctions:Tell.kt$Tell : AbstractCommand + WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.* + WildcardImport:EntryLinkTest.kt$import assertk.assertions.* + WildcardImport:FeedMgrTest.kt$import assertk.assertions.* WildcardImport:FeedReaderTest.kt$import assertk.assertions.* + WildcardImport:FeedsManager.kt$import com.rometools.rome.feed.synd.* + WildcardImport:GoogleSearchTest.kt$import assertk.assertions.* + WildcardImport:JokeTest.kt$import assertk.assertions.* + WildcardImport:Mobibot.kt$import java.io.* + WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.* + WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.links.* + WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.modules.* + WildcardImport:Mobibot.kt$import org.pircbotx.hooks.events.* + WildcardImport:ModuleExceptionTest.kt$import assertk.assertions.* + WildcardImport:SeenTest.kt$import assertk.assertions.* + WildcardImport:StockQuoteTest.kt$import assertk.assertions.* + WildcardImport:TellMessagesMgrTest.kt$import assertk.assertions.* + WildcardImport:Utils.kt$import java.io.* + WildcardImport:Weather2Test.kt$import assertk.assertions.* diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index d1d7882..4bbbd9b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -72,6 +72,12 @@ public final class War extends AbstractModule { help.add(Utils.helpFormat("%c " + WAR_CMD)); } + @NotNull + @Override + public String getName() { + return "War"; + } + /** * {@inheritDoc} */ @@ -99,10 +105,4 @@ public final class War extends AbstractModule { } while (i == y); } - - @NotNull - @Override - public String getName() { - return "War"; - } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 1127f02..2c5f05d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -38,7 +38,7 @@ import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.util.Properties +import java.util.* /** * Modules and Commands addons. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 98ef74a..7cf6719 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -63,7 +63,7 @@ object Constants { * User-Agent */ const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index dabb7c8..3342077 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -45,67 +45,24 @@ import net.thauvin.erik.mobibot.Utils.lastOrEmpty import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import net.thauvin.erik.mobibot.commands.ChannelFeed -import net.thauvin.erik.mobibot.commands.Cycle -import net.thauvin.erik.mobibot.commands.Die -import net.thauvin.erik.mobibot.commands.Ignore -import net.thauvin.erik.mobibot.commands.Info -import net.thauvin.erik.mobibot.commands.Me -import net.thauvin.erik.mobibot.commands.Modules -import net.thauvin.erik.mobibot.commands.Msg -import net.thauvin.erik.mobibot.commands.Nick -import net.thauvin.erik.mobibot.commands.Recap +import net.thauvin.erik.mobibot.commands.* import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap -import net.thauvin.erik.mobibot.commands.Say -import net.thauvin.erik.mobibot.commands.Users -import net.thauvin.erik.mobibot.commands.Versions -import net.thauvin.erik.mobibot.commands.links.Comment -import net.thauvin.erik.mobibot.commands.links.LinksManager -import net.thauvin.erik.mobibot.commands.links.Posting -import net.thauvin.erik.mobibot.commands.links.Tags -import net.thauvin.erik.mobibot.commands.links.View +import net.thauvin.erik.mobibot.commands.links.* import net.thauvin.erik.mobibot.commands.seen.Seen import net.thauvin.erik.mobibot.commands.tell.Tell -import net.thauvin.erik.mobibot.modules.Calc -import net.thauvin.erik.mobibot.modules.ChatGpt -import net.thauvin.erik.mobibot.modules.CryptoPrices -import net.thauvin.erik.mobibot.modules.CurrencyConverter -import net.thauvin.erik.mobibot.modules.Dice -import net.thauvin.erik.mobibot.modules.GoogleSearch -import net.thauvin.erik.mobibot.modules.Joke -import net.thauvin.erik.mobibot.modules.Lookup -import net.thauvin.erik.mobibot.modules.Mastodon -import net.thauvin.erik.mobibot.modules.Ping -import net.thauvin.erik.mobibot.modules.RockPaperScissors -import net.thauvin.erik.mobibot.modules.StockQuote -import net.thauvin.erik.mobibot.modules.War -import net.thauvin.erik.mobibot.modules.Weather2 -import net.thauvin.erik.mobibot.modules.WolframAlpha -import net.thauvin.erik.mobibot.modules.WorldTime +import net.thauvin.erik.mobibot.modules.* import net.thauvin.erik.semver.Version import org.pircbotx.Configuration import org.pircbotx.PircBotX import org.pircbotx.hooks.ListenerAdapter -import org.pircbotx.hooks.events.ActionEvent -import org.pircbotx.hooks.events.DisconnectEvent -import org.pircbotx.hooks.events.JoinEvent -import org.pircbotx.hooks.events.MessageEvent -import org.pircbotx.hooks.events.NickChangeEvent -import org.pircbotx.hooks.events.PartEvent -import org.pircbotx.hooks.events.PrivateMessageEvent -import org.pircbotx.hooks.events.QuitEvent +import org.pircbotx.hooks.events.* import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.io.BufferedOutputStream -import java.io.File -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException -import java.io.PrintStream +import java.io.* import java.nio.file.Files import java.nio.file.Paths -import java.util.Properties +import java.util.* import java.util.regex.Pattern import kotlin.system.exitProcess @@ -140,9 +97,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event.sendMessage("Type a URL on $channel to post it.") event.sendMessage("For more information on a specific command, type:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c ${Constants.HELP_CMD} ", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c ${Constants.HELP_CMD} ", event.bot().nick, event is PrivateMessageEvent) + ) ) event.sendMessage("The commands are:") event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) @@ -204,7 +161,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has joined ${event.channel.name} on $serverHostname" + "$nick has joined ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -252,7 +209,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has left ${event.channel.name} on $serverHostname" + "$nick has left ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -275,22 +232,22 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Set up the command line options val parser = ArgParser(Constants.CLI_CMD) val debug by parser.option( - ArgType.Boolean, - Constants.DEBUG_ARG, - Constants.DEBUG_ARG.substring(0, 1), - "Print debug & logging data directly to the console" + ArgType.Boolean, + Constants.DEBUG_ARG, + Constants.DEBUG_ARG.substring(0, 1), + "Print debug & logging data directly to the console" ).default(false) val property by parser.option( - ArgType.String, - Constants.PROPS_ARG, - Constants.PROPS_ARG.substring(0, 1), - "Use alternate properties file" + ArgType.String, + Constants.PROPS_ARG, + Constants.PROPS_ARG.substring(0, 1), + "Use alternate properties file" ).default("./${ReleaseInfo.PROJECT}.properties") val version by parser.option( - ArgType.Boolean, - Constants.VERSION_ARG, - Constants.VERSION_ARG.substring(0, 1), - "Print version info" + ArgType.Boolean, + Constants.VERSION_ARG, + Constants.VERSION_ARG.substring(0, 1), + "Print version info" ).default(false) // Parse the command line @@ -299,8 +256,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (version) { // Output the version println( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" ) println(ReleaseInfo.WEBSITE) } else { @@ -308,7 +265,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro val p = Properties() try { Files.newInputStream( - Paths.get(property) + Paths.get(property) ).use { fis -> p.load(fis) } @@ -327,11 +284,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (!debug) { try { val stdout = PrintStream( - BufferedOutputStream( - FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true - ) - ), true + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true + ) + ), true ) System.setOut(stdout) } catch (ignore: IOException) { @@ -340,9 +297,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro } try { val stderr = PrintStream( - BufferedOutputStream( - FileOutputStream("$logsDir$nickname.err", true) - ), true + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true ) System.setErr(stderr) } catch (ignore: IOException) { @@ -367,8 +324,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro login = p.getProperty("login", nickname) realName = p.getProperty("realname", nickname) addServer( - ircServer, - p.getIntProperty("port", Constants.DEFAULT_PORT) + ircServer, + p.getIntProperty("port", Constants.DEFAULT_PORT) ) addAutoJoinChannel(channel) addListener(this@Mobibot) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index b829bab..1a4260d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -37,7 +37,7 @@ import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit -import java.util.Date +import java.util.* /** * Handles posts to pinboard.in. @@ -92,7 +92,7 @@ class Pinboard { */ private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( - toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() ).format(DateTimeFormatter.ISO_INSTANT) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index f61c56c..0595220 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -39,11 +39,7 @@ import org.pircbotx.PircBotX import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger -import java.io.BufferedInputStream -import java.io.BufferedOutputStream -import java.io.IOException -import java.io.ObjectInputStream -import java.io.ObjectOutputStream +import java.io.* import java.net.HttpURLConnection import java.net.URL import java.nio.file.Files @@ -51,8 +47,7 @@ import java.nio.file.Paths import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter -import java.util.Date -import java.util.Properties +import java.util.* import kotlin.io.path.exists import kotlin.io.path.fileSize @@ -220,7 +215,7 @@ object Utils { if (serialFile.exists() && serialFile.fileSize() > 0) { try { ObjectInputStream( - BufferedInputStream(Files.newInputStream(serialFile)) + BufferedInputStream(Files.newInputStream(serialFile)) ).use { input -> if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") return input.readObject() @@ -307,20 +302,20 @@ object Utils { @JvmStatic @JvmOverloads fun GenericMessageEvent.sendList( - list: List, - maxPerLine: Int, - separator: String = " ", - isBold: Boolean = false, - isIndent: Boolean = false + list: List, + maxPerLine: Int, + separator: String = " ", + isBold: Boolean = false, + isIndent: Boolean = false ) { var i = 0 while (i < list.size) { sendMessage( - helpFormat( - list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), - isBold, - isIndent - ), + helpFormat( + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), + isBold, + isIndent + ), ) i += maxPerLine } @@ -419,8 +414,8 @@ object Utils { fun URL.reader(): UrlReaderResponse { val connection = this.openConnection() as HttpURLConnection connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" ) return if (connection.responseCode.isHttpSuccess()) { UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index a696fa8..88109e8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -50,15 +50,15 @@ class Ignore : AbstractCommand() { override val name = IGNORE_CMD override val help = listOf( - "To ignore a link posted to the channel:", - helpFormat("https://www.foo.bar %n"), - "To check your ignore status:", - helpFormat("%c $name"), - "To toggle your ignore status:", - helpFormat("%c $name $me") + "To ignore a link posted to the channel:", + helpFormat("https://www.foo.bar %n"), + "To check your ignore status:", + helpFormat("%c $name"), + "To toggle your ignore status:", + helpFormat("%c $name $me") ) private val helpOp = help.plus( - arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name [ ...]")) + arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name [ ...]")) ) override val isOpOnly = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index ed0b6ef..7eb3bdb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -48,8 +48,8 @@ import kotlin.time.toDuration class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", - "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", + "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" ) override val name = "info" override val help = listOf("To view information about the bot:", helpFormat("%c $name")) @@ -104,9 +104,9 @@ class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { event.sendList(allVersions, 1) val info = StringBuilder() info.append("Uptime: ") - .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) - .append(" [Entries: ") - .append(LinksManager.entries.links.size) + .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) + .append(" [Entries: ") + .append(LinksManager.entries.links.size) if (seen.isEnabled()) { info.append(", Seen: ").append(seen.count()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 20a6635..48ff38f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -39,8 +39,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Msg : AbstractCommand() { override val name = "msg" override val help = listOf( - "To have the bot send a private message to someone:", - helpFormat("%c $name ") + "To have the bot send a private message to someone:", + helpFormat("%c $name ") ) override val isOpOnly = true override val isPublic = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 77154c7..66e721e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -41,8 +41,8 @@ import java.time.LocalDateTime class Recap : AbstractCommand() { override val name = "recap" override val help = listOf( - "To list the last 10 public channel messages:", - helpFormat("%c $name") + "To list the last 10 public channel messages:", + helpFormat("%c $name") ) override val isOpOnly = false override val isPublic = true @@ -60,8 +60,8 @@ class Recap : AbstractCommand() { @JvmStatic fun storeRecap(sender: String, message: String, isAction: Boolean) { recaps.add( - LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() - + " - $sender" + (if (isAction) " " else ": ") + message + LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + + " - $sender" + (if (isAction) " " else ": ") + message ) if (recaps.size > MAX_RECAPS) { recaps.removeFirst() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 896c569..f920891 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -40,10 +40,10 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Versions : AbstractCommand() { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", - "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + - ", JVM ${System.getProperty("java.runtime.version")}", - "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + + ", JVM ${System.getProperty("java.runtime.version")}", + "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" ) override val name = "versions" override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 1443d44..9fe250d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -45,13 +45,13 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Comment : AbstractCommand() { override val name = COMMAND override val help = listOf( - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, use its label: ", - helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), - "To delete a comment, use its label and a minus sign: ", - helpFormat("${Constants.LINK_CMD}1.1:-") + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, use its label: ", + helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), + "To delete a comment, use its label and a minus sign: ", + helpFormat("${Constants.LINK_CMD}1.1:-") ) override val isOpOnly = false override val isPublic = true @@ -100,12 +100,12 @@ class Comment : AbstractCommand() { } private fun changeAuthor( - channel: String, - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) && cmd.length > 1) { val comment = entry.getComment(commentIndex) @@ -118,11 +118,11 @@ class Comment : AbstractCommand() { } private fun deleteComment( - channel: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) @@ -134,11 +134,11 @@ class Comment : AbstractCommand() { } private fun setComment( - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { entry.setComment(commentIndex, cmd, event.user.nick) event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fba6b99..fb1a634 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -161,8 +161,8 @@ class LinksManager : AbstractCommand() { internal fun fetchTitle(link: String): String { try { val html = Jsoup.connect(link) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") - .get() + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() val title = html.title() if (title.isNotBlank()) { return title @@ -178,7 +178,7 @@ class LinksManager : AbstractCommand() { return try { val match = entries.links.single { it.link == link } event.sendMessage( - "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) + "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) ) true } catch (ignore: NoSuchElementException) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index ff4278d..e04cd15 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -47,16 +47,16 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Posting : AbstractCommand() { override val name = "posting" override val help = listOf( - "Post a URL, by saying it on a line on its own:", - helpFormat(" [] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", - "To add a title, use its label and a pipe:", - helpFormat("${Constants.LINK_CMD}1:|This is the title"), - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, see: ", - helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + "Post a URL, by saying it on a line on its own:", + helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", + "To add a title, use its label and a pipe:", + helpFormat("${Constants.LINK_CMD}1:|This is the title"), + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, see: ", + helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1662857..9071059 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -44,8 +44,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Tags : AbstractCommand() { override val name = COMMAND override val help = listOf( - "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", - helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") + "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", + helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") ) override val isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 825e374..ea1ebf8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -46,8 +46,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class View : AbstractCommand() { override val name = VIEW_CMD override val help = listOf( - "To list or search the current URL posts:", - helpFormat("%c $name [<start>] [<query>]") + "To list or search the current URL posts:", + helpFormat("%c $name [<start>] [<query>]") ) override val isOpOnly = false override val isPublic = true @@ -107,9 +107,9 @@ class View : AbstractCommand() { if (sent == MAX_ENTRIES && index < entries.links.size) { event.sendMessage("To view more, try: ") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index c9ee0f3..05ad330 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -58,7 +58,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { override val name = "seen" override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) private val helpOp = help.plus( - arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) ) override val isOpOnly = false override val isPublic = true @@ -130,12 +130,12 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") seenNicks.putAll( - loadSerialData( - serialObject, - TreeMap<String, SeenNick>(), - logger, - "seen nicknames" - ) as TreeMap<String, SeenNick> + loadSerialData( + serialObject, + TreeMap<String, SeenNick>(), + logger, + "seen nicknames" + ) as TreeMap<String, SeenNick> ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index e073184..96800bb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -66,11 +66,11 @@ class Tell(private val serialObject: String) : AbstractCommand() { override val name = "tell" override val help = listOf( - "To send a message to someone when they join the channel:", - helpFormat("%c $name <nick> <message>"), - "To view queued and sent messages:", - helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' + "To send a message to someone when they join the channel:", + helpFormat("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpFormat("%c $name ${View.VIEW_CMD}"), + "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' ) override val isOpOnly: Boolean = false override val isPublic: Boolean = isEnabled() @@ -118,9 +118,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { if (messages.removeIf { - it.id == id && - (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) - }) { + it.id == id && + (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) + }) { save() event.sendMessage("The message was deleted from the queue.") } else { @@ -180,7 +180,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (message.sender == nickname) { if (event !is MessageEvent) { event.user.send().message( - "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" + "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" ) message.isReceived = true message.isNotified = true @@ -188,17 +188,17 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { event.user.send().message( - "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" + "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" ) message.isReceived = true save() } } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified + && !message.isNotified ) { event.user.send().message( - "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " - + "${message.recipient.bold()} on ${message.receptionDate}" + "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " + + "${message.recipient.bold()} on ${message.receptionDate}" ) message.isNotified = true save() @@ -219,8 +219,8 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (messages.isNotEmpty()) { for (message in messages) { event.sendMessage( - "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + - (if (message.isReceived) "DELIVERED]" else "QUEUED]") + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + + (if (message.isReceived) "DELIVERED]" else "QUEUED]") ) } } else { @@ -238,13 +238,13 @@ class Tell(private val serialObject: String) : AbstractCommand() { } if (message.isReceived) { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" ) } else { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" ) } event.sendMessage(helpFormat(message.message)) @@ -254,9 +254,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } else { event.sendMessage("To delete one or all delivered messages:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) - ) + helpFormat( + helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) + ) ) event.sendMessage(help.last()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 6d2f313..33bc1e9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -39,20 +39,20 @@ import java.time.format.DateTimeFormatter * Tell Message. */ class TellMessage( - /** - * Returns the message's sender. - */ - val sender: String, + /** + * Returns the message's sender. + */ + val sender: String, - /** - * Returns the message's recipient. - */ - val recipient: String, + /** + * Returns the message's recipient. + */ + val recipient: String, - /** - * Returns the message text. - */ - val message: String + /** + * Returns the message text. + */ + val message: String ) : Serializable { /** * Returns the queued date/time. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index e8676ec..ba22746 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.entries import net.thauvin.erik.mobibot.Utils.today class Entries( - var channel: String = "", - var ircServer: String = "", - var logsDir: String = "", - var backlogs: String = "" + var channel: String = "", + var ircServer: String = "", + var logsDir: String = "", + var backlogs: String = "" ) { val links = mutableListOf<EntryLink>() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 9c09626..ff1e423 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -43,7 +43,7 @@ object EntriesUtils { */ @JvmStatic fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** * Prints an entry's link for display on the channel. @@ -52,7 +52,7 @@ object EntriesUtils { @JvmOverloads fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") - .append('[').append(entry.nick).append(']') + .append('[').append(entry.nick).append(']') if (isView && entry.comments.isNotEmpty()) { buff.append("[+").append(entry.comments.size).append(']') } @@ -73,7 +73,7 @@ object EntriesUtils { */ @JvmStatic fun printTags(entryIndex: Int, entry: EntryLink): String = - entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") /** * Builds link label based on its index. e.g: L1 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index fc61d18..80ca536 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -34,47 +34,46 @@ import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import net.thauvin.erik.mobibot.commands.links.LinksManager import java.io.Serializable -import java.util.Calendar -import java.util.Date +import java.util.* /** * The class used to store link entries. */ class EntryLink( - // Link's comments - val comments: MutableList<EntryComment> = mutableListOf(), + // Link's comments + val comments: MutableList<EntryComment> = mutableListOf(), - // Tags/categories - val tags: MutableList<SyndCategory> = mutableListOf(), + // Tags/categories + val tags: MutableList<SyndCategory> = mutableListOf(), - // Channel - var channel: String, + // Channel + var channel: String, - // Creation date - var date: Date = Calendar.getInstance().time, + // Creation date + var date: Date = Calendar.getInstance().time, - // Link's URL - var link: String, + // Link's URL + var link: String, - // Author's login - var login: String = "", + // Author's login + var login: String = "", - // Author's nickname - var nick: String, + // Author's nickname + var nick: String, - // Link's title - var title: String + // Link's title + var title: String ) : Serializable { /** * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - login: String, - channel: String, - tags: List<String?> + link: String, + title: String, + nick: String, + login: String, + channel: String, + tags: List<String?> ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { setTags(tags) } @@ -83,12 +82,12 @@ class EntryLink( * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - channel: String, - date: Date, - tags: List<SyndCategory> + link: String, + title: String, + nick: String, + channel: String, + date: Date, + tags: List<SyndCategory> ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { this.tags.addAll(tags) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index bb3838a..a30ba24 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -30,11 +30,7 @@ */ package net.thauvin.erik.mobibot.entries -import com.rometools.rome.feed.synd.SyndContentImpl -import com.rometools.rome.feed.synd.SyndEntry -import com.rometools.rome.feed.synd.SyndEntryImpl -import com.rometools.rome.feed.synd.SyndFeed -import com.rometools.rome.feed.synd.SyndFeedImpl +import com.rometools.rome.feed.synd.* import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.SyndFeedOutput @@ -48,7 +44,7 @@ import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths -import java.util.Calendar +import java.util.* import kotlin.io.path.exists /** @@ -76,7 +72,7 @@ class FeedsManager private constructor() { if (xml.exists()) { val input = SyndFeedInput() InputStreamReader( - Files.newInputStream(xml), StandardCharsets.UTF_8 + Files.newInputStream(xml), StandardCharsets.UTF_8 ).use { reader -> val feed = input.build(reader) pubDate = feed.publishedDate.toIsoLocalDate() @@ -85,12 +81,12 @@ class FeedsManager private constructor() { for (i in items.indices.reversed()) { with(items[i]) { entry = EntryLink( - link, - title, - author.substring(author.lastIndexOf('(') + 1, author.length - 1), - entries.channel, - publishedDate, - categories + link, + title, + author.substring(author.lastIndexOf('(') + 1, author.length - 1), + entries.channel, + publishedDate, + categories ) var split: List<String> for (comment in description.value.split("<br/>")) { @@ -123,7 +119,7 @@ class FeedsManager private constructor() { val items: MutableList<SyndEntry> = mutableListOf() var item: SyndEntry OutputStreamWriter( - Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 + Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 ).use { fw -> with(rss) { feedType = "rss_2.0" @@ -138,13 +134,13 @@ class FeedsManager private constructor() { with(entries.links[i]) { buff.setLength(0) buff.append("Posted by <b>") - .append(nick) - .append("</b> on <a href=\"irc://") - .append(entries.ircServer).append('/') - .append(channel) - .append("\"><b>") - .append(channel) - .append("</b></a>") + .append(nick) + .append("</b> on <a href=\"irc://") + .append(entries.ircServer).append('/') + .append(channel) + .append("\"><b>") + .append(channel) + .append("</b></a>") if (comments.size > 0) { buff.append(" <br/><br/>") for (j in comments.indices) { @@ -169,11 +165,11 @@ class FeedsManager private constructor() { output.output(rss, fw) } OutputStreamWriter( - Files.newOutputStream( - Paths.get( - entries.logsDir + today() + dotXml - ) - ), StandardCharsets.UTF_8 + Files.newOutputStream( + Paths.get( + entries.logsDir + today() + dotXml + ) + ), StandardCharsets.UTF_8 ).use { fw -> output.output(rss, fw) } } catch (e: FeedException) { if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index c1f660e..e1e86df 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -55,8 +55,10 @@ class ChatGpt : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[API_KEY_PROP], - properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()) + val answer = chat( + args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() + ) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) } else { @@ -105,13 +107,13 @@ class ChatGpt : AbstractModule() { if (!apiKey.isNullOrEmpty()) { val prompt = JSONWriter.valueToString("Q:$query\nA:") val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST( - HttpRequest.BodyPublishers.ofString( - """{ + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) + .POST( + HttpRequest.BodyPublishers.ofString( + """{ "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, @@ -120,9 +122,9 @@ class ChatGpt : AbstractModule() { "frequency_penalty": 0, "presence_penalty": 0 }""".trimIndent() + ) ) - ) - .build() + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { @@ -132,16 +134,16 @@ class ChatGpt : AbstractModule() { return choices.getJSONObject(0).getString("text").trim() } catch (e: JSONException) { throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { if (response.statusCode() == 429) { throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." ) } else { throw IOException("HTTP Status Code: " + response.statusCode()) @@ -149,9 +151,9 @@ class ChatGpt : AbstractModule() { } } catch (e: IOException) { throw ModuleException( - "$CHATGPT_CMD($query): IO", - "An IO error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index d14056e..5136504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -134,9 +134,9 @@ class CryptoPrices : AbstractModule() { } } catch (e: CryptoException) { throw ModuleException( - "loadCurrencies(): CE", - "An error has occurred while retrieving the currencies table.", - e + "loadCurrencies(): CE", + "An error has occurred while retrieving the currencies table.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index d41e7a1..0bf9d7a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -45,7 +45,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL -import java.util.TreeMap +import java.util.* /** @@ -99,15 +99,15 @@ class CurrencyConverter : AbstractModule() { event.sendMessage("To convert from one currency to another:") event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) + ) ) event.sendMessage("To list the supported currency codes:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) + ) ) } return true @@ -146,7 +146,7 @@ class CurrencyConverter : AbstractModule() { if (json.getBoolean("success")) { PublicMessage( - "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}" + "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}" ) } else { ErrorMessage("Sorry, an error occurred while converting the currencies.") @@ -178,9 +178,9 @@ class CurrencyConverter : AbstractModule() { } } catch (e: IOException) { throw ModuleException( - "loadCodes(): IOE", - "An IO error has occurred while retrieving the currencies.", - e + "loadCodes(): IOE", + "An IO error has occurred while retrieving the currencies.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index f426d1e..b0e911c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -65,10 +65,10 @@ class GoogleSearch : AbstractModule() { if (args.isNotBlank()) { try { val results = searchGoogle( - args, - properties[API_KEY_PROP], - properties[CSE_KEY_PROP], - event.user.nick + args, + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], + event.user.nick ) for (msg in results) { if (msg.isError) { @@ -104,23 +104,23 @@ class GoogleSearch : AbstractModule() { @JvmStatic @Throws(ModuleException::class) fun searchGoogle( - query: String, - apiKey: String?, - cseKey: String?, - quotaUser: String = ReleaseInfo.PROJECT + query: String, + apiKey: String?, + cseKey: String?, + quotaUser: String = ReleaseInfo.PROJECT ): List<Message> { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { throw ModuleException( - "${GoogleSearch::class.java.name} is disabled.", - "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." + "${GoogleSearch::class.java.name} is disabled.", + "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." ) } val results = mutableListOf<Message>() if (query.isNotBlank()) { try { val url = URL( - "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + + ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" ) val json = JSONObject(url.reader().body) if (json.has("items")) { @@ -141,9 +141,9 @@ class GoogleSearch : AbstractModule() { throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) } catch (e: JSONException) { throw ModuleException( - "searchGoogle($query): JSON", - "A JSON error has occurred searching Google.", - e + "searchGoogle($query): JSON", + "A JSON error has occurred searching Google.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 9ab2ead..fc85226 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -55,9 +55,9 @@ class Lookup : AbstractModule() { event.respondWith(nslookup(args).prependIndent()) } catch (ignore: UnknownHostException) { if (args.matches( - ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") - .toRegex() - ) + ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + .toRegex() + ) ) { try { val lines = whois(args) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 3be3a5f..4cf2fe9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -65,7 +65,7 @@ class Mastodon : SocialModule() { private fun formatTags(entry: EntryLink): String { return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } - .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } + .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } } /** @@ -74,11 +74,11 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) override fun post(message: String, isDm: Boolean): String { return toot( - apiKey = properties[ACCESS_TOKEN_PROP], - instance = properties[INSTANCE_PROP], - handle = handle, - message = message, - isDm = isDm + apiKey = properties[ACCESS_TOKEN_PROP], + instance = properties[INSTANCE_PROP], + handle = handle, + message = message, + isDm = isDm ) } @@ -99,21 +99,21 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { val request = HttpRequest.newBuilder() - .uri(URI.create("https://$instance/api/v1/statuses")) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .POST( - HttpRequest.BodyPublishers.ofString( - JSONWriter.valueToString( - if (isDm) { - mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") - } else { - mapOf("status" to message) - } - ) + .uri(URI.create("https://$instance/api/v1/statuses")) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .POST( + HttpRequest.BodyPublishers.ofString( + JSONWriter.valueToString( + if (isDm) { + mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + } else { + mapOf("status" to message) + } + ) + ) ) - ) - .build() + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index a569d21..017efd4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -34,9 +34,9 @@ package net.thauvin.erik.mobibot.modules * The `ModuleException` class. */ class ModuleException @JvmOverloads constructor( - val debugMessage: String, - message: String? = null, - cause: Throwable? = null + val debugMessage: String, + message: String? = null, + cause: Throwable? = null ) : Exception(message, cause) { companion object { private const val serialVersionUID = 1L diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 944dbc1..de5c1e8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -50,18 +50,18 @@ class Ping : AbstractModule() { */ @JvmField val PINGS = listOf( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!" + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!" ) @JvmStatic diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index d698888..359956a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -52,10 +52,10 @@ class RockPaperScissors : AbstractModule() { with(help) { add("To play Rock Paper Scissors:") add( - helpFormat( - "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" - + " | ${Hands.SCISSORS.name.lowercase()}" - ) + helpFormat( + "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" + ) ) } } @@ -96,7 +96,7 @@ class RockPaperScissors : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val hand = Hands.valueOf(cmd.uppercase()) - val botHand = Hands.values()[(0..Hands.values().size).random()] + val botHand = Hands.entries[(0..Hands.entries.size).random()] when { hand == botHand -> { event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index dcae5e7..661a4e8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -132,8 +132,8 @@ class StockQuote : AbstractModule() { fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${StockQuote::class.java.name} is disabled.", - "${STOCK_CMD.capitalise()} is disabled. The API key is missing." + "${StockQuote::class.java.name} is disabled.", + "${STOCK_CMD.capitalise()} is disabled. The API key is missing." ) } val messages = mutableListOf<Message>() @@ -144,8 +144,8 @@ class StockQuote : AbstractModule() { with(messages) { // Search for symbol/keywords response = URL( - "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body var json = getJsonResponse(response, debugMessage) val symbols = json.getJSONArray("bestMatches") @@ -156,9 +156,9 @@ class StockQuote : AbstractModule() { // Get quote for symbol response = URL( - "${API_URL}GLOBAL_QUOTE&symbol=" - + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}GLOBAL_QUOTE&symbol=" + + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body json = getJsonResponse(response, debugMessage) val quote = json.getJSONObject("Global Quote") @@ -167,50 +167,50 @@ class StockQuote : AbstractModule() { } else { add( - PublicMessage( - "Symbol: " + quote.getString("01. symbol").unescapeXml() - + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' - ) + PublicMessage( + "Symbol: " + quote.getString("01. symbol").unescapeXml() + + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' + ) ) val pad = 10 add( - PublicMessage( - "Price:".padEnd(pad).prependIndent() - + quote.getString("05. price").unescapeXml() - ) + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + quote.getString("05. price").unescapeXml() + ) ) add( - PublicMessage( - "Previous:".padEnd(pad).prependIndent() - + quote.getString("08. previous close").unescapeXml() - ) + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + quote.getString("08. previous close").unescapeXml() + ) ) val data = arrayOf( - "Open" to "02. open", - "High" to "03. high", - "Low" to "04. low", - "Volume" to "06. volume", - "Latest" to "07. latest trading day" + "Open" to "02. open", + "High" to "03. high", + "Low" to "04. low", + "Volume" to "06. volume", + "Latest" to "07. latest trading day" ) data.forEach { add( - NoticeMessage( - "${it.first}:".padEnd(pad).prependIndent() - + quote.getString(it.second).unescapeXml() - ) + NoticeMessage( + "${it.first}:".padEnd(pad).prependIndent() + + quote.getString(it.second).unescapeXml() + ) ) } add( - NoticeMessage( - "Change:".padEnd(pad).prependIndent() - + quote.getString("09. change").unescapeXml() - + " [" + quote.getString("10. change percent").unescapeXml() + ']' - ) + NoticeMessage( + "Change:".padEnd(pad).prependIndent() + + quote.getString("09. change").unescapeXml() + + " [" + quote.getString("10. change percent").unescapeXml() + ']' + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 567728e..533cce6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -104,7 +104,7 @@ class Weather2 : AbstractModule() { * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. */ fun getCountry(countryCode: String): Country { - for (c in Country.values()) { + for (c in Country.entries) { if (c.value.equals(countryCode, ignoreCase = true)) { return c } @@ -120,8 +120,8 @@ class Weather2 : AbstractModule() { fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Weather2::class.java.name} is disabled.", - "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." + "${Weather2::class.java.name} is disabled.", + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." ) } val owm = OWM(apiKey) @@ -145,10 +145,10 @@ class Weather2 : AbstractModule() { } if (cwd.hasCityName()) { messages.add( - PublicMessage( - "City: ${cwd.cityName}, " + - country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" - ) + PublicMessage( + "City: ${cwd.cityName}, " + + country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" + ) ) cwd.mainData?.let { with(it) { @@ -181,8 +181,8 @@ class Weather2 : AbstractModule() { for (w in it) { w?.let { condition.append(' ') - .append(w.getDescription().capitalise()) - .append('.') + .append(w.getDescription().capitalise()) + .append('.') } } messages.add(NoticeMessage(condition.toString())) @@ -192,15 +192,15 @@ class Weather2 : AbstractModule() { cwd.cityId?.let { if (it > 0) { messages.add( - NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) + NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) ) } else { messages.add( - NoticeMessage( - "https://openweathermap.org/find?q=" - + "$city,${code.uppercase()}".encodeUrl(), - Colors.GREEN - ) + NoticeMessage( + "https://openweathermap.org/find?q=" + + "$city,${code.uppercase()}".encodeUrl(), + Colors.GREEN + ) ) } } @@ -209,9 +209,9 @@ class Weather2 : AbstractModule() { } catch (e: APIException) { if (e.code == 404) { throw ModuleException( - "getWeather($query): API ${e.code}", - "The requested city was not found.", - e + "getWeather($query): API ${e.code}", + "The requested city was not found.", + e ) } else { throw ModuleException("getWeather($query): API ${e.code}", e.message, e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index a72efab..049807a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -60,15 +60,15 @@ class WolframAlpha : AbstractModule() { try { val query = args.trim().split("units=", limit = 2, ignoreCase = true) event.sendMessage( - queryWolfram( - query[0].trim(), - units = if (query.size == 2) { - getUnits(query[1].trim()) - } else { - getUnits(properties[UNITS_PROP]) - }, - appId = properties[APPID_KEY_PROP] - ) + queryWolfram( + query[0].trim(), + units = if (query.size == 2) { + getUnits(query[1].trim()) + } else { + getUnits(properties[UNITS_PROP]) + }, + appId = properties[APPID_KEY_PROP] + ) ) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) @@ -111,15 +111,15 @@ class WolframAlpha : AbstractModule() { return urlReader.body } else { throw ModuleException( - "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", - urlReader.body.ifEmpty { - "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" - } + "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", + urlReader.body.ifEmpty { + "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" + } ) } } catch (ioe: IOException) { throw ModuleException( - "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe + "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 18072bc..debbe98 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -322,7 +322,7 @@ class WorldTime : AbstractModule() { put("ZULU", "Zulu") put("ZW", "Africa/Harare") ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } - .forEach { tz -> put(tz, tz) } + .forEach { tz -> put(tz, tz) } } // The Time command @@ -336,7 +336,7 @@ class WorldTime : AbstractModule() { // Date/Time Format private var dtf = - DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") + DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") /** * Returns the current Internet (beat) Time. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 0607936..2695a3b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg * The `ErrorMessage` class. */ class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isError = true) + Message(msg, color, isError = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 23a33b9..3b4be49 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -36,11 +36,11 @@ import net.thauvin.erik.semver.Constants * The `Message` class. */ open class Message @JvmOverloads constructor( - var msg: String, - var color: String = DEFAULT_COLOR, - var isNotice: Boolean = false, - isError: Boolean = false, - var isPrivate: Boolean = false + var msg: String, + var color: String = DEFAULT_COLOR, + var isNotice: Boolean = false, + isError: Boolean = false, + var isPrivate: Boolean = false ) { companion object { var DEFAULT_COLOR = Constants.EMPTY diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 037d504..cd6721c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -34,5 +34,5 @@ package net.thauvin.erik.mobibot.msg * The `NoticeMessage` class. */ class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isNotice = true) + Message(msg, color, isNotice = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 842fee5..3033d1a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -33,6 +33,5 @@ package net.thauvin.erik.mobibot.msg /** * The `PrivateMessage` class. */ -@Suppress("unused") class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isPrivate = true) + Message(msg, color, isPrivate = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index cbc1936..91f2dd9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -36,7 +36,7 @@ import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.util.Timer +import java.util.* /** * Social Manager. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index b594670..32e670a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -76,8 +76,8 @@ abstract class SocialModule : AbstractModule() { post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn( - "Failed to post entry ${index.toLinkLabel()} on $name.", - e + "Failed to post entry ${index.toLinkLabel()} on $name.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 267a59d..3fd315e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -31,7 +31,7 @@ package net.thauvin.erik.mobibot.social -import java.util.TimerTask +import java.util.* class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() { override fun run() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index ebc2aa0..5a8a638 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -41,13 +41,9 @@ import net.thauvin.erik.mobibot.commands.Die import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.View -import net.thauvin.erik.mobibot.modules.Dice -import net.thauvin.erik.mobibot.modules.Joke -import net.thauvin.erik.mobibot.modules.Lookup -import net.thauvin.erik.mobibot.modules.RockPaperScissors -import net.thauvin.erik.mobibot.modules.War +import net.thauvin.erik.mobibot.modules.* import org.testng.annotations.Test -import java.util.Properties +import java.util.* class AddonsTest { private val p = Properties().apply { @@ -80,11 +76,11 @@ class AddonsTest { assertThat(addons.names.ops, "names.ops").containsExactly("cycle") assertThat(addons.names.commands, "names.command").containsExactly( - "joke", - "rock", - "paper", - "scissors", - "ignore" + "joke", + "rock", + "paper", + "scissors", + "ignore" ) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index a3994ec..be2deb3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -46,9 +46,9 @@ object ExceptionSanitizer { with(this) { if (!cause?.message.isNullOrBlank()) { return ModuleException( - debugMessage, - cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), - this + debugMessage, + cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), + this ) } else if (!message.isNullOrBlank()) { return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index d30977e..7611ae3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -68,6 +68,6 @@ class FeedReaderTest { assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) assertFailure { readFeed("https://www.examplesfoo.com/") } - .isInstanceOf(UnknownHostException::class.java) + .isInstanceOf(UnknownHostException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index e4af75a..1384a72 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -36,7 +36,7 @@ import java.net.InetAddress import java.net.UnknownHostException import java.nio.file.Files import java.nio.file.Paths -import java.util.Properties +import java.util.* /** * Access to `local.properties`. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 87617e8..4ebb53c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -68,7 +68,7 @@ class PinboardTest : LocalProperties() { private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { val response = - URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body matches.forEach { if (!response.contains(it)) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index ef0eaaf..8ddb013 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -66,15 +66,14 @@ import java.io.File import java.io.IOException import java.net.URL import java.time.LocalDateTime -import java.util.Calendar -import java.util.Properties +import java.util.* /** * The `Utils Test` class. */ class UtilsTest { private val ascii = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" private val cal = Calendar.getInstance() private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) private val test = "This is a test." @@ -90,7 +89,7 @@ class UtilsTest { val sep = '/' val url = "https://erik.thauvin.net" assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") - .isEqualTo(dir + File.separatorChar) + .isEqualTo(dir + File.separatorChar) assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") } @@ -116,24 +115,24 @@ class UtilsTest { fun textCapitaliseWords() { assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") assertThat("Already Capitalized".capitalizeWords(), "already capitalized") - .isEqualTo("Already Capitalized") + .isEqualTo("Already Capitalized") assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") } @Test fun testColorize() { assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( - Colors.REVERSE + ascii + Colors.REVERSE + Colors.REVERSE + ascii + Colors.REVERSE ) assertThat(ascii.colorize(Colors.RED), "red.colorize()") - .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") - .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") - .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) + .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) } @Test @@ -165,19 +164,19 @@ class UtilsTest { fun testHelpCmdSyntax() { val bot = "mobibot" assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") - .isEqualTo("$bot: $test $bot $test") + .isEqualTo("$bot: $test $bot $test") assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") - .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") } @Test fun testHelpFormat() { assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") - .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") - .isEqualTo(test.prependIndent()) + .isEqualTo(test.prependIndent()) assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") - .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) + .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) } @@ -219,15 +218,15 @@ class UtilsTest { val search = arrayOf("one", "two", "three") val replace = arrayOf("1", "2", "3") assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") - .isEqualTo(replace.joinToString(",")) + .isEqualTo(replace.joinToString(",")) assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") - .isEqualTo(test.replace("t", "").replace("e", "E")) + .isEqualTo(test.replace("t", "").replace("e", "E")) assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") - .isEqualTo(test) + .isEqualTo(test) } @Test @@ -259,7 +258,7 @@ class UtilsTest { @Test fun testUnescapeXml() { assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( - "<a name=\"test & ''\">" + "<a name=\"test & ''\">" ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 265009b..1f28049 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -40,14 +40,14 @@ class InfoTest { @Test(groups = ["commands"]) fun testToUptime() { assertThat( - 547800300076L.toUptime(), - "upTime(full)" + 547800300076L.toUptime(), + "upTime(full)" ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") assertThat( - 1320300000L.toUptime(), - "upTime(weeks days hours minutes)" + 1320300000L.toUptime(), + "upTime(weeks days hours minutes)" ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index f1fbe11..5f1a690 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -48,13 +48,13 @@ class RecapTest { assertThat(Recap.recaps, "Recap.recaps").all { size().isEqualTo(Recap.MAX_RECAPS) prop(MutableList<String>::first) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) prop(MutableList<String>::last) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) } Recap.storeRecap("sender", "test action", true) assertThat(Recap.recaps.last()) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 8e49b5e..8fcbd8b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -47,8 +47,8 @@ class LinksManagerTest { fun fetchTitle() { assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") assertThat( - linksManager.fetchTitle("https://www.google.com/foo"), - "fetchTitle(Foo)" + linksManager.fetchTitle("https://www.google.com/foo"), + "fetchTitle(Foo)" ).isEqualTo(Constants.NO_TITLE) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index c28090d..0853a9d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -45,14 +45,14 @@ class ViewTest { for (i in 1..10) { LinksManager.entries.links.add( - EntryLink( - "https://www.example.com/$i", - "Example $i", - "nick$i", - "login$i", - "#channel", - emptyList() - ) + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 4298a16..52a21cc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.commands.seen import assertk.all import assertk.assertThat -import assertk.assertions.isEmpty -import assertk.assertions.isEqualTo -import assertk.assertions.isGreaterThan -import assertk.assertions.isNotEqualTo -import assertk.assertions.isNotNull -import assertk.assertions.key -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import org.testng.annotations.AfterClass import org.testng.annotations.BeforeClass import org.testng.annotations.Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index cff11f2..115e9fb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -33,13 +33,7 @@ package net.thauvin.erik.mobibot.commands.tell import assertk.all import assertk.assertThat -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFalse -import assertk.assertions.isGreaterThan -import assertk.assertions.isTrue -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import org.testng.annotations.AfterClass import org.testng.annotations.BeforeClass import org.testng.annotations.Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 6eef16e..a09ebb9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -46,14 +46,14 @@ class EntriesUtilsTest { private val links = buildList { for (i in 0..5) { add( - EntryLink( - "https://www.mobitopia.org/$i", - "Mobitopia$i", - "Skynx$i", - "JimH$i", - "#mobitopia$i", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) + EntryLink( + "https://www.mobitopia.org/$i", + "Mobitopia$i", + "Skynx$i", + "JimH$i", + "#mobitopia$i", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) ) } } @@ -67,7 +67,7 @@ class EntriesUtilsTest { fun printLinkTest() { for (i in links.indices) { assertThat( - printLink(i - 1, links[i]), "link $i" + printLink(i - 1, links[i]), "link $i" ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") } @@ -79,7 +79,7 @@ class EntriesUtilsTest { fun printTagsTest() { for (i in links.indices) { assertThat( - printTags(i - 1, links[i]), "tag $i" + printTags(i - 1, links[i]), "tag $i" ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index ab8c71c..4c20525 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -32,18 +32,12 @@ package net.thauvin.erik.mobibot.entries import assertk.all import assertk.assertThat -import assertk.assertions.index -import assertk.assertions.isEmpty -import assertk.assertions.isEqualTo -import assertk.assertions.isFalse -import assertk.assertions.isTrue -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import org.testng.annotations.Test import java.security.SecureRandom -import java.util.Date +import java.util.* /** * The `EntryUtilsTest` class. @@ -54,8 +48,8 @@ import java.util.Date */ class EntryLinkTest { private val entryLink = EntryLink( - "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") ) @Test(groups = ["entries"]) @@ -123,12 +117,12 @@ class EntryLinkTest { entryLink.setTags("+mobitopia") entryLink.setTags("-mobitopia") assertThat( - entryLink.formatTags(","), - "formatTags(',')" + entryLink.formatTags(","), + "formatTags(',')" ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") entryLink.setTags("-tag4 tag5") assertThat( - entryLink.formatTags(" ", ","), "formatTag(' ',',')" + entryLink.formatTags(" ", ","), "formatTag(' ',',')" ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") val size = entryLink.tags.size entryLink.setTags("") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index cd2ebb8..4223d9d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -33,18 +33,12 @@ package net.thauvin.erik.mobibot.entries import assertk.all import assertk.assertThat -import assertk.assertions.endsWith -import assertk.assertions.exists -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isTrue -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import net.thauvin.erik.mobibot.Utils.today import org.testng.annotations.BeforeSuite import org.testng.annotations.Test import java.nio.file.Paths -import java.util.Date +import java.util.* import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize import kotlin.io.path.name diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index b3bd248..2b1d3f9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.modules import assertk.assertFailure import assertk.assertThat import assertk.assertions.isEqualTo -import assertk.assertions.isFailure import assertk.assertions.isInstanceOf import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException import net.thauvin.erik.mobibot.Utils.bold diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index e4638b9..fa50fcb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -34,7 +34,6 @@ import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause -import assertk.assertions.isFailure import assertk.assertions.isInstanceOf import net.thauvin.erik.mobibot.LocalProperties import org.testng.annotations.Test @@ -43,21 +42,21 @@ class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } - .isInstanceOf(ModuleException::class.java) - .hasNoCause() + .isInstanceOf(ModuleException::class.java) + .hasNoCause() } @Test(groups = ["modules", "no-ci"]) fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") assertThat( - ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 8c1d745..10a2470 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -58,16 +58,16 @@ class CurrencyConverterTest { @Test(groups = ["modules"]) fun testConvertCurrency() { assertThat( - convertCurrency("100 USD to EUR").msg, - "convertCurrency(100 USD to EUR)" + convertCurrency("100 USD to EUR").msg, + "convertCurrency(100 USD to EUR)" ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex()) assertThat( - convertCurrency("1 USD to BTC").msg, - "convertCurrency(1 USD to BTC)" + convertCurrency("1 USD to BTC").msg, + "convertCurrency(1 USD to BTC)" ).matches("1 United States Dollar = 0\\.\\d+ Bitcoin".toRegex()) assertThat( - convertCurrency("100,000.00 GBP to BTC").msg, - "convertCurrency(100,000.00 GBP to BTC)" + convertCurrency("100,000.00 GBP to BTC").msg, + "convertCurrency(100,000.00 GBP to BTC)" ).matches("100,000.00 British Pound Sterling = \\d{1,2}\\.\\d+ Bitcoin".toRegex()) assertThat(convertCurrency("100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index cdc04f0..4225e3b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -42,12 +42,12 @@ class DiceTest { fun testRoll() { assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") assertThat(Dice.roll(2, 1), "roll(2d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") assertThat(Dice.roll(5, 1), "roll(5d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") assertThat(Dice.roll(2, 6), "roll(2d6)") - .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) + .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) assertThat(Dice.roll(3, 7), "roll(3d7)") - .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) + .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 175af47..640a721 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -33,15 +33,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertFailure import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.hasMessage -import assertk.assertions.hasNoCause -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFailure -import assertk.assertions.isInstanceOf -import assertk.assertions.isNotEmpty -import assertk.assertions.prop +import assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle @@ -56,19 +48,19 @@ class GoogleSearchTest : LocalProperties() { @Test(groups = ["modules"]) fun testAPIKeys() { assertThat( - searchGoogle("", "apikey", "cssKey").first(), - "searchGoogle(empty)" + searchGoogle("", "apikey", "cssKey").first(), + "searchGoogle(empty)" ).isInstanceOf(ErrorMessage::class.java) assertFailure { searchGoogle("test", "", "apiKey") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "cssKey") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("API key not valid. Please pass a valid API key.") + .isInstanceOf(ModuleException::class.java) + .hasMessage("API key not valid. Please pass a valid API key.") } @Test(groups = ["no-ci", "modules"]) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index fa063f4..55a7b8f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -32,12 +32,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertThat -import assertk.assertions.doesNotContain -import assertk.assertions.each -import assertk.assertions.isGreaterThan -import assertk.assertions.isInstanceOf -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 34f778a..84f9375 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -42,13 +42,13 @@ class MastodonTest : LocalProperties() { fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" assertThat( - toot( - getProperty(Mastodon.ACCESS_TOKEN_PROP), - getProperty(Mastodon.INSTANCE_PROP), - getProperty(Mastodon.HANDLE_PROP), - msg, - true - ) + toot( + getProperty(Mastodon.ACCESS_TOKEN_PROP), + getProperty(Mastodon.INSTANCE_PROP), + getProperty(Mastodon.HANDLE_PROP), + msg, + true + ) ).contains(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index c7dbfc0..b36285b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -32,13 +32,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.doesNotContain -import assertk.assertions.endsWith -import assertk.assertions.hasMessage -import assertk.assertions.isEqualTo -import assertk.assertions.isNotNull -import assertk.assertions.isNull +import assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import org.testng.annotations.DataProvider import org.testng.annotations.Test @@ -57,9 +51,9 @@ class ModuleExceptionTest { @DataProvider(name = "dp") fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> { return arrayOf( - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(debugMessage, message)) + arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), + arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(debugMessage, message)) ) } @@ -78,7 +72,7 @@ class ModuleExceptionTest { val apiKey = "1234567890" var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) assertThat( - e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" + e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" ).isNotNull().all { contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") doesNotContain(apiKey, "me") @@ -92,7 +86,7 @@ class ModuleExceptionTest { e = ModuleException(debugMessage, apiKey) assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() - .doesNotContain(apiKey) + .doesNotContain(apiKey) val msg: String? = null e = ModuleException(debugMessage, msg, IOException(msg)) @@ -100,8 +94,8 @@ class ModuleExceptionTest { e = ModuleException(debugMessage, msg, IOException("foo is $apiKey")) assertThat( - e.sanitize(" ", apiKey, "foo").message, - "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" + e.sanitize(" ", apiKey, "foo").message, + "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" ).isNotNull().all { doesNotContain(apiKey) endsWith("xxx is xxxxxxxxxx") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index b4a277e..17e5b92 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertFailure import assertk.assertThat -import assertk.assertions.hasNoCause -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFailure -import assertk.assertions.isInstanceOf -import assertk.assertions.isNotEmpty -import assertk.assertions.matches -import assertk.assertions.prop +import assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote @@ -67,7 +60,7 @@ class StockQuoteTest : LocalProperties() { assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) - .matches(buildMatch("Previous").toRegex()) + .matches(buildMatch("Previous").toRegex()) assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) symbol = "blahfoo" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index ca650bb..d7d65de 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -33,16 +33,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertFailure import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.endsWith -import assertk.assertions.hasNoCause -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFailure -import assertk.assertions.isInstanceOf -import assertk.assertions.isNotNull -import assertk.assertions.isTrue -import assertk.assertions.prop +import assertk.assertions.* import net.aksingh.owmjapis.api.APIException import net.aksingh.owmjapis.core.OWM import net.thauvin.erik.mobibot.LocalProperties @@ -69,7 +60,7 @@ class Weather2Test : LocalProperties() { assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) - val country = OWM.Country.values() + val country = OWM.Country.entries.toTypedArray() repeat(3) { val rand = country[(country.indices).random()] assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index ae1722d..281d8af 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -45,11 +45,11 @@ class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules"]) fun testAppId() { assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("Error 1: Invalid appid") + .isInstanceOf(ModuleException::class.java) + .hasMessage("Error 1: Invalid appid") assertFailure { queryWolfram("1 gallon to liter", appId = "") } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } @Test(groups = ["modules", "no-ci"]) @@ -62,8 +62,8 @@ class WolframAlphaTest : LocalProperties() { query = "SFO to LAX" assertThat( - queryWolfram(query, WolframAlpha.METRIC, apiKey), - "queryWolfram($query)" + queryWolfram(query, WolframAlpha.METRIC, apiKey), + "queryWolfram($query)" ).contains("kilometers") } catch (e: ModuleException) { // Avoid displaying api key in CI logs diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 46888e3..29f5589 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -30,10 +30,8 @@ */ package net.thauvin.erik.mobibot.modules -import assertk.assertFailure import assertk.assertThat import assertk.assertions.endsWith -import assertk.assertions.isSuccess import assertk.assertions.matches import assertk.assertions.startsWith import net.thauvin.erik.mobibot.Utils.bold @@ -51,9 +49,9 @@ class WordTimeTest { @Test(groups = ["modules"]) fun testTime() { assertThat(time(), "time()").matches( - ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + - "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + - "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() + ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + + "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + + "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() ) assertThat(time(""), "time()").endsWith("Los Angeles".bold()) assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) diff --git a/version.properties b/version.properties index b464c4e..b7cc5c2 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Jul 02 02:19:45 PDT 2023 -version.buildmeta=20230702021945 +#Thu Jul 06 10:21:40 PDT 2023 +version.buildmeta=20230706102140 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230702021945 +version.semver=0.8.0-rc+20230706102140 From c99debd740cb3f50ea742899efef82d4883a6ef8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 1 Sep 2023 15:29:45 -0700 Subject: [PATCH 721/842] Updated dependencies --- .gitignore | 1 - .idea/codeStyles/Project.xml | 35 ++++++++++++------ .idea/codeStyles/codeStyleConfig.xml | 1 + .idea/misc.xml | 1 - build.gradle | 18 ++++----- gradle.properties | 0 gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 4 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- version.properties | 6 +-- 12 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 gradle.properties diff --git a/.gitignore b/.gitignore index 2b959f1..970cefd 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,6 @@ dist/ ehthumbs.db fabric.properties gen/ -gradle.properties hs_err_pid* kobaltBuild kobaltw*-test diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 76b5425..10aa334 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,11 +1,17 @@ <component name="ProjectCodeStyleConfiguration"> - <code_scheme name="Project" version="173"> + <code_scheme name="Project" version="200"> <JetCodeStyleSettings> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </JetCodeStyleSettings> <codeStyleSettings language="JAVA"> <option name="IF_BRACE_FORCE" value="1" /> <arrangement> + <groups> + <group> + <type>OVERRIDDEN_METHODS</type> + <order>BY_NAME</order> + </group> + </groups> <rules> <section> <rule> @@ -17,6 +23,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -29,6 +36,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -41,6 +49,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -53,6 +62,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -64,6 +74,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -73,8 +84,10 @@ <FIELD>true</FIELD> <PROTECTED>true</PROTECTED> <STATIC>true</STATIC> + <visibility /> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -86,6 +99,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -97,6 +111,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -118,6 +133,7 @@ <PUBLIC>true</PUBLIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -129,6 +145,7 @@ <PROTECTED>true</PROTECTED> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -140,6 +157,7 @@ <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -151,6 +169,7 @@ <PRIVATE>true</PRIVATE> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -161,6 +180,7 @@ <PUBLIC>true</PUBLIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -171,6 +191,7 @@ <PROTECTED>true</PROTECTED> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -195,21 +216,12 @@ <order>BY_NAME</order> </rule> </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>false</PRIVATE> - </AND> - </match> - </rule> - </section> <section> <rule> <match> <FIELD>true</FIELD> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -234,6 +246,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index d91f848..23f4bb5 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ <component name="ProjectCodeStyleConfiguration"> <state> + <option name="USE_PER_PROJECT_SETTINGS" value="true" /> <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> </state> </component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index f648a66..e7a3cca 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="FrameworkDetectionExcludesConfiguration"> diff --git a/build.gradle b/build.gradle index fa00b54..caf1183 100644 --- a/build.gradle +++ b/build.gradle @@ -7,13 +7,13 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.47.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.23.0' + id 'io.gitlab.arturbosch.detekt' version '1.23.1' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.9.0' - id 'org.jetbrains.kotlin.kapt' version '1.9.0' - id 'org.jetbrains.kotlinx.kover' version '0.7.2' - id 'org.sonarqube' version '4.2.1.3168' + id 'org.jetbrains.kotlin.jvm' version '1.9.10' + id 'org.jetbrains.kotlin.kapt' version '1.9.10' + id 'org.jetbrains.kotlinx.kover' version '0.7.3' + id 'org.sonarqube' version '4.3.1.3277' id 'pmd' } @@ -54,20 +54,20 @@ dependencies { // implementation fileTree(dir: 'lib', include: '*.jar') // Commons (mostly for PircBotX) - implementation 'org.apache.commons:commons-lang3:3.12.0' + implementation 'org.apache.commons:commons-lang3:3.13.0' implementation 'org.apache.commons:commons-text:1.10.0' implementation 'commons-codec:commons-codec:1.16.0' implementation 'commons-net:commons-net:3.9.0' // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.1.1-jre' + implementation 'com.google.guava:guava:32.1.2-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2' - implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' + implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6' // Logging implementation 'org.slf4j:slf4j-api:2.0.7' diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 28216 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 delta 27891 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62f495d..ac72c34 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fcb6fca..0adc8e1 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index a0e8fd4..2760fa7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.jokeapi.exceptions.HttpErrorException import net.thauvin.erik.jokeapi.exceptions.JokeException -import net.thauvin.erik.jokeapi.getJoke +import net.thauvin.erik.jokeapi.joke import net.thauvin.erik.jokeapi.models.Type import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.colorize @@ -83,7 +83,7 @@ class Joke : AbstractModule() { @Throws(ModuleException::class) fun randomJoke(): List<Message> { return try { - val joke = getJoke(safe = true, type = Type.SINGLE, splitNewLine = true) + val joke = joke(safe = true, type = Type.SINGLE, splitNewLine = true) joke.joke.map { PublicMessage(it, Colors.CYAN) } } catch (e: JokeException) { throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index e3475f1..0547c95 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -72,7 +72,7 @@ class CryptoPricesTest { @Test(groups = ["modules"]) fun testGetCurrencyName() { - assertThat(getCurrencyName("USD"), "USD").isEqualTo("US Dollar") + assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") } } diff --git a/version.properties b/version.properties index b7cc5c2..854597d 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jul 06 10:21:40 PDT 2023 -version.buildmeta=20230706102140 +#Fri Sep 01 15:28:31 PDT 2023 +version.buildmeta=20230901152831 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230706102140 +version.semver=0.8.0-rc+20230901152831 From 65fa55d51f20af2405692feb428171e2b6d99d5d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 22 Sep 2023 03:05:10 -0700 Subject: [PATCH 722/842] Updated dependencies --- .idea/kotlinc.xml | 2 +- build.gradle | 8 ++++---- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fdf8d99..f8467b4 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="KotlinJpsPluginSettings"> - <option name="version" value="1.9.0" /> + <option name="version" value="1.9.10" /> </component> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index caf1183..d5a1151 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.47.0' + id 'com.github.ben-manes.versions' version '0.48.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.23.1' id 'java' @@ -70,7 +70,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6' // Logging - implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'org.slf4j:slf4j-api:2.0.9' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" @@ -86,9 +86,9 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'net.thauvin.erik:urlencoder:1.3.0' + implementation 'net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0' - testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.26.1' + testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.27.0' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation "org.mockito:mockito-core:4.0.0" testImplementation 'org.testng:testng:7.8.0' diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 0595220..51adc88 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import net.thauvin.erik.urlencoder.UrlEncoder +import net.thauvin.erik.urlencoder.UrlEncoderUtil import org.jsoup.Jsoup import org.pircbotx.Colors import org.pircbotx.PircBotX @@ -42,6 +42,7 @@ import org.slf4j.Logger import java.io.* import java.net.HttpURLConnection import java.net.URL +import java.net.URLEncoder import java.nio.file.Files import java.nio.file.Paths import java.time.LocalDateTime @@ -145,7 +146,7 @@ object Utils { * URL encodes the given string. */ @JvmStatic - fun String.encodeUrl(): String = UrlEncoder.encode(this) + fun String.encodeUrl(): String = UrlEncoderUtil.encode(this) /** * Returns a property as an int. From 8fb872ad6fca07974e8143cdfc38895defcf1ee5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 22 Sep 2023 03:06:08 -0700 Subject: [PATCH 723/842] Switched to ExchangeRate-API --- config/detekt/baseline.xml | 3 +- properties/mobibot.properties | 6 ++ .../erik/mobibot/modules/CurrencyConverter.kt | 80 ++++++++++++------- .../mobibot/modules/CurrencyConverterTest.kt | 26 +++--- 4 files changed, 72 insertions(+), 43 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index f7cc151..9f157cf 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -59,7 +59,8 @@ <ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID> <ID>NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(query: String): Message</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?)</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>)</ID> <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index fa8ce50..24d9977 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -45,6 +45,12 @@ disabled-modules=mastodon # Automatically post links to Mastodon #mastodon-auto-post=true + +# +# Get Exchange Rate API key from: https://www.exchangerate-api.com/ +# +#exchangerate-api-key= + # # Create custom search engine at: https://programmablesearchengine.google.com/ # and get API key from: https://console.cloud.google.com/apis diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 0bf9d7a..284f9d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -44,6 +44,7 @@ import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException +import java.math.BigDecimal import java.net.URL import java.util.* @@ -57,10 +58,10 @@ class CurrencyConverter : AbstractModule() { override val name = "CurrencyConverter" // Reload currency codes - private fun reload() { - if (SYMBOLS.isEmpty()) { + private fun reload(apiKey: String?) { + if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) { try { - loadSymbols() + loadSymbols(apiKey) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) } @@ -71,12 +72,12 @@ class CurrencyConverter : AbstractModule() { * Converts the specified currencies. */ override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - reload() + reload(properties[API_KEY_PROP]) if (SYMBOLS.isEmpty()) { event.respond(EMPTY_SYMBOLS_TABLE) } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { - val msg = convertCurrency(args) + val msg = convertCurrency(properties[API_KEY_PROP], args) event.respond(msg.msg) if (msg.isError) { helpResponse(event) @@ -90,7 +91,7 @@ class CurrencyConverter : AbstractModule() { } override fun helpResponse(event: GenericMessageEvent): Boolean { - reload() + reload(properties[API_KEY_PROP]) if (SYMBOLS.isEmpty()) { event.sendMessage(EMPTY_SYMBOLS_TABLE) @@ -99,21 +100,26 @@ class CurrencyConverter : AbstractModule() { event.sendMessage("To convert from one currency to another:") event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) + ) ) event.sendMessage("To list the supported currency codes:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) + ) ) } return true } companion object { + /** + * The API Key property. + */ + const val API_KEY_PROP = "exchangerate-api-key" + // Currency command private const val CURRENCY_CMD = "currency" @@ -130,7 +136,11 @@ class CurrencyConverter : AbstractModule() { * Converts from a currency to another. */ @JvmStatic - fun convertCurrency(query: String): Message { + fun convertCurrency(apiKey: String?, query: String): Message { + if (apiKey.isNullOrEmpty()) { + throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.") + } + val cmds = query.split(" ") return if (cmds.size == 4) { if (cmds[3] == cmds[1] || "0" == cmds[0]) { @@ -141,12 +151,14 @@ class CurrencyConverter : AbstractModule() { if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) { try { val amt = cmds[0].replace(",", "") - val url = URL("https://api.exchangerate.host/convert?from=$to&to=$from&amount=$amt") - val json = JSONObject(url.reader().body) + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt") + val body = url.reader().body + val json = JSONObject(body) - if (json.getBoolean("success")) { + if (json.getString("result") == "success") { + val result = json.getDouble("conversion_result") PublicMessage( - "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}" + "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" ) } else { ErrorMessage("Sorry, an error occurred while converting the currencies.") @@ -158,7 +170,9 @@ class CurrencyConverter : AbstractModule() { ErrorMessage("Sounds like monopoly money to me!") } } - } else ErrorMessage("Invalid query. Let's try again.") + } else { + ErrorMessage("Invalid query. Let's try again.") + } } /** @@ -166,28 +180,32 @@ class CurrencyConverter : AbstractModule() { */ @JvmStatic @Throws(ModuleException::class) - fun loadSymbols() { - try { - val url = URL("https://api.exchangerate.host/symbols") - val json = JSONObject(url.reader().body) - if (json.getBoolean("success")) { - val symbols = json.getJSONObject("symbols") - for (key in symbols.keys()) { - SYMBOLS[key] = symbols.getJSONObject(key).getString("description") + fun loadSymbols(apiKey: String?) { + if (!apiKey.isNullOrEmpty()) { + try { + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes") + val json = JSONObject(url.reader().body) + if (json.getString("result") == "success") { + val codes = json.getJSONArray("supported_codes") + for (i in 0 until codes.length()) { + val code = codes.getJSONArray(i); + SYMBOLS[code.getString(0)] = code.getString(1); + } } - } - } catch (e: IOException) { - throw ModuleException( + } catch (e: IOException) { + throw ModuleException( "loadCodes(): IOE", "An IO error has occurred while retrieving the currencies.", e - ) + ) + } } } } init { commands.add(CURRENCY_CMD) - loadSymbols() + initProperties(API_KEY_PROP) + loadSymbols(properties[ChatGpt.API_KEY_PROP]) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 10a2470..59ea2e7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -36,6 +36,7 @@ import assertk.assertions.contains import assertk.assertions.isInstanceOf import assertk.assertions.matches import assertk.assertions.prop +import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols import net.thauvin.erik.mobibot.msg.ErrorMessage @@ -48,32 +49,35 @@ import org.testng.annotations.Test /** * The `CurrencyConvertTest` class. */ -class CurrencyConverterTest { +class CurrencyConverterTest: LocalProperties() { + @BeforeClass @Throws(ModuleException::class) fun before() { - loadSymbols() + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) + loadSymbols(apiKey) } @Test(groups = ["modules"]) fun testConvertCurrency() { + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) assertThat( - convertCurrency("100 USD to EUR").msg, + convertCurrency(apiKey,"100 USD to EUR").msg, "convertCurrency(100 USD to EUR)" ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex()) assertThat( - convertCurrency("1 USD to BTC").msg, - "convertCurrency(1 USD to BTC)" - ).matches("1 United States Dollar = 0\\.\\d+ Bitcoin".toRegex()) + convertCurrency(apiKey,"1 USD to GBP").msg, + "convertCurrency(1 USD to BGP)" + ).matches("1 United States Dollar = 0\\.\\d+ Pound Sterling".toRegex()) assertThat( - convertCurrency("100,000.00 GBP to BTC").msg, - "convertCurrency(100,000.00 GBP to BTC)" - ).matches("100,000.00 British Pound Sterling = \\d{1,2}\\.\\d+ Bitcoin".toRegex()) - assertThat(convertCurrency("100 USD to USD"), "convertCurrency(100 USD to USD)").all { + convertCurrency(apiKey,"100,000.00 CAD to USD").msg, + "convertCurrency(100,000.00 GBP to USD)" + ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d+ United States Dollar".toRegex()) + assertThat(convertCurrency(apiKey,"100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") isInstanceOf(PublicMessage::class.java) } - assertThat(convertCurrency("100 USD"), "convertCurrency(100 USD)").all { + assertThat(convertCurrency(apiKey,"100 USD"), "convertCurrency(100 USD)").all { prop(Message::msg).contains("Invalid query.") isInstanceOf(ErrorMessage::class.java) } From 8b6384627025b93d9dae9953006d48bbe4919573 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 22 Sep 2023 04:21:24 -0700 Subject: [PATCH 724/842] Added ExchangeRate-API enviornment variable --- .github/workflows/gradle.yml | 1 + version.properties | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c917819..aa034f9 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -47,6 +47,7 @@ jobs: MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} + EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} with: arguments: build check --stacktrace diff --git a/version.properties b/version.properties index 854597d..f272017 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 01 15:28:31 PDT 2023 -version.buildmeta=20230901152831 +#Fri Sep 22 04:02:33 PDT 2023 +version.buildmeta=20230922040233 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230901152831 +version.semver=0.8.0-rc+20230922040233 From bc8bf6bf57247a81a934df3a435063c17d95697e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 4 Oct 2023 08:55:48 -0700 Subject: [PATCH 725/842] Added date format to currency converter response. --- .idea/misc.xml | 2 ++ build.gradle | 13 +++++++------ .../erik/mobibot/modules/CurrencyConverter.kt | 13 ++++++++----- version.properties | 6 +++--- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index e7a3cca..67f5457 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="FrameworkDetectionExcludesConfiguration"> @@ -7,6 +8,7 @@ <option name="customRuleSets"> <list> <option value="K:\java\semver\config\pmd.xml" /> + <option value="$PROJECT_DIR$/../../java/bld-exec/config/pmd.xml" /> </list> </option> <option name="skipTestSources" value="false" /> diff --git a/build.gradle b/build.gradle index d5a1151..7e13746 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version '1.9.10' id 'org.jetbrains.kotlin.kapt' version '1.9.10' id 'org.jetbrains.kotlinx.kover' version '0.7.3' - id 'org.sonarqube' version '4.3.1.3277' + id 'org.sonarqube' version '4.4.1.3373' id 'pmd' } @@ -21,7 +21,7 @@ defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' -final def semverProcessor = "net.thauvin.erik:semver:1.2.0" +final def semverProcessor = "net.thauvin.erik:semver:1.2.1" final def isCI = (System.getenv('CI') != null) @@ -77,15 +77,16 @@ dependencies { implementation 'com.rometools:rome:2.1.0' implementation 'com.squareup.okhttp3:okhttp:4.11.0' + implementation 'com.squareup.okio:okio:3.6.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' implementation 'org.json:json:20230618' implementation 'org.jsoup:jsoup:1.16.1' // Thauvin - implementation 'net.thauvin.erik:cryptoprice:1.0.0' - implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' - implementation 'net.thauvin.erik:pinboard-poster:1.0.3' + implementation 'net.thauvin.erik:cryptoprice:1.0.1' + implementation 'net.thauvin.erik:jokeapi:0.9.0' + implementation 'net.thauvin.erik:pinboard-poster:1.1.0' implementation 'net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.27.0' @@ -211,7 +212,7 @@ sonarqube { property('sonar.organization', 'ethauvin-github') property('sonar.projectKey', 'ethauvin_mobibot') property('sonar.host.url', 'https://sonarcloud.io') - property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/report.xml") + property('sonar.coverage.jacoco.xmlReportPaths', "${layout.buildDirectory.get()}/reports/kover/report.xml") } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 284f9d2..fc56d42 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -44,8 +44,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException -import java.math.BigDecimal import java.net.URL +import java.text.DecimalFormat import java.util.* @@ -101,7 +101,7 @@ class CurrencyConverter : AbstractModule() { event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage( helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled) ) ) event.sendMessage("To list the supported currency codes:") @@ -132,6 +132,9 @@ class CurrencyConverter : AbstractModule() { // Currency symbols private val SYMBOLS: TreeMap<String, String> = TreeMap() + // Decimal format + private val DECIMAL_FORMAT = DecimalFormat("0.00") + /** * Converts from a currency to another. */ @@ -156,7 +159,7 @@ class CurrencyConverter : AbstractModule() { val json = JSONObject(body) if (json.getString("result") == "success") { - val result = json.getDouble("conversion_result") + val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result")) PublicMessage( "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" ) @@ -188,8 +191,8 @@ class CurrencyConverter : AbstractModule() { if (json.getString("result") == "success") { val codes = json.getJSONArray("supported_codes") for (i in 0 until codes.length()) { - val code = codes.getJSONArray(i); - SYMBOLS[code.getString(0)] = code.getString(1); + val code = codes.getJSONArray(i) + SYMBOLS[code.getString(0)] = code.getString(1) } } } catch (e: IOException) { diff --git a/version.properties b/version.properties index f272017..1765f9b 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 22 04:02:33 PDT 2023 -version.buildmeta=20230922040233 +#Wed Oct 04 08:46:27 PDT 2023 +version.buildmeta=20231004084627 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230922040233 +version.semver=0.8.0-rc+20231004084627 From b562b674a776a7a108f38c6f31accce1694aad6e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 12:37:20 -0700 Subject: [PATCH 726/842] Updated dependencies --- build.gradle | 8 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 7e13746..4bd1a08 100644 --- a/build.gradle +++ b/build.gradle @@ -5,14 +5,14 @@ import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.48.0' + id 'com.github.ben-manes.versions' version '0.49.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.23.1' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.9.10' id 'org.jetbrains.kotlin.kapt' version '1.9.10' - id 'org.jetbrains.kotlinx.kover' version '0.7.3' + id 'org.jetbrains.kotlinx.kover' version '0.7.4' id 'org.sonarqube' version '4.4.1.3373' id 'pmd' } @@ -57,11 +57,11 @@ dependencies { implementation 'org.apache.commons:commons-lang3:3.13.0' implementation 'org.apache.commons:commons-text:1.10.0' implementation 'commons-codec:commons-codec:1.16.0' - implementation 'commons-net:commons-net:3.9.0' + implementation 'commons-net:commons-net:3.10.0' // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.1.2-jre' + implementation 'com.google.guava:guava:32.1.3-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ac72c34..3fa8f86 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 0adc8e1..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -202,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ From 5a3b1d3f1912eb2b68090ec0733afa11000a3240 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 12:37:55 -0700 Subject: [PATCH 727/842] Changed decimal formatting --- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../thauvin/erik/mobibot/modules/CurrencyConverterTest.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index fc56d42..41cb995 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -133,7 +133,7 @@ class CurrencyConverter : AbstractModule() { private val SYMBOLS: TreeMap<String, String> = TreeMap() // Decimal format - private val DECIMAL_FORMAT = DecimalFormat("0.00") + private val DECIMAL_FORMAT = DecimalFormat("0.00#") /** * Converts from a currency to another. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 59ea2e7..4ee9668 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -64,15 +64,15 @@ class CurrencyConverterTest: LocalProperties() { assertThat( convertCurrency(apiKey,"100 USD to EUR").msg, "convertCurrency(100 USD to EUR)" - ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex()) + ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) assertThat( convertCurrency(apiKey,"1 USD to GBP").msg, "convertCurrency(1 USD to BGP)" - ).matches("1 United States Dollar = 0\\.\\d+ Pound Sterling".toRegex()) + ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) assertThat( convertCurrency(apiKey,"100,000.00 CAD to USD").msg, "convertCurrency(100,000.00 GBP to USD)" - ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d+ United States Dollar".toRegex()) + ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) assertThat(convertCurrency(apiKey,"100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") isInstanceOf(PublicMessage::class.java) From 105850990b8b7b185088d54def99b21817513431 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 13:34:30 -0700 Subject: [PATCH 728/842] Updated versions --- README.md | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index df026ab..1ecefeb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.0-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.10-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/version.properties b/version.properties index 1765f9b..8b93b12 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Oct 04 08:46:27 PDT 2023 -version.buildmeta=20231004084627 +#Fri Oct 13 13:31:20 PDT 2023 +version.buildmeta=20231013133120 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231004084627 +version.semver=0.8.0-rc+20231013133120 From 86b35874bfb6f5ec5f259cfbc3c1c54cc6a2d20c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 13:35:19 -0700 Subject: [PATCH 729/842] Using lorem-rss instead of my server for testing --- src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 7611ae3..51e2296 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -54,12 +54,13 @@ class FeedReaderTest { index(1).prop(Message::msg).contains("erik.thauvin.net") } - messages = readFeed("https://www.mobitopia.org/mobibot/logs/2021-10-27.xml") + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing") - messages = readFeed("https://www.mobitopia.org/mobibot/logs/2005-10-11.xml", 42) + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) assertThat(messages, "messages").size().isEqualTo(84) - assertThat(messages.last(), "messages.last").prop(Message::msg).contains("techdigest.tv") + assertThat(messages[messages.size - 2], "messages.size - 2").prop(Message::msg).startsWith("Lorem ipsum") + assertThat(messages.last(), "messages.last").prop(Message::msg).contains("http://example.com/test/") assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) From 8e37c3912a5d14bc35a17ad61c67238a856e6fd1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 17 Oct 2023 13:26:24 -0700 Subject: [PATCH 730/842] Improved feed reader testing --- .../net/thauvin/erik/mobibot/FeedReaderTest.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 51e2296..78f5b18 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -59,8 +59,13 @@ class FeedReaderTest { messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) assertThat(messages, "messages").size().isEqualTo(84) - assertThat(messages[messages.size - 2], "messages.size - 2").prop(Message::msg).startsWith("Lorem ipsum") - assertThat(messages.last(), "messages.last").prop(Message::msg).contains("http://example.com/test/") + messages.forEachIndexed { i, m -> + if (i % 2 == 0) { + assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum") + } else { + assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/") + } + } assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) @@ -68,7 +73,6 @@ class FeedReaderTest { assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) - assertFailure { readFeed("https://www.examplesfoo.com/") } - .isInstanceOf(UnknownHostException::class.java) + assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java) } } From abebca50796430694d12eecd478d5399c78377a9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 17 Oct 2023 13:26:43 -0700 Subject: [PATCH 731/842] Updated dependencies --- build.gradle | 4 ++-- version.properties | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 4bd1a08..75db38b 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.20.0', + log4j: '2.21.0', pmd : '6.55.0', ] @@ -80,7 +80,7 @@ dependencies { implementation 'com.squareup.okio:okio:3.6.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20230618' + implementation 'org.json:json:20231013' implementation 'org.jsoup:jsoup:1.16.1' // Thauvin diff --git a/version.properties b/version.properties index 8b93b12..ad204e5 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Oct 13 13:31:20 PDT 2023 -version.buildmeta=20231013133120 +#Fri Oct 13 20:37:28 PDT 2023 +version.buildmeta=20231013203728 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231013133120 +version.semver=0.8.0-rc+20231013203728 From acaa27f20de4ba34ee3d18d07071d38dae8ea2d0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 26 Oct 2023 20:46:36 -0700 Subject: [PATCH 732/842] Added support for currency X in Y command --- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 41cb995..c194571 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -76,7 +76,7 @@ class CurrencyConverter : AbstractModule() { if (SYMBOLS.isEmpty()) { event.respond(EMPTY_SYMBOLS_TABLE) - } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { + } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex())) { val msg = convertCurrency(properties[API_KEY_PROP], args) event.respond(msg.msg) if (msg.isError) { From adaff5ec38a809545b6b2832a8665a228600c2a4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 26 Oct 2023 20:46:53 -0700 Subject: [PATCH 733/842] Updated dependencies --- build.gradle | 7 +++---- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 75db38b..9fb85c6 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.21.0', + log4j: '2.21.1', pmd : '6.55.0', ] @@ -76,12 +76,11 @@ dependencies { implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" implementation 'com.rometools:rome:2.1.0' - implementation 'com.squareup.okhttp3:okhttp:4.11.0' - implementation 'com.squareup.okio:okio:3.6.0' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' implementation 'org.json:json:20231013' - implementation 'org.jsoup:jsoup:1.16.1' + implementation 'org.jsoup:jsoup:1.16.2' // Thauvin implementation 'net.thauvin.erik:cryptoprice:1.0.1' diff --git a/version.properties b/version.properties index ad204e5..c73da9e 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Oct 13 20:37:28 PDT 2023 -version.buildmeta=20231013203728 +#Thu Oct 26 20:43:39 PDT 2023 +version.buildmeta=20231026204339 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231013203728 +version.semver=0.8.0-rc+20231026204339 From d700aa06dfe785ab6dee411482f5866d554f6440 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 26 Oct 2023 21:13:46 -0700 Subject: [PATCH 734/842] Fixed SonarCloud code smells --- .idea/misc.xml | 1 - .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 22 ++++++++------- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 27 +++++++++++-------- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 27 +++++++++++-------- 6 files changed, 47 insertions(+), 34 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 67f5457..a59e398 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="FrameworkDetectionExcludesConfiguration"> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 51adc88..38b89e3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -42,7 +42,6 @@ import org.slf4j.Logger import java.io.* import java.net.HttpURLConnection import java.net.URL -import java.net.URLEncoder import java.nio.file.Files import java.nio.file.Paths import java.time.LocalDateTime @@ -125,14 +124,19 @@ object Utils { */ @JvmStatic fun String?.colorize(color: String): String { - return if (isNullOrEmpty()) { - "" - } else if (color == DEFAULT_COLOR) { - this - } else if (Colors.BOLD == color || Colors.REVERSE == color) { - color + this + color - } else { - color + this + Colors.NORMAL + return when { + isNullOrEmpty() -> { + "" + } + color == DEFAULT_COLOR -> { + this + } + Colors.BOLD == color || Colors.REVERSE == color -> { + color + this + color + } + else -> { + color + this + Colors.NORMAL + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 88109e8..723108d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -129,7 +129,7 @@ class Ignore : AbstractCommand() { } } - if (ignored.size > 0) { + if (ignored.isNotEmpty()) { event.sendMessage("The following nicks are ignored:") event.sendList(ignored.sorted(), 8, isIndent = true) } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 96800bb..cdeb943 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -85,18 +85,23 @@ class Tell(private val serialObject: String) : AbstractCommand() { override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isEnabled()) { - if (args.isBlank()) { - helpResponse(channel, args, event) - } else if (args.startsWith(View.VIEW_CMD)) { - if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { - viewAll(event) - } else { - viewMessages(event) + when { + args.isBlank() -> { + helpResponse(channel, args, event) + } + args.startsWith(View.VIEW_CMD) -> { + if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { + viewAll(event) + } else { + viewMessages(event) + } + } + args.startsWith("$TELL_DEL_KEYWORD ") -> { + deleteMessage(channel, args, event) + } + else -> { + newMessage(channel, args, event) } - } else if (args.startsWith("$TELL_DEL_KEYWORD ")) { - deleteMessage(channel, args, event) - } else { - newMessage(channel, args, event) } if (clean()) { save() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index a30ba24..2d87dbf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -141,7 +141,7 @@ class FeedsManager private constructor() { .append("\"><b>") .append(channel) .append("</b></a>") - if (comments.size > 0) { + if (comments.isNotEmpty()) { buff.append(" <br/><br/>") for (j in comments.indices) { if (j > 0) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index c194571..3aad379 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -74,19 +74,24 @@ class CurrencyConverter : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { reload(properties[API_KEY_PROP]) - if (SYMBOLS.isEmpty()) { - event.respond(EMPTY_SYMBOLS_TABLE) - } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex())) { - val msg = convertCurrency(properties[API_KEY_PROP], args) - event.respond(msg.msg) - if (msg.isError) { + when { + SYMBOLS.isEmpty() -> { + event.respond(EMPTY_SYMBOLS_TABLE) + } + args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { + val msg = convertCurrency(properties[API_KEY_PROP], args) + event.respond(msg.msg) + if (msg.isError) { + helpResponse(event) + } + } + args.contains(CODES_KEYWORD) -> { + event.sendMessage("The supported currency codes are:") + event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) + } + else -> { helpResponse(event) } - } else if (args.contains(CODES_KEYWORD)) { - event.sendMessage("The supported currency codes are:") - event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) - } else { - helpResponse(event) } } From 4c90870f4a2f9ff75adb842b6fc10a2d9bd39a55 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Nov 2023 22:02:54 -0700 Subject: [PATCH 735/842] Cleaned up code --- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 60 ++++++++--------- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 29 ++++---- .../thauvin/erik/mobibot/commands/Ignore.kt | 14 ++-- .../net/thauvin/erik/mobibot/commands/Info.kt | 10 +-- .../net/thauvin/erik/mobibot/commands/Msg.kt | 4 +- .../thauvin/erik/mobibot/commands/Recap.kt | 8 +-- .../thauvin/erik/mobibot/commands/Versions.kt | 8 +-- .../erik/mobibot/commands/links/Comment.kt | 46 ++++++------- .../mobibot/commands/links/LinksManager.kt | 6 +- .../erik/mobibot/commands/links/Posting.kt | 20 +++--- .../erik/mobibot/commands/links/Tags.kt | 4 +- .../erik/mobibot/commands/links/View.kt | 10 +-- .../mobibot/commands/seen/NickComparator.kt | 1 + .../erik/mobibot/commands/seen/Seen.kt | 14 ++-- .../erik/mobibot/commands/seen/SeenNick.kt | 1 + .../erik/mobibot/commands/tell/Tell.kt | 47 ++++++------- .../erik/mobibot/commands/tell/TellMessage.kt | 25 +++---- .../thauvin/erik/mobibot/entries/Entries.kt | 8 +-- .../erik/mobibot/entries/EntriesUtils.kt | 6 +- .../erik/mobibot/entries/EntryComment.kt | 1 + .../thauvin/erik/mobibot/entries/EntryLink.kt | 57 ++++++++-------- .../erik/mobibot/entries/FeedsManager.kt | 48 +++++++------- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 38 +++++------ .../erik/mobibot/modules/CryptoPrices.kt | 6 +- .../erik/mobibot/modules/CurrencyConverter.kt | 3 + .../erik/mobibot/modules/GoogleSearch.kt | 30 ++++----- .../thauvin/erik/mobibot/modules/Lookup.kt | 6 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 40 +++++------ .../erik/mobibot/modules/ModuleException.kt | 7 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 24 +++---- .../erik/mobibot/modules/RockPaperScissors.kt | 8 +-- .../erik/mobibot/modules/StockQuote.kt | 66 +++++++++---------- .../thauvin/erik/mobibot/modules/Weather2.kt | 34 +++++----- .../erik/mobibot/modules/WolframAlpha.kt | 28 ++++---- .../thauvin/erik/mobibot/modules/WorldTime.kt | 4 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 10 +-- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 4 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 10 +-- .../erik/mobibot/ExceptionSanitizer.kt | 6 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 32 ++++----- .../thauvin/erik/mobibot/commands/InfoTest.kt | 8 +-- .../erik/mobibot/commands/RecapTest.kt | 6 +- .../commands/links/LinksManagerTest.kt | 4 +- .../erik/mobibot/commands/links/ViewTest.kt | 16 ++--- .../erik/mobibot/entries/EntriesUtilsTest.kt | 20 +++--- .../erik/mobibot/entries/EntryLinkTest.kt | 10 +-- .../erik/mobibot/modules/ChatGptTest.kt | 10 +-- .../mobibot/modules/CurrencyConverterTest.kt | 18 ++--- .../thauvin/erik/mobibot/modules/DiceTest.kt | 8 +-- .../erik/mobibot/modules/GoogleSearchTest.kt | 12 ++-- .../erik/mobibot/modules/MastodonTest.kt | 14 ++-- .../mobibot/modules/ModuleExceptionTest.kt | 38 +++++------ .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 10 +-- .../erik/mobibot/modules/WordTimeTest.kt | 6 +- 61 files changed, 496 insertions(+), 481 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 7cf6719..98ef74a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -63,7 +63,7 @@ object Constants { * User-Agent */ const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 3342077..f91c457 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -97,9 +97,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event.sendMessage("Type a URL on $channel to post it.") event.sendMessage("For more information on a specific command, type:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) + ) ) event.sendMessage("The commands are:") event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) @@ -161,7 +161,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot<PircBotX>()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has joined ${event.channel.name} on $serverHostname" + "$nick has joined ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -209,7 +209,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot<PircBotX>()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has left ${event.channel.name} on $serverHostname" + "$nick has left ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -232,22 +232,22 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Set up the command line options val parser = ArgParser(Constants.CLI_CMD) val debug by parser.option( - ArgType.Boolean, - Constants.DEBUG_ARG, - Constants.DEBUG_ARG.substring(0, 1), - "Print debug & logging data directly to the console" + ArgType.Boolean, + Constants.DEBUG_ARG, + Constants.DEBUG_ARG.substring(0, 1), + "Print debug & logging data directly to the console" ).default(false) val property by parser.option( - ArgType.String, - Constants.PROPS_ARG, - Constants.PROPS_ARG.substring(0, 1), - "Use alternate properties file" + ArgType.String, + Constants.PROPS_ARG, + Constants.PROPS_ARG.substring(0, 1), + "Use alternate properties file" ).default("./${ReleaseInfo.PROJECT}.properties") val version by parser.option( - ArgType.Boolean, - Constants.VERSION_ARG, - Constants.VERSION_ARG.substring(0, 1), - "Print version info" + ArgType.Boolean, + Constants.VERSION_ARG, + Constants.VERSION_ARG.substring(0, 1), + "Print version info" ).default(false) // Parse the command line @@ -256,8 +256,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (version) { // Output the version println( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" ) println(ReleaseInfo.WEBSITE) } else { @@ -265,7 +265,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro val p = Properties() try { Files.newInputStream( - Paths.get(property) + Paths.get(property) ).use { fis -> p.load(fis) } @@ -284,11 +284,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (!debug) { try { val stdout = PrintStream( - BufferedOutputStream( - FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true - ) - ), true + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true + ) + ), true ) System.setOut(stdout) } catch (ignore: IOException) { @@ -297,9 +297,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro } try { val stderr = PrintStream( - BufferedOutputStream( - FileOutputStream("$logsDir$nickname.err", true) - ), true + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true ) System.setErr(stderr) } catch (ignore: IOException) { @@ -324,8 +324,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro login = p.getProperty("login", nickname) realName = p.getProperty("realname", nickname) addServer( - ircServer, - p.getIntProperty("port", Constants.DEFAULT_PORT) + ircServer, + p.getIntProperty("port", Constants.DEFAULT_PORT) ) addAutoJoinChannel(channel) addListener(this@Mobibot) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 1a4260d..7cb5aed 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -92,7 +92,7 @@ class Pinboard { */ private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( - toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() ).format(DateTimeFormatter.ISO_INSTANT) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 38b89e3..e4760d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -128,12 +128,15 @@ object Utils { isNullOrEmpty() -> { "" } + color == DEFAULT_COLOR -> { this } + Colors.BOLD == color || Colors.REVERSE == color -> { color + this + color } + else -> { color + this + Colors.NORMAL } @@ -220,7 +223,7 @@ object Utils { if (serialFile.exists() && serialFile.fileSize() > 0) { try { ObjectInputStream( - BufferedInputStream(Files.newInputStream(serialFile)) + BufferedInputStream(Files.newInputStream(serialFile)) ).use { input -> if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") return input.readObject() @@ -307,20 +310,20 @@ object Utils { @JvmStatic @JvmOverloads fun GenericMessageEvent.sendList( - list: List<String>, - maxPerLine: Int, - separator: String = " ", - isBold: Boolean = false, - isIndent: Boolean = false + list: List<String>, + maxPerLine: Int, + separator: String = " ", + isBold: Boolean = false, + isIndent: Boolean = false ) { var i = 0 while (i < list.size) { sendMessage( - helpFormat( - list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), - isBold, - isIndent - ), + helpFormat( + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), + isBold, + isIndent + ), ) i += maxPerLine } @@ -419,8 +422,8 @@ object Utils { fun URL.reader(): UrlReaderResponse { val connection = this.openConnection() as HttpURLConnection connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" ) return if (connection.responseCode.isHttpSuccess()) { UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 723108d..d083c10 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -50,15 +50,15 @@ class Ignore : AbstractCommand() { override val name = IGNORE_CMD override val help = listOf( - "To ignore a link posted to the channel:", - helpFormat("https://www.foo.bar %n"), - "To check your ignore status:", - helpFormat("%c $name"), - "To toggle your ignore status:", - helpFormat("%c $name $me") + "To ignore a link posted to the channel:", + helpFormat("https://www.foo.bar %n"), + "To check your ignore status:", + helpFormat("%c $name"), + "To toggle your ignore status:", + helpFormat("%c $name $me") ) private val helpOp = help.plus( - arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) + arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) ) override val isOpOnly = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 7eb3bdb..ed0b6ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -48,8 +48,8 @@ import kotlin.time.toDuration class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", - "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", + "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" ) override val name = "info" override val help = listOf("To view information about the bot:", helpFormat("%c $name")) @@ -104,9 +104,9 @@ class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { event.sendList(allVersions, 1) val info = StringBuilder() info.append("Uptime: ") - .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) - .append(" [Entries: ") - .append(LinksManager.entries.links.size) + .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) + .append(" [Entries: ") + .append(LinksManager.entries.links.size) if (seen.isEnabled()) { info.append(", Seen: ").append(seen.count()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 48ff38f..20a6635 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -39,8 +39,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Msg : AbstractCommand() { override val name = "msg" override val help = listOf( - "To have the bot send a private message to someone:", - helpFormat("%c $name <nick> <text>") + "To have the bot send a private message to someone:", + helpFormat("%c $name <nick> <text>") ) override val isOpOnly = true override val isPublic = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 66e721e..77154c7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -41,8 +41,8 @@ import java.time.LocalDateTime class Recap : AbstractCommand() { override val name = "recap" override val help = listOf( - "To list the last 10 public channel messages:", - helpFormat("%c $name") + "To list the last 10 public channel messages:", + helpFormat("%c $name") ) override val isOpOnly = false override val isPublic = true @@ -60,8 +60,8 @@ class Recap : AbstractCommand() { @JvmStatic fun storeRecap(sender: String, message: String, isAction: Boolean) { recaps.add( - LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() - + " - $sender" + (if (isAction) " " else ": ") + message + LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + + " - $sender" + (if (isAction) " " else ": ") + message ) if (recaps.size > MAX_RECAPS) { recaps.removeFirst() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index f920891..896c569 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -40,10 +40,10 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Versions : AbstractCommand() { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", - "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + - ", JVM ${System.getProperty("java.runtime.version")}", - "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + + ", JVM ${System.getProperty("java.runtime.version")}", + "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" ) override val name = "versions" override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 9fe250d..1443d44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -45,13 +45,13 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Comment : AbstractCommand() { override val name = COMMAND override val help = listOf( - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, use its label: ", - helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), - "To delete a comment, use its label and a minus sign: ", - helpFormat("${Constants.LINK_CMD}1.1:-") + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, use its label: ", + helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), + "To delete a comment, use its label and a minus sign: ", + helpFormat("${Constants.LINK_CMD}1.1:-") ) override val isOpOnly = false override val isPublic = true @@ -100,12 +100,12 @@ class Comment : AbstractCommand() { } private fun changeAuthor( - channel: String, - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) && cmd.length > 1) { val comment = entry.getComment(commentIndex) @@ -118,11 +118,11 @@ class Comment : AbstractCommand() { } private fun deleteComment( - channel: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) @@ -134,11 +134,11 @@ class Comment : AbstractCommand() { } private fun setComment( - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { entry.setComment(commentIndex, cmd, event.user.nick) event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fb1a634..fba6b99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -161,8 +161,8 @@ class LinksManager : AbstractCommand() { internal fun fetchTitle(link: String): String { try { val html = Jsoup.connect(link) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") - .get() + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() val title = html.title() if (title.isNotBlank()) { return title @@ -178,7 +178,7 @@ class LinksManager : AbstractCommand() { return try { val match = entries.links.single { it.link == link } event.sendMessage( - "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) + "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) ) true } catch (ignore: NoSuchElementException) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index e04cd15..ff4278d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -47,16 +47,16 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Posting : AbstractCommand() { override val name = "posting" override val help = listOf( - "Post a URL, by saying it on a line on its own:", - helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", - "To add a title, use its label and a pipe:", - helpFormat("${Constants.LINK_CMD}1:|This is the title"), - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, see: ", - helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + "Post a URL, by saying it on a line on its own:", + helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", + "To add a title, use its label and a pipe:", + helpFormat("${Constants.LINK_CMD}1:|This is the title"), + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, see: ", + helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 9071059..1662857 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -44,8 +44,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Tags : AbstractCommand() { override val name = COMMAND override val help = listOf( - "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", - helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") + "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", + helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") ) override val isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index ea1ebf8..825e374 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -46,8 +46,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class View : AbstractCommand() { override val name = VIEW_CMD override val help = listOf( - "To list or search the current URL posts:", - helpFormat("%c $name [<start>] [<query>]") + "To list or search the current URL posts:", + helpFormat("%c $name [<start>] [<query>]") ) override val isOpOnly = false override val isPublic = true @@ -107,9 +107,9 @@ class View : AbstractCommand() { if (sent == MAX_ENTRIES && index < entries.links.size) { event.sendMessage("To view more, try: ") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index d29b30d..cfd2c27 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -39,6 +39,7 @@ class NickComparator : Comparator<String>, Serializable { } companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 05ad330..c9ee0f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -58,7 +58,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { override val name = "seen" override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) private val helpOp = help.plus( - arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) ) override val isOpOnly = false override val isPublic = true @@ -130,12 +130,12 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") seenNicks.putAll( - loadSerialData( - serialObject, - TreeMap<String, SeenNick>(), - logger, - "seen nicknames" - ) as TreeMap<String, SeenNick> + loadSerialData( + serialObject, + TreeMap<String, SeenNick>(), + logger, + "seen nicknames" + ) as TreeMap<String, SeenNick> ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index b09cbf4..7924977 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -35,6 +35,7 @@ import java.io.Serializable data class SeenNick(val nick: String, val lastSeen: Long) : Serializable { companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index cdeb943..061ca6a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -66,11 +66,11 @@ class Tell(private val serialObject: String) : AbstractCommand() { override val name = "tell" override val help = listOf( - "To send a message to someone when they join the channel:", - helpFormat("%c $name <nick> <message>"), - "To view queued and sent messages:", - helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' + "To send a message to someone when they join the channel:", + helpFormat("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpFormat("%c $name ${View.VIEW_CMD}"), + "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' ) override val isOpOnly: Boolean = false override val isPublic: Boolean = isEnabled() @@ -89,6 +89,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { args.isBlank() -> { helpResponse(channel, args, event) } + args.startsWith(View.VIEW_CMD) -> { if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { viewAll(event) @@ -96,9 +97,11 @@ class Tell(private val serialObject: String) : AbstractCommand() { viewMessages(event) } } + args.startsWith("$TELL_DEL_KEYWORD ") -> { deleteMessage(channel, args, event) } + else -> { newMessage(channel, args, event) } @@ -123,9 +126,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { if (messages.removeIf { - it.id == id && - (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) - }) { + it.id == id && + (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) + }) { save() event.sendMessage("The message was deleted from the queue.") } else { @@ -185,7 +188,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (message.sender == nickname) { if (event !is MessageEvent) { event.user.send().message( - "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" + "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" ) message.isReceived = true message.isNotified = true @@ -193,17 +196,17 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { event.user.send().message( - "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" + "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" ) message.isReceived = true save() } } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified + && !message.isNotified ) { event.user.send().message( - "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " - + "${message.recipient.bold()} on ${message.receptionDate}" + "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " + + "${message.recipient.bold()} on ${message.receptionDate}" ) message.isNotified = true save() @@ -224,8 +227,8 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (messages.isNotEmpty()) { for (message in messages) { event.sendMessage( - "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + - (if (message.isReceived) "DELIVERED]" else "QUEUED]") + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + + (if (message.isReceived) "DELIVERED]" else "QUEUED]") ) } } else { @@ -243,13 +246,13 @@ class Tell(private val serialObject: String) : AbstractCommand() { } if (message.isReceived) { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" ) } else { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" ) } event.sendMessage(helpFormat(message.message)) @@ -259,9 +262,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } else { event.sendMessage("To delete one or all delivered messages:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) - ) + helpFormat( + helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) + ) ) event.sendMessage(help.last()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 33bc1e9..d17fbb5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -39,20 +39,20 @@ import java.time.format.DateTimeFormatter * Tell Message. */ class TellMessage( - /** - * Returns the message's sender. - */ - val sender: String, + /** + * Returns the message's sender. + */ + val sender: String, - /** - * Returns the message's recipient. - */ - val recipient: String, + /** + * Returns the message's recipient. + */ + val recipient: String, - /** - * Returns the message text. - */ - val message: String + /** + * Returns the message text. + */ + val message: String ) : Serializable { /** * Returns the queued date/time. @@ -98,6 +98,7 @@ class TellMessage( } companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 2L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index ba22746..e8676ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.entries import net.thauvin.erik.mobibot.Utils.today class Entries( - var channel: String = "", - var ircServer: String = "", - var logsDir: String = "", - var backlogs: String = "" + var channel: String = "", + var ircServer: String = "", + var logsDir: String = "", + var backlogs: String = "" ) { val links = mutableListOf<EntryLink>() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index ff1e423..9c09626 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -43,7 +43,7 @@ object EntriesUtils { */ @JvmStatic fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** * Prints an entry's link for display on the channel. @@ -52,7 +52,7 @@ object EntriesUtils { @JvmOverloads fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") - .append('[').append(entry.nick).append(']') + .append('[').append(entry.nick).append(']') if (isView && entry.comments.isNotEmpty()) { buff.append("[+").append(entry.comments.size).append(']') } @@ -73,7 +73,7 @@ object EntriesUtils { */ @JvmStatic fun printTags(entryIndex: Int, entry: EntryLink): String = - entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") /** * Builds link label based on its index. e.g: L1 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index 8de54e4..e18d692 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -46,6 +46,7 @@ data class EntryComment(var comment: String, var nick: String) : Serializable { companion object { // Serial version UID + @Suppress("ConstPropertyName") private const val serialVersionUID: Long = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 80ca536..4a69446 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -40,40 +40,40 @@ import java.util.* * The class used to store link entries. */ class EntryLink( - // Link's comments - val comments: MutableList<EntryComment> = mutableListOf(), + // Link's comments + val comments: MutableList<EntryComment> = mutableListOf(), - // Tags/categories - val tags: MutableList<SyndCategory> = mutableListOf(), + // Tags/categories + val tags: MutableList<SyndCategory> = mutableListOf(), - // Channel - var channel: String, + // Channel + var channel: String, - // Creation date - var date: Date = Calendar.getInstance().time, + // Creation date + var date: Date = Calendar.getInstance().time, - // Link's URL - var link: String, + // Link's URL + var link: String, - // Author's login - var login: String = "", + // Author's login + var login: String = "", - // Author's nickname - var nick: String, + // Author's nickname + var nick: String, - // Link's title - var title: String + // Link's title + var title: String ) : Serializable { /** * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - login: String, - channel: String, - tags: List<String?> + link: String, + title: String, + nick: String, + login: String, + channel: String, + tags: List<String?> ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { setTags(tags) } @@ -82,12 +82,12 @@ class EntryLink( * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - channel: String, - date: Date, - tags: List<SyndCategory> + link: String, + title: String, + nick: String, + channel: String, + date: Date, + tags: List<SyndCategory> ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { this.tags.addAll(tags) } @@ -207,6 +207,7 @@ class EntryLink( companion object { // Serial version UID + @Suppress("ConstPropertyName") private const val serialVersionUID: Long = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index 2d87dbf..f786cb2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -55,24 +55,24 @@ class FeedsManager private constructor() { private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) // The file containing the current entries. - private const val currentXml = "current.xml" + private const val CURRENT_XML = "current.xml" // The .xml extension. - private const val dotXml = ".xml" + private const val DOT_XML = ".xml" /** * Loads the current feed. */ @JvmStatic @Throws(IOException::class, FeedException::class) - fun loadFeed(entries: Entries, currentFile: String = currentXml): String { + fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String { entries.links.clear() val xml = Paths.get("${entries.logsDir}${currentFile}") var pubDate = today() if (xml.exists()) { val input = SyndFeedInput() InputStreamReader( - Files.newInputStream(xml), StandardCharsets.UTF_8 + Files.newInputStream(xml), StandardCharsets.UTF_8 ).use { reader -> val feed = input.build(reader) pubDate = feed.publishedDate.toIsoLocalDate() @@ -81,12 +81,12 @@ class FeedsManager private constructor() { for (i in items.indices.reversed()) { with(items[i]) { entry = EntryLink( - link, - title, - author.substring(author.lastIndexOf('(') + 1, author.length - 1), - entries.channel, - publishedDate, - categories + link, + title, + author.substring(author.lastIndexOf('(') + 1, author.length - 1), + entries.channel, + publishedDate, + categories ) var split: List<String> for (comment in description.value.split("<br/>")) { @@ -110,7 +110,7 @@ class FeedsManager private constructor() { * Saves the feeds. */ @JvmStatic - fun saveFeed(entries: Entries, currentFile: String = currentXml) { + fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) { if (logger.isDebugEnabled) logger.debug("Saving the feeds...") if (entries.logsDir.isNotBlank()) { try { @@ -119,7 +119,7 @@ class FeedsManager private constructor() { val items: MutableList<SyndEntry> = mutableListOf() var item: SyndEntry OutputStreamWriter( - Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 + Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 ).use { fw -> with(rss) { feedType = "rss_2.0" @@ -134,13 +134,13 @@ class FeedsManager private constructor() { with(entries.links[i]) { buff.setLength(0) buff.append("Posted by <b>") - .append(nick) - .append("</b> on <a href=\"irc://") - .append(entries.ircServer).append('/') - .append(channel) - .append("\"><b>") - .append(channel) - .append("</b></a>") + .append(nick) + .append("</b> on <a href=\"irc://") + .append(entries.ircServer).append('/') + .append(channel) + .append("\"><b>") + .append(channel) + .append("</b></a>") if (comments.isNotEmpty()) { buff.append(" <br/><br/>") for (j in comments.indices) { @@ -165,11 +165,11 @@ class FeedsManager private constructor() { output.output(rss, fw) } OutputStreamWriter( - Files.newOutputStream( - Paths.get( - entries.logsDir + today() + dotXml - ) - ), StandardCharsets.UTF_8 + Files.newOutputStream( + Paths.get( + entries.logsDir + today() + DOT_XML + ) + ), StandardCharsets.UTF_8 ).use { fw -> output.output(rss, fw) } } catch (e: FeedException) { if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index e1e86df..bd92332 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -56,8 +56,8 @@ class ChatGpt : AbstractModule() { if (args.isNotBlank()) { try { val answer = chat( - args.trim(), properties[API_KEY_PROP], - properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() + args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) @@ -107,13 +107,13 @@ class ChatGpt : AbstractModule() { if (!apiKey.isNullOrEmpty()) { val prompt = JSONWriter.valueToString("Q:$query\nA:") val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST( - HttpRequest.BodyPublishers.ofString( - """{ + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) + .POST( + HttpRequest.BodyPublishers.ofString( + """{ "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, @@ -122,9 +122,9 @@ class ChatGpt : AbstractModule() { "frequency_penalty": 0, "presence_penalty": 0 }""".trimIndent() - ) ) - .build() + ) + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { @@ -134,16 +134,16 @@ class ChatGpt : AbstractModule() { return choices.getJSONObject(0).getString("text").trim() } catch (e: JSONException) { throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { if (response.statusCode() == 429) { throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." ) } else { throw IOException("HTTP Status Code: " + response.statusCode()) @@ -151,9 +151,9 @@ class ChatGpt : AbstractModule() { } } catch (e: IOException) { throw ModuleException( - "$CHATGPT_CMD($query): IO", - "An IO error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 5136504..d14056e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -134,9 +134,9 @@ class CryptoPrices : AbstractModule() { } } catch (e: CryptoException) { throw ModuleException( - "loadCurrencies(): CE", - "An error has occurred while retrieving the currencies table.", - e + "loadCurrencies(): CE", + "An error has occurred while retrieving the currencies table.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 3aad379..da0efd8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -78,6 +78,7 @@ class CurrencyConverter : AbstractModule() { SYMBOLS.isEmpty() -> { event.respond(EMPTY_SYMBOLS_TABLE) } + args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { val msg = convertCurrency(properties[API_KEY_PROP], args) event.respond(msg.msg) @@ -85,10 +86,12 @@ class CurrencyConverter : AbstractModule() { helpResponse(event) } } + args.contains(CODES_KEYWORD) -> { event.sendMessage("The supported currency codes are:") event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) } + else -> { helpResponse(event) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index b0e911c..f426d1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -65,10 +65,10 @@ class GoogleSearch : AbstractModule() { if (args.isNotBlank()) { try { val results = searchGoogle( - args, - properties[API_KEY_PROP], - properties[CSE_KEY_PROP], - event.user.nick + args, + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], + event.user.nick ) for (msg in results) { if (msg.isError) { @@ -104,23 +104,23 @@ class GoogleSearch : AbstractModule() { @JvmStatic @Throws(ModuleException::class) fun searchGoogle( - query: String, - apiKey: String?, - cseKey: String?, - quotaUser: String = ReleaseInfo.PROJECT + query: String, + apiKey: String?, + cseKey: String?, + quotaUser: String = ReleaseInfo.PROJECT ): List<Message> { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { throw ModuleException( - "${GoogleSearch::class.java.name} is disabled.", - "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." + "${GoogleSearch::class.java.name} is disabled.", + "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." ) } val results = mutableListOf<Message>() if (query.isNotBlank()) { try { val url = URL( - "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + + ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" ) val json = JSONObject(url.reader().body) if (json.has("items")) { @@ -141,9 +141,9 @@ class GoogleSearch : AbstractModule() { throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) } catch (e: JSONException) { throw ModuleException( - "searchGoogle($query): JSON", - "A JSON error has occurred searching Google.", - e + "searchGoogle($query): JSON", + "A JSON error has occurred searching Google.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index fc85226..9ab2ead 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -55,9 +55,9 @@ class Lookup : AbstractModule() { event.respondWith(nslookup(args).prependIndent()) } catch (ignore: UnknownHostException) { if (args.matches( - ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") - .toRegex() - ) + ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + .toRegex() + ) ) { try { val lines = whois(args) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 4cf2fe9..3be3a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -65,7 +65,7 @@ class Mastodon : SocialModule() { private fun formatTags(entry: EntryLink): String { return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } - .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } + .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } } /** @@ -74,11 +74,11 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) override fun post(message: String, isDm: Boolean): String { return toot( - apiKey = properties[ACCESS_TOKEN_PROP], - instance = properties[INSTANCE_PROP], - handle = handle, - message = message, - isDm = isDm + apiKey = properties[ACCESS_TOKEN_PROP], + instance = properties[INSTANCE_PROP], + handle = handle, + message = message, + isDm = isDm ) } @@ -99,21 +99,21 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { val request = HttpRequest.newBuilder() - .uri(URI.create("https://$instance/api/v1/statuses")) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .POST( - HttpRequest.BodyPublishers.ofString( - JSONWriter.valueToString( - if (isDm) { - mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") - } else { - mapOf("status" to message) - } - ) - ) + .uri(URI.create("https://$instance/api/v1/statuses")) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .POST( + HttpRequest.BodyPublishers.ofString( + JSONWriter.valueToString( + if (isDm) { + mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + } else { + mapOf("status" to message) + } + ) ) - .build() + ) + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 017efd4..a7416c2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -34,11 +34,12 @@ package net.thauvin.erik.mobibot.modules * The `ModuleException` class. */ class ModuleException @JvmOverloads constructor( - val debugMessage: String, - message: String? = null, - cause: Throwable? = null + val debugMessage: String, + message: String? = null, + cause: Throwable? = null ) : Exception(message, cause) { companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index de5c1e8..944dbc1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -50,18 +50,18 @@ class Ping : AbstractModule() { */ @JvmField val PINGS = listOf( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!" + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!" ) @JvmStatic diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 359956a..a299d8d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -52,10 +52,10 @@ class RockPaperScissors : AbstractModule() { with(help) { add("To play Rock Paper Scissors:") add( - helpFormat( - "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" - + " | ${Hands.SCISSORS.name.lowercase()}" - ) + helpFormat( + "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 661a4e8..dcae5e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -132,8 +132,8 @@ class StockQuote : AbstractModule() { fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${StockQuote::class.java.name} is disabled.", - "${STOCK_CMD.capitalise()} is disabled. The API key is missing." + "${StockQuote::class.java.name} is disabled.", + "${STOCK_CMD.capitalise()} is disabled. The API key is missing." ) } val messages = mutableListOf<Message>() @@ -144,8 +144,8 @@ class StockQuote : AbstractModule() { with(messages) { // Search for symbol/keywords response = URL( - "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body var json = getJsonResponse(response, debugMessage) val symbols = json.getJSONArray("bestMatches") @@ -156,9 +156,9 @@ class StockQuote : AbstractModule() { // Get quote for symbol response = URL( - "${API_URL}GLOBAL_QUOTE&symbol=" - + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}GLOBAL_QUOTE&symbol=" + + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body json = getJsonResponse(response, debugMessage) val quote = json.getJSONObject("Global Quote") @@ -167,50 +167,50 @@ class StockQuote : AbstractModule() { } else { add( - PublicMessage( - "Symbol: " + quote.getString("01. symbol").unescapeXml() - + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' - ) + PublicMessage( + "Symbol: " + quote.getString("01. symbol").unescapeXml() + + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' + ) ) val pad = 10 add( - PublicMessage( - "Price:".padEnd(pad).prependIndent() - + quote.getString("05. price").unescapeXml() - ) + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + quote.getString("05. price").unescapeXml() + ) ) add( - PublicMessage( - "Previous:".padEnd(pad).prependIndent() - + quote.getString("08. previous close").unescapeXml() - ) + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + quote.getString("08. previous close").unescapeXml() + ) ) val data = arrayOf( - "Open" to "02. open", - "High" to "03. high", - "Low" to "04. low", - "Volume" to "06. volume", - "Latest" to "07. latest trading day" + "Open" to "02. open", + "High" to "03. high", + "Low" to "04. low", + "Volume" to "06. volume", + "Latest" to "07. latest trading day" ) data.forEach { add( - NoticeMessage( - "${it.first}:".padEnd(pad).prependIndent() - + quote.getString(it.second).unescapeXml() - ) + NoticeMessage( + "${it.first}:".padEnd(pad).prependIndent() + + quote.getString(it.second).unescapeXml() + ) ) } add( - NoticeMessage( - "Change:".padEnd(pad).prependIndent() - + quote.getString("09. change").unescapeXml() - + " [" + quote.getString("10. change percent").unescapeXml() + ']' - ) + NoticeMessage( + "Change:".padEnd(pad).prependIndent() + + quote.getString("09. change").unescapeXml() + + " [" + quote.getString("10. change percent").unescapeXml() + ']' + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 533cce6..80a06fa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -120,8 +120,8 @@ class Weather2 : AbstractModule() { fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Weather2::class.java.name} is disabled.", - "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." + "${Weather2::class.java.name} is disabled.", + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." ) } val owm = OWM(apiKey) @@ -145,10 +145,10 @@ class Weather2 : AbstractModule() { } if (cwd.hasCityName()) { messages.add( - PublicMessage( - "City: ${cwd.cityName}, " + - country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" - ) + PublicMessage( + "City: ${cwd.cityName}, " + + country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" + ) ) cwd.mainData?.let { with(it) { @@ -181,8 +181,8 @@ class Weather2 : AbstractModule() { for (w in it) { w?.let { condition.append(' ') - .append(w.getDescription().capitalise()) - .append('.') + .append(w.getDescription().capitalise()) + .append('.') } } messages.add(NoticeMessage(condition.toString())) @@ -192,15 +192,15 @@ class Weather2 : AbstractModule() { cwd.cityId?.let { if (it > 0) { messages.add( - NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) + NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) ) } else { messages.add( - NoticeMessage( - "https://openweathermap.org/find?q=" - + "$city,${code.uppercase()}".encodeUrl(), - Colors.GREEN - ) + NoticeMessage( + "https://openweathermap.org/find?q=" + + "$city,${code.uppercase()}".encodeUrl(), + Colors.GREEN + ) ) } } @@ -209,9 +209,9 @@ class Weather2 : AbstractModule() { } catch (e: APIException) { if (e.code == 404) { throw ModuleException( - "getWeather($query): API ${e.code}", - "The requested city was not found.", - e + "getWeather($query): API ${e.code}", + "The requested city was not found.", + e ) } else { throw ModuleException("getWeather($query): API ${e.code}", e.message, e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 049807a..a72efab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -60,15 +60,15 @@ class WolframAlpha : AbstractModule() { try { val query = args.trim().split("units=", limit = 2, ignoreCase = true) event.sendMessage( - queryWolfram( - query[0].trim(), - units = if (query.size == 2) { - getUnits(query[1].trim()) - } else { - getUnits(properties[UNITS_PROP]) - }, - appId = properties[APPID_KEY_PROP] - ) + queryWolfram( + query[0].trim(), + units = if (query.size == 2) { + getUnits(query[1].trim()) + } else { + getUnits(properties[UNITS_PROP]) + }, + appId = properties[APPID_KEY_PROP] + ) ) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) @@ -111,15 +111,15 @@ class WolframAlpha : AbstractModule() { return urlReader.body } else { throw ModuleException( - "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", - urlReader.body.ifEmpty { - "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" - } + "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", + urlReader.body.ifEmpty { + "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" + } ) } } catch (ioe: IOException) { throw ModuleException( - "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe + "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index debbe98..18072bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -322,7 +322,7 @@ class WorldTime : AbstractModule() { put("ZULU", "Zulu") put("ZW", "Africa/Harare") ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } - .forEach { tz -> put(tz, tz) } + .forEach { tz -> put(tz, tz) } } // The Time command @@ -336,7 +336,7 @@ class WorldTime : AbstractModule() { // Date/Time Format private var dtf = - DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") + DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") /** * Returns the current Internet (beat) Time. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 2695a3b..0607936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg * The `ErrorMessage` class. */ class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isError = true) + Message(msg, color, isError = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 3b4be49..23a33b9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -36,11 +36,11 @@ import net.thauvin.erik.semver.Constants * The `Message` class. */ open class Message @JvmOverloads constructor( - var msg: String, - var color: String = DEFAULT_COLOR, - var isNotice: Boolean = false, - isError: Boolean = false, - var isPrivate: Boolean = false + var msg: String, + var color: String = DEFAULT_COLOR, + var isNotice: Boolean = false, + isError: Boolean = false, + var isPrivate: Boolean = false ) { companion object { var DEFAULT_COLOR = Constants.EMPTY diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index cd6721c..037d504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -34,5 +34,5 @@ package net.thauvin.erik.mobibot.msg * The `NoticeMessage` class. */ class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isNotice = true) + Message(msg, color, isNotice = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 3033d1a..b424fdf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg * The `PrivateMessage` class. */ class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isPrivate = true) + Message(msg, color, isPrivate = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index 32e670a..b594670 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -76,8 +76,8 @@ abstract class SocialModule : AbstractModule() { post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn( - "Failed to post entry ${index.toLinkLabel()} on $name.", - e + "Failed to post entry ${index.toLinkLabel()} on $name.", + e ) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 5a8a638..3241bf0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -76,11 +76,11 @@ class AddonsTest { assertThat(addons.names.ops, "names.ops").containsExactly("cycle") assertThat(addons.names.commands, "names.command").containsExactly( - "joke", - "rock", - "paper", - "scissors", - "ignore" + "joke", + "rock", + "paper", + "scissors", + "ignore" ) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index be2deb3..a3994ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -46,9 +46,9 @@ object ExceptionSanitizer { with(this) { if (!cause?.message.isNullOrBlank()) { return ModuleException( - debugMessage, - cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), - this + debugMessage, + cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), + this ) } else if (!message.isNullOrBlank()) { return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 4ebb53c..87617e8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -68,7 +68,7 @@ class PinboardTest : LocalProperties() { private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { val response = - URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body matches.forEach { if (!response.contains(it)) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 8ddb013..7a3f5d2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -73,7 +73,7 @@ import java.util.* */ class UtilsTest { private val ascii = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" private val cal = Calendar.getInstance() private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) private val test = "This is a test." @@ -89,7 +89,7 @@ class UtilsTest { val sep = '/' val url = "https://erik.thauvin.net" assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") - .isEqualTo(dir + File.separatorChar) + .isEqualTo(dir + File.separatorChar) assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") } @@ -115,24 +115,24 @@ class UtilsTest { fun textCapitaliseWords() { assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") assertThat("Already Capitalized".capitalizeWords(), "already capitalized") - .isEqualTo("Already Capitalized") + .isEqualTo("Already Capitalized") assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") } @Test fun testColorize() { assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( - Colors.REVERSE + ascii + Colors.REVERSE + Colors.REVERSE + ascii + Colors.REVERSE ) assertThat(ascii.colorize(Colors.RED), "red.colorize()") - .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") - .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") - .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) + .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) } @Test @@ -164,19 +164,19 @@ class UtilsTest { fun testHelpCmdSyntax() { val bot = "mobibot" assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") - .isEqualTo("$bot: $test $bot $test") + .isEqualTo("$bot: $test $bot $test") assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") - .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") } @Test fun testHelpFormat() { assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") - .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") - .isEqualTo(test.prependIndent()) + .isEqualTo(test.prependIndent()) assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") - .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) + .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) } @@ -218,15 +218,15 @@ class UtilsTest { val search = arrayOf("one", "two", "three") val replace = arrayOf("1", "2", "3") assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") - .isEqualTo(replace.joinToString(",")) + .isEqualTo(replace.joinToString(",")) assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") - .isEqualTo(test.replace("t", "").replace("e", "E")) + .isEqualTo(test.replace("t", "").replace("e", "E")) assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") - .isEqualTo(test) + .isEqualTo(test) } @Test @@ -258,7 +258,7 @@ class UtilsTest { @Test fun testUnescapeXml() { assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( - "<a name=\"test & ''\">" + "<a name=\"test & ''\">" ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 1f28049..265009b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -40,14 +40,14 @@ class InfoTest { @Test(groups = ["commands"]) fun testToUptime() { assertThat( - 547800300076L.toUptime(), - "upTime(full)" + 547800300076L.toUptime(), + "upTime(full)" ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") assertThat( - 1320300000L.toUptime(), - "upTime(weeks days hours minutes)" + 1320300000L.toUptime(), + "upTime(weeks days hours minutes)" ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 5f1a690..f1fbe11 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -48,13 +48,13 @@ class RecapTest { assertThat(Recap.recaps, "Recap.recaps").all { size().isEqualTo(Recap.MAX_RECAPS) prop(MutableList<String>::first) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) prop(MutableList<String>::last) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) } Recap.storeRecap("sender", "test action", true) assertThat(Recap.recaps.last()) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 8fcbd8b..8e49b5e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -47,8 +47,8 @@ class LinksManagerTest { fun fetchTitle() { assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") assertThat( - linksManager.fetchTitle("https://www.google.com/foo"), - "fetchTitle(Foo)" + linksManager.fetchTitle("https://www.google.com/foo"), + "fetchTitle(Foo)" ).isEqualTo(Constants.NO_TITLE) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index 0853a9d..c28090d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -45,14 +45,14 @@ class ViewTest { for (i in 1..10) { LinksManager.entries.links.add( - EntryLink( - "https://www.example.com/$i", - "Example $i", - "nick$i", - "login$i", - "#channel", - emptyList() - ) + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index a09ebb9..6eef16e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -46,14 +46,14 @@ class EntriesUtilsTest { private val links = buildList { for (i in 0..5) { add( - EntryLink( - "https://www.mobitopia.org/$i", - "Mobitopia$i", - "Skynx$i", - "JimH$i", - "#mobitopia$i", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) + EntryLink( + "https://www.mobitopia.org/$i", + "Mobitopia$i", + "Skynx$i", + "JimH$i", + "#mobitopia$i", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) ) } } @@ -67,7 +67,7 @@ class EntriesUtilsTest { fun printLinkTest() { for (i in links.indices) { assertThat( - printLink(i - 1, links[i]), "link $i" + printLink(i - 1, links[i]), "link $i" ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") } @@ -79,7 +79,7 @@ class EntriesUtilsTest { fun printTagsTest() { for (i in links.indices) { assertThat( - printTags(i - 1, links[i]), "tag $i" + printTags(i - 1, links[i]), "tag $i" ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 4c20525..ab3feee 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -48,8 +48,8 @@ import java.util.* */ class EntryLinkTest { private val entryLink = EntryLink( - "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") ) @Test(groups = ["entries"]) @@ -117,12 +117,12 @@ class EntryLinkTest { entryLink.setTags("+mobitopia") entryLink.setTags("-mobitopia") assertThat( - entryLink.formatTags(","), - "formatTags(',')" + entryLink.formatTags(","), + "formatTags(',')" ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") entryLink.setTags("-tag4 tag5") assertThat( - entryLink.formatTags(" ", ","), "formatTag(' ',',')" + entryLink.formatTags(" ", ","), "formatTag(' ',',')" ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") val size = entryLink.tags.size entryLink.setTags("") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index fa50fcb..66fb98d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -42,21 +42,21 @@ class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } - .isInstanceOf(ModuleException::class.java) - .hasNoCause() + .isInstanceOf(ModuleException::class.java) + .hasNoCause() } @Test(groups = ["modules", "no-ci"]) fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") assertThat( - ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 4ee9668..5375784 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -49,7 +49,7 @@ import org.testng.annotations.Test /** * The `CurrencyConvertTest` class. */ -class CurrencyConverterTest: LocalProperties() { +class CurrencyConverterTest : LocalProperties() { @BeforeClass @Throws(ModuleException::class) @@ -62,22 +62,22 @@ class CurrencyConverterTest: LocalProperties() { fun testConvertCurrency() { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) assertThat( - convertCurrency(apiKey,"100 USD to EUR").msg, - "convertCurrency(100 USD to EUR)" + convertCurrency(apiKey, "100 USD to EUR").msg, + "convertCurrency(100 USD to EUR)" ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) assertThat( - convertCurrency(apiKey,"1 USD to GBP").msg, - "convertCurrency(1 USD to BGP)" + convertCurrency(apiKey, "1 USD to GBP").msg, + "convertCurrency(1 USD to BGP)" ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) assertThat( - convertCurrency(apiKey,"100,000.00 CAD to USD").msg, - "convertCurrency(100,000.00 GBP to USD)" + convertCurrency(apiKey, "100,000.00 CAD to USD").msg, + "convertCurrency(100,000.00 GBP to USD)" ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) - assertThat(convertCurrency(apiKey,"100 USD to USD"), "convertCurrency(100 USD to USD)").all { + assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") isInstanceOf(PublicMessage::class.java) } - assertThat(convertCurrency(apiKey,"100 USD"), "convertCurrency(100 USD)").all { + assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all { prop(Message::msg).contains("Invalid query.") isInstanceOf(ErrorMessage::class.java) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 4225e3b..cdc04f0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -42,12 +42,12 @@ class DiceTest { fun testRoll() { assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") assertThat(Dice.roll(2, 1), "roll(2d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") assertThat(Dice.roll(5, 1), "roll(5d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") assertThat(Dice.roll(2, 6), "roll(2d6)") - .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) + .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) assertThat(Dice.roll(3, 7), "roll(3d7)") - .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) + .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 640a721..fa50f8c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -48,19 +48,19 @@ class GoogleSearchTest : LocalProperties() { @Test(groups = ["modules"]) fun testAPIKeys() { assertThat( - searchGoogle("", "apikey", "cssKey").first(), - "searchGoogle(empty)" + searchGoogle("", "apikey", "cssKey").first(), + "searchGoogle(empty)" ).isInstanceOf(ErrorMessage::class.java) assertFailure { searchGoogle("test", "", "apiKey") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "cssKey") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("API key not valid. Please pass a valid API key.") + .isInstanceOf(ModuleException::class.java) + .hasMessage("API key not valid. Please pass a valid API key.") } @Test(groups = ["no-ci", "modules"]) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 84f9375..34f778a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -42,13 +42,13 @@ class MastodonTest : LocalProperties() { fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" assertThat( - toot( - getProperty(Mastodon.ACCESS_TOKEN_PROP), - getProperty(Mastodon.INSTANCE_PROP), - getProperty(Mastodon.HANDLE_PROP), - msg, - true - ) + toot( + getProperty(Mastodon.ACCESS_TOKEN_PROP), + getProperty(Mastodon.INSTANCE_PROP), + getProperty(Mastodon.HANDLE_PROP), + msg, + true + ) ).contains(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index b36285b..db68280 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -44,58 +44,58 @@ import java.lang.reflect.Method */ class ModuleExceptionTest { companion object { - const val debugMessage = "debugMessage" - const val message = "message" + const val DEBUG_MESSAGE = "debugMessage" + const val MESSAGE = "message" } @DataProvider(name = "dp") fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> { return arrayOf( - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(debugMessage, message)) + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) ) } @Test(dataProvider = "dp") fun testGetDebugMessage(e: ModuleException) { - assertThat(e::debugMessage).isEqualTo(debugMessage) + assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) } @Test(dataProvider = "dp") fun testGetMessage(e: ModuleException) { - assertThat(e).hasMessage(message) + assertThat(e).hasMessage(MESSAGE) } @Test(groups = ["modules"]) fun testSanitizeMessage() { val apiKey = "1234567890" - var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) + var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) assertThat( - e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" + e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" ).isNotNull().all { contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") doesNotContain(apiKey, "me") } - e = ModuleException(debugMessage, message, null) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(message) + e = ModuleException(DEBUG_MESSAGE, MESSAGE, null) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE) - e = ModuleException(debugMessage, message, IOException()) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(message) + e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException()) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE) - e = ModuleException(debugMessage, apiKey) + e = ModuleException(DEBUG_MESSAGE, apiKey) assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() - .doesNotContain(apiKey) + .doesNotContain(apiKey) val msg: String? = null - e = ModuleException(debugMessage, msg, IOException(msg)) + e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg)) assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull() - e = ModuleException(debugMessage, msg, IOException("foo is $apiKey")) + e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey")) assertThat( - e.sanitize(" ", apiKey, "foo").message, - "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" + e.sanitize(" ", apiKey, "foo").message, + "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" ).isNotNull().all { doesNotContain(apiKey) endsWith("xxx is xxxxxxxxxx") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 17e5b92..aff4188 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -60,7 +60,7 @@ class StockQuoteTest : LocalProperties() { assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) - .matches(buildMatch("Previous").toRegex()) + .matches(buildMatch("Previous").toRegex()) assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) symbol = "blahfoo" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 281d8af..ae1722d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -45,11 +45,11 @@ class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules"]) fun testAppId() { assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("Error 1: Invalid appid") + .isInstanceOf(ModuleException::class.java) + .hasMessage("Error 1: Invalid appid") assertFailure { queryWolfram("1 gallon to liter", appId = "") } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } @Test(groups = ["modules", "no-ci"]) @@ -62,8 +62,8 @@ class WolframAlphaTest : LocalProperties() { query = "SFO to LAX" assertThat( - queryWolfram(query, WolframAlpha.METRIC, apiKey), - "queryWolfram($query)" + queryWolfram(query, WolframAlpha.METRIC, apiKey), + "queryWolfram($query)" ).contains("kilometers") } catch (e: ModuleException) { // Avoid displaying api key in CI logs diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 29f5589..3602a27 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -49,9 +49,9 @@ class WordTimeTest { @Test(groups = ["modules"]) fun testTime() { assertThat(time(), "time()").matches( - ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + - "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + - "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() + ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + + "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + + "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() ) assertThat(time(""), "time()").endsWith("Los Angeles".bold()) assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) From 1cd7c5a79e1b38d5f582a051bd2e20538b1d9edf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Nov 2023 22:10:50 -0700 Subject: [PATCH 736/842] Upgraded to Kotlin 1.9.20 --- .idea/kotlinc.xml | 2 +- build.gradle | 8 ++++---- config/detekt/baseline.xml | 17 ++++------------- version.properties | 6 +++--- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index f8467b4..e805548 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="KotlinJpsPluginSettings"> - <option name="version" value="1.9.10" /> + <option name="version" value="1.9.20" /> </component> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9fb85c6..6507e20 100644 --- a/build.gradle +++ b/build.gradle @@ -7,11 +7,11 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.49.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.23.1' + id 'io.gitlab.arturbosch.detekt' version '1.23.3' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.9.10' - id 'org.jetbrains.kotlin.kapt' version '1.9.10' + id 'org.jetbrains.kotlin.jvm' version '1.9.20' + id 'org.jetbrains.kotlin.kapt' version '1.9.20' id 'org.jetbrains.kotlinx.kover' version '0.7.4' id 'org.sonarqube' version '4.4.1.3373' id 'pmd' @@ -55,7 +55,7 @@ dependencies { // Commons (mostly for PircBotX) implementation 'org.apache.commons:commons-lang3:3.13.0' - implementation 'org.apache.commons:commons-text:1.10.0' + implementation 'org.apache.commons:commons-text:1.11.0' implementation 'commons-codec:commons-codec:1.16.0' implementation 'commons-net:commons-net:3.10.0' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 9f157cf..5c6be73 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,9 +2,9 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> - <ID>CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID> <ID>CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>LongMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>LongMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID> <ID>LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) fun main(args: Array<String>)</ID> <ID>LongMethod:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID> <ID>LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> @@ -46,15 +46,6 @@ <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> - <ID>MaxLineLength:DiceTest.kt$DiceTest$.</ID> - <ID>MaxLineLength:Lookup.kt$Lookup$("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")</ID> - <ID>MaxLineLength:Mastodon.kt$Mastodon.Companion$mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct")</ID> - <ID>MaxLineLength:Mobibot.kt$Mobibot$helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent)</ID> - <ID>MaxLineLength:PinboardTest.kt$PinboardTest$URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body</ID> - <ID>MaxLineLength:StockQuote.kt$StockQuote.Companion$+</ID> - <ID>MaxLineLength:Utils.kt$Utils$list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = "")</ID> - <ID>MaxLineLength:View.kt$View$helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent)</ID> - <ID>MaxLineLength:Weather2.kt$Weather2.Companion$country.name.replace('_', ' ').capitalizeWords()</ID> <ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID> <ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID> <ID>NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID> @@ -62,8 +53,8 @@ <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>)</ID> - <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> - <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String</ID> + <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message></ID> <ID>NestedBlockDepth:LinksManager.kt$LinksManager$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> diff --git a/version.properties b/version.properties index c73da9e..9c446af 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Oct 26 20:43:39 PDT 2023 -version.buildmeta=20231026204339 +#Wed Nov 01 22:09:32 PDT 2023 +version.buildmeta=20231101220932 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231026204339 +version.semver=0.8.0-rc+20231101220932 From c68c25aa9504a4217cb9495b8086ea9d795a5aa6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 10 Nov 2023 23:43:20 -0800 Subject: [PATCH 737/842] Moved from Gradle to bld --- .circleci/config.yml | 42 +- .github/workflows/gradle.yml | 48 +- .gitignore | 150 +++--- .gitlab-ci.yml | 31 +- .idea/app.iml | 30 ++ .idea/bld.iml | 14 + .idea/codeStyles/Project.xml | 298 ------------ .idea/codeStyles/codeStyleConfig.xml | 6 - .idea/compiler.xml | 6 - ...{Erik_s_Copyright_Notice.xml => BSD_3.xml} | 4 +- .idea/copyright/profiles_settings.xml | 2 +- .idea/inspectionProfiles/Project_Default.xml | 64 --- .idea/jarRepositories.xml | 45 -- .idea/kotlinc.xml | 6 - .idea/libraries/bld.xml | 17 + .idea/libraries/compile.xml | 13 + .idea/libraries/runtime.xml | 14 + .idea/libraries/test.xml | 14 + .idea/misc.xml | 18 +- .idea/modules.xml | 9 + .idea/runConfigurations/Run Tests.xml | 9 + .vscode/launch.json | 11 + .vscode/settings.json | 15 + README.md | 6 +- bin/main/log4j2.xml | 69 +++ bin/main/net/thauvin/erik/mobibot/Addons.kt | 190 ++++++++ .../net/thauvin/erik/mobibot/Constants.kt | 102 ++++ .../net/thauvin/erik/mobibot/FeedReader.kt | 92 ++++ bin/main/net/thauvin/erik/mobibot/Mobibot.kt | 421 +++++++++++++++++ bin/main/net/thauvin/erik/mobibot/Pinboard.kt | 113 +++++ bin/main/net/thauvin/erik/mobibot/Utils.kt | 439 ++++++++++++++++++ .../erik/mobibot/commands/AbstractCommand.kt | 79 ++++ .../erik/mobibot/commands/ChannelFeed.kt | 62 +++ .../thauvin/erik/mobibot/commands/Cycle.kt | 66 +++ .../net/thauvin/erik/mobibot/commands/Die.kt | 62 +++ .../thauvin/erik/mobibot/commands/Ignore.kt | 147 ++++++ .../net/thauvin/erik/mobibot/commands/Info.kt | 124 +++++ .../net/thauvin/erik/mobibot/commands/Me.kt | 51 ++ .../thauvin/erik/mobibot/commands/Modules.kt | 63 +++ .../net/thauvin/erik/mobibot/commands/Msg.kt | 60 +++ .../net/thauvin/erik/mobibot/commands/Nick.kt | 51 ++ .../thauvin/erik/mobibot/commands/Recap.kt | 81 ++++ .../net/thauvin/erik/mobibot/commands/Say.kt | 51 ++ .../thauvin/erik/mobibot/commands/Users.kt | 50 ++ .../thauvin/erik/mobibot/commands/Versions.kt | 59 +++ .../erik/mobibot/commands/links/Comment.kt | 151 ++++++ .../mobibot/commands/links/LinksManager.kt | 207 +++++++++ .../erik/mobibot/commands/links/Posting.kt | 164 +++++++ .../erik/mobibot/commands/links/Tags.kt | 87 ++++ .../erik/mobibot/commands/links/View.kt | 120 +++++ .../mobibot/commands/seen/NickComparator.kt | 45 ++ .../erik/mobibot/commands/seen/Seen.kt | 150 ++++++ .../erik/mobibot/commands/seen/SeenNick.kt | 41 ++ .../erik/mobibot/commands/tell/Tell.kt | 306 ++++++++++++ .../erik/mobibot/commands/tell/TellManager.kt | 74 +++ .../erik/mobibot/commands/tell/TellMessage.kt | 104 +++++ .../thauvin/erik/mobibot/entries/Entries.kt | 54 +++ .../erik/mobibot/entries/EntriesUtils.kt | 83 ++++ .../erik/mobibot/entries/EntryComment.kt | 52 +++ .../thauvin/erik/mobibot/entries/EntryLink.kt | 213 +++++++++ .../erik/mobibot/entries/FeedsManager.kt | 187 ++++++++ .../erik/mobibot/modules/AbstractModule.kt | 131 ++++++ .../net/thauvin/erik/mobibot/modules/Calc.kt | 87 ++++ .../thauvin/erik/mobibot/modules/ChatGpt.kt | 176 +++++++ .../erik/mobibot/modules/CryptoPrices.kt | 159 +++++++ .../erik/mobibot/modules/CurrencyConverter.kt | 222 +++++++++ .../net/thauvin/erik/mobibot/modules/Dice.kt | 87 ++++ .../erik/mobibot/modules/GoogleSearch.kt | 162 +++++++ .../net/thauvin/erik/mobibot/modules/Joke.kt | 105 +++++ .../thauvin/erik/mobibot/modules/Lookup.kt | 171 +++++++ .../thauvin/erik/mobibot/modules/Mastodon.kt | 149 ++++++ .../erik/mobibot/modules/ModuleException.kt | 45 ++ .../net/thauvin/erik/mobibot/modules/Ping.kt | 83 ++++ .../erik/mobibot/modules/RockPaperScissors.kt | 114 +++++ .../erik/mobibot/modules/StockQuote.kt | 236 ++++++++++ .../thauvin/erik/mobibot/modules/War.class | Bin 0 -> 2452 bytes .../thauvin/erik/mobibot/modules/Weather2.kt | 250 ++++++++++ .../erik/mobibot/modules/WolframAlpha.kt | 142 ++++++ .../thauvin/erik/mobibot/modules/WorldTime.kt | 390 ++++++++++++++++ .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 37 ++ .../net/thauvin/erik/mobibot/msg/Message.kt | 65 +++ .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 38 ++ .../erik/mobibot/msg/PrivateMessage.kt | 37 ++ .../thauvin/erik/mobibot/msg/PublicMessage.kt | 36 ++ .../erik/mobibot/social/SocialManager.kt | 116 +++++ .../erik/mobibot/social/SocialModule.kt | 96 ++++ .../erik/mobibot/social/SocialTimer.kt | 40 ++ bin/test/current.xml | 67 +++ .../net/thauvin/erik/mobibot/AddonsTest.kt | 86 ++++ .../erik/mobibot/ExceptionSanitizer.kt | 60 +++ .../thauvin/erik/mobibot/FeedReaderTest.kt | 78 ++++ .../thauvin/erik/mobibot/LocalProperties.kt | 85 ++++ .../net/thauvin/erik/mobibot/PinboardTest.kt | 81 ++++ .../net/thauvin/erik/mobibot/UtilsTest.kt | 278 +++++++++++ .../thauvin/erik/mobibot/commands/InfoTest.kt | 58 +++ .../erik/mobibot/commands/RecapTest.kt | 60 +++ .../commands/links/LinksManagerTest.kt | 77 +++ .../erik/mobibot/commands/links/ViewTest.kt | 111 +++++ .../erik/mobibot/commands/seen/SeenTest.kt | 85 ++++ .../mobibot/commands/tell/TellMessageTest.kt | 72 +++ .../commands/tell/TellMessagesMgrTest.kt | 87 ++++ .../erik/mobibot/entries/EntriesUtilsTest.kt | 91 ++++ .../erik/mobibot/entries/EntryLinkTest.kt | 133 ++++++ .../erik/mobibot/entries/FeedMgrTest.kt | 115 +++++ .../thauvin/erik/mobibot/modules/CalcTest.kt | 53 +++ .../erik/mobibot/modules/ChatGptTest.kt | 62 +++ .../erik/mobibot/modules/CryptoPricesTest.kt | 78 ++++ .../mobibot/modules/CurrencyConverterTest.kt | 85 ++++ .../thauvin/erik/mobibot/modules/DiceTest.kt | 53 +++ .../erik/mobibot/modules/GoogleSearchTest.kt | 95 ++++ .../thauvin/erik/mobibot/modules/JokeTest.kt | 57 +++ .../erik/mobibot/modules/LookupTest.kt | 60 +++ .../erik/mobibot/modules/MastodonTest.kt | 54 +++ .../mobibot/modules/ModuleExceptionTest.kt | 105 +++++ .../thauvin/erik/mobibot/modules/PingTest.kt | 54 +++ .../mobibot/modules/RockPaperScissorsTest.kt | 50 ++ .../erik/mobibot/modules/StockQuoteTest.kt | 85 ++++ .../erik/mobibot/modules/Weather2Test.kt | 117 +++++ .../erik/mobibot/modules/WolframAlphaTest.kt | 77 +++ .../erik/mobibot/modules/WordTimeTest.kt | 70 +++ .../thauvin/erik/mobibot/msg/MessageTest.kt | 109 +++++ bitbucket-pipelines.yml | 9 +- bld | 2 + bld.bat | 4 + build.gradle | 247 ---------- deploy.sh | 2 +- gradle.properties | 0 gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 7 - gradlew | 249 ---------- gradlew.bat | 92 ---- lib/bld/bld-wrapper.jar | Bin 0 -> 27321 bytes lib/bld/bld-wrapper.properties | 10 + release_info.txt | 27 ++ settings.gradle | 18 - sonar-project.properties | 7 + .../java/net/thauvin/erik/MobibotBuild.java | 170 +++++++ .../net/thauvin/erik/mobibot/modules/War.java | 108 ----- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 6 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 27 ++ .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 4 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 2 +- .../erik/mobibot/modules/CryptoPrices.kt | 4 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 89 ++++ .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 6 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- src/main/resources/log4j2.xml | 38 ++ .../net/thauvin/erik/mobibot/AddonsTest.kt | 4 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 44 ++ .../erik/mobibot/DisableOnCiCondition.kt | 51 ++ .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 4 +- .../thauvin/erik/mobibot/LocalProperties.kt | 6 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 8 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 8 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 6 +- .../erik/mobibot/commands/RecapTest.kt | 6 +- .../commands/links/LinksManagerTest.kt | 10 +- .../erik/mobibot/commands/links/ViewTest.kt | 6 +- .../erik/mobibot/commands/seen/SeenTest.kt | 52 ++- .../mobibot/commands/tell/TellMessageTest.kt | 6 +- .../commands/tell/TellMessagesMgrTest.kt | 30 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 12 +- .../erik/mobibot/entries/EntryLinkTest.kt | 12 +- .../erik/mobibot/entries/FeedMgrTest.kt | 10 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 6 +- .../erik/mobibot/modules/ChatGptTest.kt | 10 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 13 +- .../mobibot/modules/CurrencyConverterTest.kt | 12 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 6 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 10 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 6 +- .../erik/mobibot/modules/LookupTest.kt | 8 +- .../erik/mobibot/modules/MastodonTest.kt | 6 +- .../mobibot/modules/ModuleExceptionTest.kt | 34 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 8 +- .../mobibot/modules/RockPaperScissorsTest.kt | 6 +- .../erik/mobibot/modules/StockQuoteTest.kt | 6 +- .../erik/mobibot/modules/Weather2Test.kt | 12 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 10 +- .../erik/mobibot/modules/WordTimeTest.kt | 8 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 4 +- src/test/resources/current.xml | 31 -- version.mustache | 32 -- version.properties | 9 - 240 files changed, 11508 insertions(+), 1650 deletions(-) create mode 100644 .idea/app.iml create mode 100644 .idea/bld.iml delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/compiler.xml rename .idea/copyright/{Erik_s_Copyright_Notice.xml => BSD_3.xml} (93%) delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/kotlinc.xml create mode 100644 .idea/libraries/bld.xml create mode 100644 .idea/libraries/compile.xml create mode 100644 .idea/libraries/runtime.xml create mode 100644 .idea/libraries/test.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations/Run Tests.xml create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 bin/main/log4j2.xml create mode 100644 bin/main/net/thauvin/erik/mobibot/Addons.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Constants.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/FeedReader.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Mobibot.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Pinboard.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Utils.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Die.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Info.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Me.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Modules.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Msg.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Nick.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Recap.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Say.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Users.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Versions.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/View.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/Entries.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Calc.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Dice.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Joke.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Ping.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/War.class create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/Message.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt create mode 100644 bin/test/current.xml create mode 100644 bin/test/net/thauvin/erik/mobibot/AddonsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/LocalProperties.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/PinboardTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/UtilsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt create mode 100755 bld create mode 100644 bld.bat delete mode 100644 build.gradle delete mode 100644 gradle.properties delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100755 gradlew delete mode 100644 gradlew.bat create mode 100644 lib/bld/bld-wrapper.jar create mode 100644 lib/bld/bld-wrapper.properties create mode 100644 release_info.txt delete mode 100644 settings.gradle create mode 100644 sonar-project.properties create mode 100644 src/bld/java/net/thauvin/erik/MobibotBuild.java delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/War.java create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt create mode 100644 src/main/resources/log4j2.xml create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt delete mode 100644 version.mustache delete mode 100644 version.properties diff --git a/.circleci/config.yml b/.circleci/config.yml index 1868b37..77889be 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,49 +6,39 @@ defaults: &defaults TERM: dumb CI_NAME: "CircleCI" -defaults_gradle: &defaults_gradle +defaults_gradle: &defaults_bld steps: - checkout - - restore_cache: - keys: - - gradle-dependencies-{{ checksum "build.gradle" }} - # fallback to using the latest cache if no exact match is found - - gradle-dependencies- - run: - name: Gradle Dependencies - command: ./gradlew dependencies - - save_cache: - paths: - - ~/.m2 - key: gradle-dependencies-{{ checksum "build.gradle" }} + name: Download the bld dependencies + command: ./bld download - run: - name: Run All Checks - command: ./gradlew check - - store_artifacts: - path: build/reports/ - destination: reports - - store_test_results: - path: build/reports/ + name: Compile source with bld + command: ./bld compile + - run: + name: Run tests with bld + command: ./bld test jobs: - build_gradle_jdk17: + bld_jdk17: <<: *defaults docker: - image: cimg/openjdk:17.0 - <<: *defaults_gradle + <<: *defaults_bld - build_gradle_jdk19: + bld_jdk20: <<: *defaults docker: - - image: cimg/openjdk:19.0 + - image: cimg/openjdk:20.0 - <<: *defaults_gradle + <<: *defaults_bld workflows: version: 2 - gradle: + bld: jobs: - - build_gradle_jdk17 + - bld_jdk17 + - bld_jdk20 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index aa034f9..ae6c66f 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,21 +1,21 @@ -name: gradle-ci +name: bld-ci on: [ push, pull_request, workflow_dispatch ] jobs: - build: + build-bld-project: runs-on: ubuntu-latest env: - GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m" - SONAR_JDK: "17" + COVERAGE_SDK: "17" strategy: matrix: java-version: [ 17, 20 ] steps: - - uses: actions/checkout@v3 + - name: Checkout source repository + uses: actions/checkout@v3 with: fetch-depth: 0 @@ -25,35 +25,21 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java-version }} - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - name: Grant bld execute permission + run: chmod +x bld - - name: Cache SonarCloud packages - if: matrix.java-version == env.SONAR_JDK - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar + - name: Download the bld dependencies + run: ./bld download - - name: Test with Gradle - uses: gradle/gradle-build-action@v2 - env: - CI_NAME: "GitHub CI" - ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} - CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} - OWM_API_KEY: ${{ secrets.OWM_API_KEY }} - PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} - MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} - MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} - MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} - EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} - with: - arguments: build check --stacktrace + - name: Compile source with bld + run: ./bld compile - - name: SonarCloud - if: success() && matrix.java-version == env.SONAR_JDK + - name: Run tests with bld + run: ./bld jacoco + + - name: SonarCloud Scan + uses: sonarsource/sonarcloud-github-action@master + if: success() && matrix.java-version == env.COVERAGE_SDK env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew sonar --info diff --git a/.gitignore b/.gitignore index 970cefd..fa550e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,89 +1,61 @@ -!.vscode/extensions.json -!.vscode/launch.json -!.vscode/settings.json -!.vscode/tasks.json -!gradle-wrapper.jar -!properties/* -*.class -*.code-workspace -*.ctxt -*.ear -*.iws -*.jar -*.log -*.nar -*.rar -*.sublime-* -*.tar.gz -*.war -*.zip -.DS_Store -.classpath -.gradle -.history -.idea/**/caches/build_file_checksums.ser -.idea/**/contentModel.xml -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/dataSources/ -.idea/**/dbnavigator.xml -.idea/**/dictionaries -.idea/**/dynamic.xml -.idea/**/gradle.xml -.idea/**/httpRequests -.idea/**/libraries -.idea/**/mongoSettings.xml -.idea/**/replstate.xml -.idea/**/shelf -.idea/**/shelf/ -.idea/**/sonarlint* -.idea/**/sqlDataSources.xml -.idea/**/tasks.xml -.idea/**/uiDesigner.xml -.idea/**/usage.statistics.xml -.idea/**/workspace.xml -.idea_modules/ -.kobalt -.mtj.tmp/ -.mvn/timing.properties -.mvn/wrapper/maven-wrapper.jar -.nb-gradle -.project -.scannerwork -.settings -.vscode/* -Thumbs.db -__pycache__ -atlassian-ide-plugin.xml -bin/ -build/ -cmake-build-*/ -com_crashlytics_export_strings.xml -crashlytics-build.properties -crashlytics.properties -dependency-reduced-pom.xml -deploy/ -dist/ -ehthumbs.db -fabric.properties -gen/ -hs_err_pid* -kobaltBuild -kobaltw*-test -lib/kotlin* -libs/ -local.properties -log4j2.xml -logs/ -mobibot.properties -out/ -pom.xml.next -pom.xml.releaseBackup -pom.xml.tag -pom.xml.versionsBackup -proguard-project.txt -project.properties -release.properties -target/ -test-output -venv +.gradle +.DS_Store +build +lib/bld/** +!lib/bld/bld-wrapper.jar +!lib/bld/bld-wrapper.properties +lib/compile/ +lib/runtime/ +lib/standalone/ +lib/test/ + +# IDEA ignores + +# User-specific +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Editor-based Rest Client +.idea/httpRequests + +local.properties + +deploy +logs +mobibot.properties diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 48a3396..052df48 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,32 +1,11 @@ -image: gradle:8-jdk17 - -variables: - GRADLE_OPTS: "-Dorg.gradle.daemon=false" - CI_NAME: "GitLab CI" - -before_script: - - export GRADLE_USER_HOME=`pwd`/.gradle +image: openjdk:17 stages: - - build - test -build: - stage: build - script: gradle --build-cache assemble - cache: - key: "$CI_COMMIT_REF_NAME" - policy: push - paths: - - build - - .gradle - test: stage: test - script: gradle check - cache: - key: "$CI_COMMIT_REF_NAME" - policy: pull - paths: - - build - - .gradle + script: + - ./bld download + - ./bld compile + - ./bld test diff --git a/.idea/app.iml b/.idea/app.iml new file mode 100644 index 0000000..2c1fe21 --- /dev/null +++ b/.idea/app.iml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> + <output url="file://$MODULE_DIR$/build/main" /> + <output-test url="file://$MODULE_DIR$/build/test" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" relativeOutputPath="resources" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library" scope="RUNTIME"> + <library> + <CLASSES> + <root url="file://$MODULE_DIR$/src/main/resources/templates" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="library" name="compile" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="runtime" level="project" /> + <orderEntry type="library" scope="TEST" name="test" level="project" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/bld.iml b/.idea/bld.iml new file mode 100644 index 0000000..e63e11e --- /dev/null +++ b/.idea/bld.iml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> + <output url="file://$MODULE_DIR$/build/bld" /> + <output-test url="file://$MODULE_DIR$/build/bld" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/src/bld"> + <sourceFolder url="file://$MODULE_DIR$/src/bld/java" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="bld" level="project" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 10aa334..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,298 +0,0 @@ -<component name="ProjectCodeStyleConfiguration"> - <code_scheme name="Project" version="200"> - <JetCodeStyleSettings> - <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> - </JetCodeStyleSettings> - <codeStyleSettings language="JAVA"> - <option name="IF_BRACE_FORCE" value="1" /> - <arrangement> - <groups> - <group> - <type>OVERRIDDEN_METHODS</type> - <order>BY_NAME</order> - </group> - </groups> - <rules> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PUBLIC>true</PUBLIC> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PROTECTED>true</PROTECTED> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PRIVATE>true</PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PUBLIC>true</PUBLIC> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PROTECTED>true</PROTECTED> - <STATIC>true</STATIC> - <visibility /> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>true</PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> - <STATIC>true</STATIC> - </AND> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PUBLIC>true</PUBLIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PROTECTED>true</PROTECTED> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PRIVATE>true</PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PUBLIC>true</PUBLIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PROTECTED>true</PROTECTED> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>true</PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <FIELD>true</FIELD> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> - </match> - </rule> - </section> - <section> - <rule> - <match> - <CONSTRUCTOR>true</CONSTRUCTOR> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <METHOD>true</METHOD> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <METHOD>true</METHOD> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <ENUM>true</ENUM> - </match> - </rule> - </section> - <section> - <rule> - <match> - <INTERFACE>true</INTERFACE> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <CLASS>true</CLASS> - <STATIC>true</STATIC> - </AND> - </match> - </rule> - </section> - <section> - <rule> - <match> - <CLASS>true</CLASS> - </match> - </rule> - </section> - </rules> - </arrangement> - </codeStyleSettings> - <codeStyleSettings language="kotlin"> - <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> - </codeStyleSettings> - </code_scheme> -</component> \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 23f4bb5..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,6 +0,0 @@ -<component name="ProjectCodeStyleConfiguration"> - <state> - <option name="USE_PER_PROJECT_SETTINGS" value="true" /> - <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> - </state> -</component> \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b589d56..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CompilerConfiguration"> - <bytecodeTargetLevel target="17" /> - </component> -</project> \ No newline at end of file diff --git a/.idea/copyright/Erik_s_Copyright_Notice.xml b/.idea/copyright/BSD_3.xml similarity index 93% rename from .idea/copyright/Erik_s_Copyright_Notice.xml rename to .idea/copyright/BSD_3.xml index 055999a..275eca9 100644 --- a/.idea/copyright/Erik_s_Copyright_Notice.xml +++ b/.idea/copyright/BSD_3.xml @@ -1,6 +1,6 @@ <component name="CopyrightManager"> <copyright> - <option name="notice" value="&#36;file.fileName Copyright 2004-&#36;today.year Erik C. Thauvin (erik@thauvin.net) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> - <option name="myName" value="Erik's Copyright Notice" /> + <option name="notice" value="&#36;file.fileName Copyright 2021-&#36;today.year Erik C. Thauvin (erik@thauvin.net) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> + <option name="myName" value="BSD-3" /> </copyright> </component> \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml index 1419e40..3203074 100644 --- a/.idea/copyright/profiles_settings.xml +++ b/.idea/copyright/profiles_settings.xml @@ -1,3 +1,3 @@ <component name="CopyrightManager"> - <settings default="Erik's Copyright Notice" /> + <settings default="BSD-3" /> </component> \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 60682bf..1e01b48 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,72 +1,8 @@ <component name="InspectionProjectProfileManager"> <profile version="1.0"> <option name="myName" value="Project Default" /> - <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="TOP_LEVEL_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="INNER_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="METHOD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> - </value> - </option> - <option name="FIELD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="IGNORE_DEPRECATED" value="false" /> - <option name="IGNORE_JAVADOC_PERIOD" value="true" /> - <option name="IGNORE_DUPLICATED_THROWS" value="false" /> - <option name="IGNORE_POINT_TO_ITSELF" value="false" /> - <option name="myAdditionalJavadocTags" value="created" /> - </inspection_tool> <inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true"> <option name="ADDITIONAL_TAGS" value="created" /> </inspection_tool> - <inspection_tool class="MissingJavadoc" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="PACKAGE_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="MODULE_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="TOP_LEVEL_CLASS_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="INNER_CLASS_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="METHOD_SETTINGS"> - <Options> - <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="FIELD_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - </inspection_tool> </profile> </component> \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 0e29f96..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="RemoteRepositoriesConfiguration"> - <remote-repository> - <option name="id" value="central" /> - <option name="name" value="Maven Central repository" /> - <option name="url" value="https://repo1.maven.org/maven2" /> - </remote-repository> - <remote-repository> - <option name="id" value="jboss.community" /> - <option name="name" value="JBoss Community repository" /> - <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$/" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$/" /> - </remote-repository> - <remote-repository> - <option name="id" value="maven2" /> - <option name="name" value="maven2" /> - <option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenRepo" /> - <option name="name" value="MavenRepo" /> - <option name="url" value="https://repo.maven.apache.org/maven2/" /> - </remote-repository> - <remote-repository> - <option name="id" value="maven" /> - <option name="name" value="maven" /> - <option name="url" value="https://jitpack.io" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$PROJECT_DIR$/../../maven/repository/" /> - </remote-repository> - </component> -</project> \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index e805548..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="KotlinJpsPluginSettings"> - <option name="version" value="1.9.20" /> - </component> -</project> \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml new file mode 100644 index 0000000..cf75013 --- /dev/null +++ b/.idea/libraries/bld.xml @@ -0,0 +1,17 @@ +<component name="libraryTable"> + <library name="bld"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/bld" /> + <root url="jar://$USER_HOME$/.bld/dist/bld-1.7.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.bld/dist/bld-1.7.5-sources.jar!/" /> + </SOURCES> + <excluded> + <root url="jar://$PROJECT_DIR$/lib/bld/bld-wrapper.jar!/" /> + </excluded> + <jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml new file mode 100644 index 0000000..9bd86aa --- /dev/null +++ b/.idea/libraries/compile.xml @@ -0,0 +1,13 @@ +<component name="libraryTable"> + <library name="compile"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/compile" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$PROJECT_DIR$/lib/compile" /> + </SOURCES> + <jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml new file mode 100644 index 0000000..2ae5c4b --- /dev/null +++ b/.idea/libraries/runtime.xml @@ -0,0 +1,14 @@ +<component name="libraryTable"> + <library name="runtime"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/runtime" /> + <root url="file://$PROJECT_DIR$/src/main/resources" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$PROJECT_DIR$/lib/runtime" /> + </SOURCES> + <jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml new file mode 100644 index 0000000..b80486a --- /dev/null +++ b/.idea/libraries/test.xml @@ -0,0 +1,14 @@ +<component name="libraryTable"> + <library name="test"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/test" /> + <root url="file://$PROJECT_DIR$/src/test/resources" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$PROJECT_DIR$/lib/test" /> + </SOURCES> + <jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index a59e398..e853f87 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,16 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="FrameworkDetectionExcludesConfiguration"> - <file type="web" url="file://$PROJECT_DIR$" /> + <component name="EntryPointsManager"> + <pattern value="net.thauvin.erik.MobibotBuild" method="deploy" /> + <pattern value="net.thauvin.erik.MobibotBuild" method="jacoco" /> + <pattern value="net.thauvin.erik.MobibotBuild" /> </component> <component name="PDMPlugin"> - <option name="customRuleSets"> - <list> - <option value="K:\java\semver\config\pmd.xml" /> - <option value="$PROJECT_DIR$/../../java/bld-exec/config/pmd.xml" /> - </list> - </option> <option name="skipTestSources" value="false" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="19" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/build" /> + </component> </project> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..55adcb9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/app.iml" filepath="$PROJECT_DIR$/.idea/app.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/bld.iml" filepath="$PROJECT_DIR$/.idea/bld.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/.idea/runConfigurations/Run Tests.xml b/.idea/runConfigurations/Run Tests.xml new file mode 100644 index 0000000..37dc742 --- /dev/null +++ b/.idea/runConfigurations/Run Tests.xml @@ -0,0 +1,9 @@ +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="Run Tests" type="Application" factoryName="Application" nameIsGenerated="true"> + <option name="MAIN_CLASS_NAME" value="net.thauvin.erik.MobibotTest" /> + <module name="app" /> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c6500f2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Run Tests", + "request": "launch", + "mainClass": "net.thauvin.erik.MobibotTest" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..133aa45 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "java.project.sourcePaths": [ + "src/main/java", + "src/main/resources", + "src/test/java", + "src/bld/java" + ], + "java.configuration.updateBuildConfiguration": "automatic", + "java.project.referencedLibraries": [ + "${HOME}/.bld/dist/bld-1.7.5.jar", + "lib/compile/*.jar", + "lib/runtime/*.jar", + "lib/test/*.jar" + ] +} diff --git a/README.md b/README.md index 1ecefeb..fbcacc2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.10-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.20-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) @@ -14,8 +14,8 @@ Some very basic instructions: cd mobibot - # build with gradle - ./gradlew + # build JAR and deploy + ./bld jar deploy cd deploy diff --git a/bin/main/log4j2.xml b/bin/main/log4j2.xml new file mode 100644 index 0000000..265c88f --- /dev/null +++ b/bin/main/log4j2.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ log4j2.xml + ~ + ~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + ~ + ~ Redistribution and use in source and binary forms, with or without + ~ modification, are permitted provided that the following conditions are met: + ~ + ~ Redistributions of source code must retain the above copyright notice, this + ~ list of conditions and the following disclaimer. + ~ + ~ Redistributions in binary form must reproduce the above copyright notice, + ~ this list of conditions and the following disclaimer in the documentation + ~ and/or other materials provided with the distribution. + ~ + ~ Neither the name of this project nor the names of its contributors may be + ~ used to endorse or promote products derived from this software without + ~ specific prior written permission. + ~ + ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + ~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + ~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + ~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + ~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + ~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + ~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + --> + +<Configuration status="warn"> + <Appenders> + <Console name="stderr" target="SYSTEM_ERR"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> + </Console> + <Console name="input" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{UNIX_MILLIS} %msg%n"/> + <Filters> + <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/> + <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/> + </Filters> + </Console> + <Console name="output" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{UNIX_MILLIS} >>>%msg%n"/> + <Filters> + <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/> + <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/> + </Filters> + </Console> + </Appenders> + <Loggers> + <Root level="warn" additivity="false"> + <AppenderRef ref="stderr"/> + </Root> + <logger level="debug" name="org.pircbotx.InputParser" additivity="false"> + <appender-ref ref="input"/> + <appender-ref ref="stderr" level="warn"/> + </logger> + <logger level="debug" name="org.pircbotx.output.OutputRaw" additivity="false"> + <appender-ref ref="output"/> + <appender-ref ref="stderr" level="warn"/> + </logger> + <logger level="trace" name="net.thauvin.erik.mobibot" additivity="false"> + <appender-ref ref="stderr"/> + </logger> + </Loggers> +</Configuration> diff --git a/bin/main/net/thauvin/erik/mobibot/Addons.kt b/bin/main/net/thauvin/erik/mobibot/Addons.kt new file mode 100644 index 0000000..2c5f05d --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Addons.kt @@ -0,0 +1,190 @@ +/* + * Addons.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.Utils.notContains +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.LinksManager +import net.thauvin.erik.mobibot.modules.AbstractModule +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.util.* + +/** + * Modules and Commands addons. + */ +class Addons(private val props: Properties) { + private val logger: Logger = LoggerFactory.getLogger(Addons::class.java) + private val disabledModules = props.getProperty("disabled-modules", "").split(LinksManager.TAG_MATCH) + private val disableCommands = props.getProperty("disabled-commands", "").split(LinksManager.TAG_MATCH) + + val commands: MutableList<AbstractCommand> = mutableListOf() + val modules: MutableList<AbstractModule> = mutableListOf() + val names = Names + + /** + * Add a module with properties. + */ + fun add(module: AbstractModule): Boolean { + var enabled = false + with(module) { + if (disabledModules.notContains(name, true)) { + if (hasProperties()) { + propertyKeys.forEach { + setProperty(it, props.getProperty(it, "")) + } + } + + if (isEnabled) { + modules.add(this) + names.modules.add(name) + names.commands.addAll(commands) + enabled = true + } else { + if (logger.isDebugEnabled) { + logger.debug("Module $name is disabled.") + } + names.disabledModules.add(name) + } + } else { + names.disabledModules.add(name) + } + } + return enabled + } + + /** + * Add a command with properties. + */ + fun add(command: AbstractCommand): Boolean { + var enabled = false + with(command) { + if (disableCommands.notContains(name, true)) { + if (properties.isNotEmpty()) { + properties.keys.forEach { + setProperty(it, props.getProperty(it, "")) + } + } + if (isEnabled()) { + commands.add(this) + if (isVisible) { + if (isOpOnly) { + names.ops.add(name) + } else { + names.commands.add(name) + } + } + enabled = true + } else { + if (logger.isDebugEnabled) { + logger.debug("Command $name is disabled.") + } + names.disabledCommands.add(name) + } + } else { + names.disabledCommands.add(name) + } + } + return enabled + } + + /** + * Execute a command or module. + */ + fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean { + val cmds = if (event is PrivateMessageEvent) commands else commands.filter { it.isPublic } + for (command in cmds) { + if (command.name.startsWith(cmd)) { + command.commandResponse(channel, args, event) + return true + } + } + val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules + for (module in mods) { + if (module.commands.contains(cmd)) { + module.commandResponse(channel, cmd, args, event) + return true + } + } + return false + } + + /** + * Match a command. + */ + fun match(channel: String, event: GenericMessageEvent): Boolean { + for (command in commands) { + if (command.matches(event.message)) { + command.commandResponse(channel, event.message, event) + return true + } + } + return false + } + + /** + * Commands and Modules help. + */ + fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean { + for (command in commands) { + if (command.isVisible && command.name.startsWith(topic)) { + return command.helpResponse(channel, topic, event) + } + } + for (module in modules) { + if (module.commands.contains(topic)) { + return module.helpResponse(event) + } + } + return false + } + + /** + * Holds commands and modules names. + */ + object Names { + val modules: MutableList<String> = mutableListOf() + val disabledModules: MutableList<String> = mutableListOf() + val commands: MutableList<String> = mutableListOf() + val disabledCommands: MutableList<String> = mutableListOf() + val ops: MutableList<String> = mutableListOf() + + fun sort() { + modules.sort() + disabledModules.sort() + commands.sort() + disabledCommands.sort() + ops.sort() + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/Constants.kt b/bin/main/net/thauvin/erik/mobibot/Constants.kt new file mode 100644 index 0000000..98ef74a --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Constants.kt @@ -0,0 +1,102 @@ +/* + * Constants.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +/** + * The `Constants`. + */ +object Constants { + /** + * The connect/read timeout in ms. + */ + const val CONNECT_TIMEOUT = 5000 + + /** + * Debug command line argument. + */ + const val DEBUG_ARG = "debug" + + /** + * Default IRC Port. + */ + const val DEFAULT_PORT = 6667 + + /** + * Default IRC Server. + */ + const val DEFAULT_SERVER = "irc.libera.chat" + + /** + * CLI command for usage. + */ + const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar" + + /** + * User-Agent + */ + const val USER_AGENT = + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + + /** + * The help command. + */ + const val HELP_CMD = "help" + + /** + * The link command. + */ + const val LINK_CMD = "L" + + /** + * The empty title string. + */ + const val NO_TITLE = "No Title" + + /** + * Properties command line argument. + */ + const val PROPS_ARG = "properties" + + /** + * The tag command + */ + const val TAG_CMD = "T" + + /** + * The timer delay in minutes. + */ + const val TIMER_DELAY = 10L + + /** + * Properties version line argument. + */ + const val VERSION_ARG = "version" +} diff --git a/bin/main/net/thauvin/erik/mobibot/FeedReader.kt b/bin/main/net/thauvin/erik/mobibot/FeedReader.kt new file mode 100644 index 0000000..d82f011 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/FeedReader.kt @@ -0,0 +1,92 @@ +/* + * FeedReader.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import com.rometools.rome.io.FeedException +import com.rometools.rome.io.SyndFeedInput +import com.rometools.rome.io.XmlReader +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.entries.FeedsManager +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URL + +/** + * Reads an RSS feed. + */ +class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable { + private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) + + /** + * Fetches the Feed's items. + */ + override fun run() { + try { + readFeed(url).forEach { + event.sendMessage("", it) + } + } catch (e: FeedException) { + if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e) + event.sendMessage("An error has occurred while parsing the feed: ${e.message}") + } catch (e: IOException) { + if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e) + event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}") + } + } + + companion object { + @JvmStatic + @Throws(FeedException::class, IOException::class) + fun readFeed(url: String, maxItems: Int = 5): List<Message> { + val messages = mutableListOf<Message>() + val input = SyndFeedInput() + XmlReader(URL(url).openStream()).use { reader -> + val feed = input.build(reader) + val items = feed.entries + if (items.isEmpty()) { + messages.add(NoticeMessage("There is currently nothing to view.")) + } else { + items.take(maxItems).forEach { + messages.add(NoticeMessage(it.title)) + messages.add(NoticeMessage(helpFormat(it.link.green(), false))) + } + } + } + return messages + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/Mobibot.kt b/bin/main/net/thauvin/erik/mobibot/Mobibot.kt new file mode 100644 index 0000000..f91c457 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Mobibot.kt @@ -0,0 +1,421 @@ +/* + * Mobibot.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import kotlinx.cli.ArgParser +import kotlinx.cli.ArgType +import kotlinx.cli.default +import net.thauvin.erik.mobibot.Utils.appendIfMissing +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.lastOrEmpty +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.commands.* +import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap +import net.thauvin.erik.mobibot.commands.links.* +import net.thauvin.erik.mobibot.commands.seen.Seen +import net.thauvin.erik.mobibot.commands.tell.Tell +import net.thauvin.erik.mobibot.modules.* +import net.thauvin.erik.semver.Version +import org.pircbotx.Configuration +import org.pircbotx.PircBotX +import org.pircbotx.hooks.ListenerAdapter +import org.pircbotx.hooks.events.* +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.* +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* +import java.util.regex.Pattern +import kotlin.system.exitProcess + +@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") +class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { + // The bot configuration. + private val config: Configuration + + // Commands and Modules + private val addons: Addons + + // Seen command + private val seen: Seen + + // Tell command + private val tell: Tell + + /** Logger. */ + val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java) + + /** + * Connects to the server and joins the channel. + */ + fun connect() { + PircBotX(config).startBot() + } + + /** + * Responds with the default help. + */ + private fun helpDefault(event: GenericMessageEvent) { + event.sendMessage("Type a URL on $channel to post it.") + event.sendMessage("For more information on a specific command, type:") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) + ) + ) + event.sendMessage("The commands are:") + event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) + if (event.isChannelOp(channel)) { + if (addons.names.disabledCommands.isNotEmpty()) { + event.sendMessage("The disabled commands are:") + event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true) + } + event.sendMessage("The op commands are:") + event.sendList(addons.names.ops, 8, isBold = true, isIndent = true) + } + } + + /** + * Responds with the default, commands or modules help. + */ + private fun helpResponse(event: GenericMessageEvent, topic: String) { + if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) { + helpDefault(event) + } + } + + override fun onAction(event: ActionEvent?) { + event?.channel?.let { + if (channel == it.name) { + event.user?.let { user -> + storeRecap(user.nick, event.action, true) + } + } + } + } + + override fun onDisconnect(event: DisconnectEvent?) { + event?.let { + with(event.getBot<PircBotX>()) { + LinksManager.socialManager.notification("$nick disconnected from $serverHostname") + seen.add(userChannelDao.getChannel(channel).users) + } + } + LinksManager.socialManager.shutdown() + } + + override fun onPrivateMessage(event: PrivateMessageEvent?) { + event?.user?.let { user -> + if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}") + val cmds = event.message.trim().split(" ".toRegex(), 2) + val cmd = cmds[0].lowercase() + val args = cmds.lastOrEmpty().trim() + if (cmd.startsWith(Constants.HELP_CMD)) { // help + helpResponse(event, args) + } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module + helpDefault(event) + } + } + } + + override fun onJoin(event: JoinEvent?) { + event?.user?.let { user -> + with(event.getBot<PircBotX>()) { + if (user.nick == nick) { + LinksManager.socialManager.notification( + "$nick has joined ${event.channel.name} on $serverHostname" + ) + seen.add(userChannelDao.getChannel(channel).users) + } else { + tell.send(event) + seen.add(user.nick) + } + } + } + } + + override fun onMessage(event: MessageEvent?) { + event?.user?.let { user -> + tell.send(event) + if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command> + if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") + val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2) + val cmd = cmds[0].lowercase() + val args = cmds.lastOrEmpty().trim() + if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help + helpResponse(event, args) + } else { + // Execute module or command + addons.exec(channel, cmd, args, event) + } + } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. + if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") + } + storeRecap(user.nick, event.message, false) + seen.add(user.nick) + } + } + + override fun onNickChange(event: NickChangeEvent?) { + event?.let { + tell.send(event) + if (!it.oldNick.equals(it.newNick, true)) { + seen.add(it.oldNick) + } + seen.add(it.newNick) + } + } + + override fun onPart(event: PartEvent?) { + event?.user?.let { user -> + with(event.getBot<PircBotX>()) { + if (user.nick == nick) { + LinksManager.socialManager.notification( + "$nick has left ${event.channel.name} on $serverHostname" + ) + seen.add(userChannelDao.getChannel(channel).users) + } else { + seen.add(user.nick) + } + } + } + } + + override fun onQuit(event: QuitEvent?) { + event?.user?.let { user -> + seen.add(user.nick) + } + } + + companion object { + @JvmStatic + @Throws(Exception::class) + fun main(args: Array<String>) { + // Set up the command line options + val parser = ArgParser(Constants.CLI_CMD) + val debug by parser.option( + ArgType.Boolean, + Constants.DEBUG_ARG, + Constants.DEBUG_ARG.substring(0, 1), + "Print debug & logging data directly to the console" + ).default(false) + val property by parser.option( + ArgType.String, + Constants.PROPS_ARG, + Constants.PROPS_ARG.substring(0, 1), + "Use alternate properties file" + ).default("./${ReleaseInfo.PROJECT}.properties") + val version by parser.option( + ArgType.Boolean, + Constants.VERSION_ARG, + Constants.VERSION_ARG.substring(0, 1), + "Print version info" + ).default(false) + + // Parse the command line + parser.parse(args) + + if (version) { + // Output the version + println( + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + ) + println(ReleaseInfo.WEBSITE) + } else { + // Load the properties + val p = Properties() + try { + Files.newInputStream( + Paths.get(property) + ).use { fis -> + p.load(fis) + } + } catch (ignore: FileNotFoundException) { + System.err.println("Unable to find properties file.") + exitProcess(1) + } catch (ignore: IOException) { + System.err.println("Unable to open properties file.") + exitProcess(1) + } + val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) + val channel = p.getProperty("channel") + val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) + + // Redirect stdout and stderr + if (!debug) { + try { + val stdout = PrintStream( + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true + ) + ), true + ) + System.setOut(stdout) + } catch (ignore: IOException) { + System.err.println("Unable to open output (stdout) log file.") + exitProcess(1) + } + try { + val stderr = PrintStream( + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true + ) + System.setErr(stderr) + } catch (ignore: IOException) { + System.err.println("Unable to open error (stderr) log file.") + exitProcess(1) + } + } + + // Start the bot + Mobibot(nickname, channel, logsDir, p).connect() + } + } + } + + /** + * Initialize the bot. + */ + init { + val ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) + config = Configuration.Builder().apply { + name = nickname + login = p.getProperty("login", nickname) + realName = p.getProperty("realname", nickname) + addServer( + ircServer, + p.getIntProperty("port", Constants.DEFAULT_PORT) + ) + addAutoJoinChannel(channel) + addListener(this@Mobibot) + version = "${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}" + isAutoNickChange = true + val identPwd = p.getProperty("ident") + if (!identPwd.isNullOrBlank()) { + nickservPassword = identPwd + } + val identNick = p.getProperty("ident-nick") + if (!identNick.isNullOrBlank()) { + nickservNick = identNick + } + val identMsg = p.getProperty("ident-msg") + if (!identMsg.isNullOrBlank()) { + nickservCustomMessage = identMsg + } + isAutoReconnect = true + + //socketConnectTimeout = Constants.CONNECT_TIMEOUT + //socketTimeout = Constants.CONNECT_TIMEOUT + //messageDelay = StaticDelay(500) + }.buildConfiguration() + + // Load the current entries + with(LinksManager) { + entries.channel = channel + entries.ircServer = ircServer + entries.logsDir = logsDirPath + entries.backlogs = p.getProperty("backlogs", "") + entries.load() + + // Set up pinboard + pinboard.setApiToken(p.getProperty("pinboard-api-token", "")) + } + + addons = Addons(p) + + // Load the commands + addons.add(ChannelFeed(channel.removePrefix("#"))) + addons.add(Comment()) + addons.add(Cycle()) + addons.add(Die()) + addons.add(Ignore()) + addons.add(LinksManager()) + addons.add(Me()) + addons.add(Modules(addons.names.modules, addons.names.disabledModules)) + addons.add(Msg()) + addons.add(Nick()) + addons.add(Posting()) + addons.add(Recap()) + addons.add(Say()) + + // Seen command + seen = Seen("${logsDirPath}${nickname}-seen.ser") + addons.add(seen) + + addons.add(Tags()) + + // Tell command + tell = Tell("${logsDirPath}${nickname}.ser") + addons.add(tell) + + addons.add(Users()) + addons.add(Versions()) + addons.add(View()) + + // Load social modules + LinksManager.socialManager.add(addons, Mastodon()) + + // Load the modules + addons.add(Calc()) + addons.add(ChatGpt()) + addons.add(CryptoPrices()) + addons.add(CurrencyConverter()) + addons.add(Dice()) + addons.add(GoogleSearch()) + addons.add(Info(tell, seen)) + addons.add(Joke()) + addons.add(Lookup()) + addons.add(Ping()) + addons.add(RockPaperScissors()) + addons.add(StockQuote()) + addons.add(War()) + addons.add(Weather2()) + addons.add(WolframAlpha()) + addons.add(WorldTime()) + + // Sort the addons + addons.names.sort() + } +} + diff --git a/bin/main/net/thauvin/erik/mobibot/Pinboard.kt b/bin/main/net/thauvin/erik/mobibot/Pinboard.kt new file mode 100644 index 0000000..7cb5aed --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Pinboard.kt @@ -0,0 +1,113 @@ +/* + * Pinboard.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.pinboard.PinboardPoster +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit +import java.util.* + +/** + * Handles posts to pinboard.in. + */ +class Pinboard { + private val poster = PinboardPoster() + + /** + * Adds a pin. + */ + fun addPin(ircServer: String, entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + with(entry) { + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) + } + } + } + + /** + * Sets the pinboard API token. + */ + fun setApiToken(apiToken: String) { + poster.apiToken = apiToken + } + + /** + * Deletes a pin. + */ + fun deletePin(entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + poster.deletePin(entry.link) + } + + } + + /** + * Updates a pin. + */ + fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + with(entry) { + if (oldUrl != link) { + poster.deletePin(oldUrl) + } + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) + } + } + } + + /** + * Formats a date to a UTC timestamp. + */ + private fun Date.toTimestamp(): String { + return ZonedDateTime.ofInstant( + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() + ).format(DateTimeFormatter.ISO_INSTANT) + } + + /** + * Formats the tags for pinboard. + */ + private fun EntryLink.formatTags(): String { + return nick + formatTags(",", ",") + } + + /** + * Returns the pinboard.in extended attribution line. + */ + private fun EntryLink.postedBy(ircServer: String): String { + return "Posted by $nick on $channel ( $ircServer )" + } +} + diff --git a/bin/main/net/thauvin/erik/mobibot/Utils.kt b/bin/main/net/thauvin/erik/mobibot/Utils.kt new file mode 100644 index 0000000..e4760d2 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Utils.kt @@ -0,0 +1,439 @@ +/* + * Utils.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import net.thauvin.erik.urlencoder.UrlEncoderUtil +import org.jsoup.Jsoup +import org.pircbotx.Colors +import org.pircbotx.PircBotX +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import java.io.* +import java.net.HttpURLConnection +import java.net.URL +import java.nio.file.Files +import java.nio.file.Paths +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.util.* +import kotlin.io.path.exists +import kotlin.io.path.fileSize + +/** + * Miscellaneous utilities. + */ +@Suppress("TooManyFunctions") +object Utils { + private val searchFlags = arrayOf("%c", "%n") + + /** + * Prepends a prefix if not present. + */ + @JvmStatic + fun String.prefixIfMissing(prefix: Char): String { + return if (first() != prefix) { + "$prefix${this}" + } else { + this + } + } + + /** + * Appends a suffix to the end of the String if not present. + */ + @JvmStatic + fun String.appendIfMissing(suffix: Char): String { + return if (last() != suffix) { + "$this${suffix}" + } else { + this + } + } + + /** + * Makes the given int bold. + */ + @JvmStatic + fun Int.bold(): String = toString().bold() + + /** + * Makes the given long bold. + */ + @JvmStatic + fun Long.bold(): String = toString().bold() + + /** + * Makes the given string bold. + */ + @JvmStatic + fun String?.bold(): String = colorize(Colors.BOLD) + + /** + * Returns the [PircBotX] instance. + */ + fun GenericMessageEvent.bot(): PircBotX { + return getBot() as PircBotX + } + + /** + * Capitalize a string. + */ + @JvmStatic + fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() } + + /** + * Capitalize words + */ + @JvmStatic + fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() } + + /** + * Colorize a string. + */ + @JvmStatic + fun String?.colorize(color: String): String { + return when { + isNullOrEmpty() -> { + "" + } + + color == DEFAULT_COLOR -> { + this + } + + Colors.BOLD == color || Colors.REVERSE == color -> { + color + this + color + } + + else -> { + color + this + Colors.NORMAL + } + } + } + + /** + * Makes the given string cyan. + */ + @JvmStatic + fun String?.cyan(): String = colorize(Colors.CYAN) + + /** + * URL encodes the given string. + */ + @JvmStatic + fun String.encodeUrl(): String = UrlEncoderUtil.encode(this) + + /** + * Returns a property as an int. + */ + @JvmStatic + fun Properties.getIntProperty(key: String, defaultValue: Int): Int { + return getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue + } + + /** + * Makes the given string green. + */ + @JvmStatic + fun String?.green(): String = colorize(Colors.DARK_GREEN) + + /** + * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's + * nick. + */ + @JvmStatic + fun helpCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { + val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) + return text.replaceEach(searchFlags, replace) + } + + /** + * Returns a formatted help string. + */ + @JvmStatic + @JvmOverloads + fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { + val s = if (isBold) help.bold() else help + return if (isIndent) s.prependIndent() else s + } + + /** + * Returns `true` if the specified user is an operator on the [channel]. + */ + @JvmStatic + fun GenericMessageEvent.isChannelOp(channel: String): Boolean { + return this.bot().userChannelDao.getChannel(channel).isOp(this.user) + } + + /** + * Returns `true` if a HTTP status code indicates a successful response. + */ + @JvmStatic + fun Int.isHttpSuccess() = this in 200..399 + + /** + * Returns the last item of a list of strings or empty if none. + */ + @JvmStatic + fun List<String>.lastOrEmpty(): String { + return if (this.size >= 2) { + this.last() + } else + "" + } + + /** + * Load serial data from file. + */ + @JvmStatic + fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any { + val serialFile = Paths.get(file) + if (serialFile.exists() && serialFile.fileSize() > 0) { + try { + ObjectInputStream( + BufferedInputStream(Files.newInputStream(serialFile)) + ).use { input -> + if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") + return input.readObject() + } + } catch (e: IOException) { + logger.error("An IO error occurred loading the ${description}.", e) + } catch (e: ClassNotFoundException) { + logger.error("An error occurred loading the ${description}.", e) + } + } + return default + } + + /** + * Returns `true` if the list does not contain the given string. + */ + @JvmStatic + fun List<String>.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } + + /** + * Obfuscates the given string. + */ + @JvmStatic + fun String.obfuscate(): String { + return if (isNotBlank()) { + "x".repeat(length) + } else this + } + + /** + * Returns the plural form of a word, if count > 1. + */ + @JvmStatic + fun String.plural(count: Long): String { + return if (count > 1) "${this}s" else this + } + + /** + * Makes the given string red. + */ + @JvmStatic + fun String?.red(): String = colorize(Colors.RED) + + /** + * Replaces all occurrences of Strings within another String. + */ + @JvmStatic + fun String.replaceEach(search: Array<out String>, replace: Array<out String>): String { + var result = this + if (search.size == replace.size) { + search.forEachIndexed { i, s -> + result = result.replace(s, replace[i]) + } + } + return result + } + + /** + * Makes the given string reverse color. + */ + @JvmStatic + fun String?.reverseColor(): String = colorize(Colors.REVERSE) + + /** + * Save data + */ + @JvmStatic + fun saveSerialData(file: String, data: Any, logger: Logger, description: String) { + try { + BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> + ObjectOutputStream(bos).use { output -> + if (logger.isDebugEnabled) logger.debug("Saving the ${description}.") + output.writeObject(data) + } + } + } catch (e: IOException) { + logger.error("Unable to save the ${description}.", e) + } + } + + /** + * Send a formatted commands/modules, etc. list. + */ + @JvmStatic + @JvmOverloads + fun GenericMessageEvent.sendList( + list: List<String>, + maxPerLine: Int, + separator: String = " ", + isBold: Boolean = false, + isIndent: Boolean = false + ) { + var i = 0 + while (i < list.size) { + sendMessage( + helpFormat( + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), + isBold, + isIndent + ), + ) + i += maxPerLine + } + } + + /** + * Sends a [message]. + */ + @JvmStatic + fun GenericMessageEvent.sendMessage(channel: String, message: Message) { + if (message.isNotice) { + bot().sendIRC().notice(user.nick, message.msg.colorize(message.color)) + } else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) { + respondPrivateMessage(message.msg.colorize(message.color)) + } else { + bot().sendIRC().message(channel, message.msg.colorize(message.color)) + } + } + + /** + * Sends a response as a private message or notice. + */ + @JvmStatic + fun GenericMessageEvent.sendMessage(message: String) { + if (this is PrivateMessageEvent) { + respondPrivateMessage(message) + } else { + bot().sendIRC().notice(user.nick, message) + } + } + + /** + * Returns today's date. + */ + @JvmStatic + fun today(): String = LocalDateTime.now().toIsoLocalDate() + + /** + * Converts a string to an int. + */ + @JvmStatic + fun String.toIntOrDefault(defaultValue: Int): Int { + return try { + toInt() + } catch (e: NumberFormatException) { + defaultValue + } + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun Date.toIsoLocalDate(): String { + return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toIsoLocalDate() + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun LocalDateTime.toIsoLocalDate(): String = format(DateTimeFormatter.ISO_LOCAL_DATE) + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun Date.toUtcDateTime(): String { + return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toUtcDateTime() + } + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + + /** + * Makes the given string bold. + */ + @JvmStatic + fun String?.underline(): String = colorize(Colors.UNDERLINE) + + + /** + * Converts XML/XHTML entities to plain text. + */ + @JvmStatic + fun String.unescapeXml(): String = Jsoup.parse(this).text() + + /** + * Reads contents of a URL. + */ + @JvmStatic + @Throws(IOException::class) + fun URL.reader(): UrlReaderResponse { + val connection = this.openConnection() as HttpURLConnection + connection.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + ) + return if (connection.responseCode.isHttpSuccess()) { + UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) + } else { + UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) + } + } + + /** + * Holds the [URL.reader] response code and body text. + */ + data class UrlReaderResponse(val responseCode: Int, val body: String) +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt new file mode 100644 index 0000000..5f79472 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -0,0 +1,79 @@ +/* + * AbstractCommand.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent + +abstract class AbstractCommand { + abstract val name: String + abstract val help: List<String> + abstract val isOpOnly: Boolean + abstract val isPublic: Boolean + abstract val isVisible: Boolean + + val properties: MutableMap<String, String> = mutableMapOf() + + abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent) + + open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + if (!isOpOnly || isOpOnly == event.isChannelOp(channel)) { + for (h in help) { + event.sendMessage(helpCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic)) + } + return true + } + return false + } + + open fun initProperties(vararg keys: String) { + keys.forEach { + properties[it] = "" + } + } + + open fun isEnabled(): Boolean { + return true + } + + open fun matches(message: String): Boolean { + return false + } + + open fun setProperty(key: String, value: String) { + properties[key] = value + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt new file mode 100644 index 0000000..038e378 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -0,0 +1,62 @@ +/* + * ChannelFeed.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.FeedReader +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + +class ChannelFeed(channel: String) : AbstractCommand() { + override val name = channel + override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel")) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val FEED_PROP = "feed" + } + + init { + initProperties(FEED_PROP) + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isEnabled()) { + properties[FEED_PROP]?.let { FeedReader(it, event).run() } + } + } + + override fun isEnabled(): Boolean { + return !properties[FEED_PROP].isNullOrBlank() + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt b/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt new file mode 100644 index 0000000..9608ca8 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -0,0 +1,66 @@ +/* + * Cycle.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Cycle : AbstractCommand() { + private val wait = 10 + override val name = "cycle" + override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + if (event.isChannelOp(channel)) { + runBlocking { + launch { + sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!") + userChannelDao.getChannel(channel).send().part() + delay(wait * 1000L) + sendIRC().joinChannel(channel) + } + } + } else { + helpResponse(channel, args, event) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Die.kt b/bin/main/net/thauvin/erik/mobibot/commands/Die.kt new file mode 100644 index 0000000..f271bfa --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Die.kt @@ -0,0 +1,62 @@ +/* + * Die.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Die : AbstractCommand() { + override val name = "die" + override val help = emptyList<String>() + override val isOpOnly = true + override val isPublic = false + override val isVisible = false + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + if (event.isChannelOp(channel) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) { + sendIRC().message(channel, "${event.user?.nick} has just signed my death sentence.") + stopBotReconnect() + sendIRC().quitServer("The Bot is Out There!") + } + } + } + + companion object { + const val DIE_PROP = "die" + } + + init { + initProperties(DIE_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt b/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt new file mode 100644 index 0000000..d083c10 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -0,0 +1,147 @@ +/* + * Ignore.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.links.LinksManager +import org.pircbotx.hooks.types.GenericMessageEvent + +class Ignore : AbstractCommand() { + private val me = "me" + + init { + initProperties(IGNORE_PROP) + } + + override val name = IGNORE_CMD + override val help = listOf( + "To ignore a link posted to the channel:", + helpFormat("https://www.foo.bar %n"), + "To check your ignore status:", + helpFormat("%c $name"), + "To toggle your ignore status:", + helpFormat("%c $name $me") + ) + private val helpOp = help.plus( + arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) + ) + + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val IGNORE_CMD = "ignore" + const val IGNORE_PROP = IGNORE_CMD + private val ignored = mutableSetOf<String>() + + @JvmStatic + fun isNotIgnored(nick: String): Boolean { + return !ignored.contains(nick.lowercase()) + } + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val isMe = args.trim().equals(me, true) + if (isMe || !event.isChannelOp(channel)) { + val nick = event.user.nick.lowercase() + ignoreNick(nick, isMe, event) + } else { + ignoreOp(args, event) + } + } + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + return if (event.isChannelOp(channel)) { + for (h in helpOp) { + event.sendMessage(helpCmdSyntax(h, event.bot().nick, true)) + } + true + } else { + super.helpResponse(channel, topic, event) + } + } + + private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) { + if (isMe) { + if (ignored.remove(sender)) { + event.sendMessage("You are no longer ignored.") + } else { + ignored.add(sender) + event.sendMessage("You are now ignored.") + } + } else { + if (ignored.contains(sender)) { + event.sendMessage("You are currently ignored.") + } else { + event.sendMessage("You are not currently ignored.") + } + } + } + + private fun ignoreOp(args: String, event: GenericMessageEvent) { + if (args.isNotEmpty()) { + val nicks = args.lowercase().split(" ") + for (nick in nicks) { + val ignore = if (me == nick) { + nick.lowercase() + } else { + nick + } + if (!ignored.remove(ignore)) { + ignored.add(ignore) + } + } + } + + if (ignored.isNotEmpty()) { + event.sendMessage("The following nicks are ignored:") + event.sendList(ignored.sorted(), 8, isIndent = true) + } else { + event.sendMessage("No one is currently ${"ignored".bold()}.") + } + } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (IGNORE_PROP == key) { + ignored.addAll(value.split(LinksManager.TAG_MATCH)) + } + } + +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Info.kt b/bin/main/net/thauvin/erik/mobibot/commands/Info.kt new file mode 100644 index 0000000..ed0b6ef --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Info.kt @@ -0,0 +1,124 @@ +/* + * Info.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.ReleaseInfo +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.links.LinksManager +import net.thauvin.erik.mobibot.commands.seen.Seen +import net.thauvin.erik.mobibot.commands.tell.Tell +import org.pircbotx.hooks.types.GenericMessageEvent +import java.lang.management.ManagementFactory +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { + private val allVersions = listOf( + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", + "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" + ) + override val name = "info" + override val help = listOf("To view information about the bot:", helpFormat("%c $name")) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + /** + * Converts milliseconds to year month week day hour and minutes. + */ + @JvmStatic + fun Long.toUptime(): String { + this.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, seconds, _ -> + val years = wholeDays / 365 + var days = wholeDays % 365 + val months = days / 30 + days %= 30 + val weeks = days / 7 + days %= 7 + + with(StringBuffer()) { + if (years > 0) { + append(years).append(" year".plural(years)).append(' ') + } + if (months > 0) { + append(months).append(" month".plural(months)).append(' ') + } + if (weeks > 0) { + append(weeks).append(" week".plural(weeks)).append(' ') + } + if (days > 0) { + append(days).append(" day".plural(days)).append(' ') + } + if (hours > 0) { + append(hours).append(" hour".plural(hours.toLong())).append(' ') + } + + if (minutes > 0) { + append(minutes).append(" minute".plural(minutes.toLong())) + } else { + append(seconds).append(" second".plural(seconds.toLong())) + } + + return toString() + } + } + } + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + event.sendList(allVersions, 1) + val info = StringBuilder() + info.append("Uptime: ") + .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) + .append(" [Entries: ") + .append(LinksManager.entries.links.size) + if (seen.isEnabled()) { + info.append(", Seen: ").append(seen.count()) + } + if (event.isChannelOp(channel)) { + if (tell.isEnabled()) { + info.append(", Messages: ").append(tell.size()) + } + if (LinksManager.socialManager.entriesCount() > 0) { + info.append(", Social: ").append(LinksManager.socialManager.entriesCount()) + } + } + info.append(", Recap: ").append(Recap.recaps.size).append(']') + event.sendMessage(info.toString()) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Me.kt b/bin/main/net/thauvin/erik/mobibot/commands/Me.kt new file mode 100644 index 0000000..ec7823b --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Me.kt @@ -0,0 +1,51 @@ +/* + * Me.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Me : AbstractCommand() { + override val name = "me" + override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.bot().sendIRC().action(channel, args) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt b/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt new file mode 100644 index 0000000..b2293b0 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt @@ -0,0 +1,63 @@ +/* + * Modules.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import org.pircbotx.hooks.types.GenericMessageEvent + +class Modules(private val modules: List<String>, private val disabledModules: List<String>) : AbstractCommand() { + override val name = "modules" + override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + if (modules.isEmpty()) { + event.respondPrivateMessage("There are no enabled modules.") + } else { + event.respondPrivateMessage("The enabled modules are: ") + event.sendList(modules, 7, isIndent = true) + } + if (disabledModules.isNotEmpty()) { + event.respondPrivateMessage("The disabled modules are: ") + event.sendList(disabledModules, 7, isIndent = true) + } + } else { + helpResponse(channel, args, event) + } + } +} + diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt b/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt new file mode 100644 index 0000000..20a6635 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt @@ -0,0 +1,60 @@ +/* + * Msg.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Msg : AbstractCommand() { + override val name = "msg" + override val help = listOf( + "To have the bot send a private message to someone:", + helpFormat("%c $name <nick> <text>") + ) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + val msg = args.split(" ", limit = 2) + if (args.length > 2) { + event.bot().sendIRC().message(msg[0], msg[1]) + event.respondPrivateMessage("A message was sent to ${msg[0]}") + } else { + helpResponse(channel, args, event) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt b/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt new file mode 100644 index 0000000..85a03ab --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt @@ -0,0 +1,51 @@ +/* + * Nick.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Nick : AbstractCommand() { + override val name = "nick" + override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>")) + override val isOpOnly = true + override val isPublic = true + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.bot().sendIRC().changeNick(args) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt b/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt new file mode 100644 index 0000000..77154c7 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt @@ -0,0 +1,81 @@ +/* + * Recap.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.toUtcDateTime +import org.pircbotx.hooks.types.GenericMessageEvent +import java.time.Clock +import java.time.LocalDateTime + +class Recap : AbstractCommand() { + override val name = "recap" + override val help = listOf( + "To list the last 10 public channel messages:", + helpFormat("%c $name") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val MAX_RECAPS = 10 + + @JvmField + val recaps = mutableListOf<String>() + + /** + * Stores the last 10 public messages and actions. + */ + @JvmStatic + fun storeRecap(sender: String, message: String, isAction: Boolean) { + recaps.add( + LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + + " - $sender" + (if (isAction) " " else ": ") + message + ) + if (recaps.size > MAX_RECAPS) { + recaps.removeFirst() + } + } + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (recaps.isNotEmpty()) { + for (r in recaps) { + event.sendMessage(r) + } + } else { + event.sendMessage("Sorry, nothing to recap.") + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Say.kt b/bin/main/net/thauvin/erik/mobibot/commands/Say.kt new file mode 100644 index 0000000..7f76d35 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Say.kt @@ -0,0 +1,51 @@ +/* + * Say.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Say : AbstractCommand() { + override val name = "say" + override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name <text>")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.bot().sendIRC().message(channel, args) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Users.kt b/bin/main/net/thauvin/erik/mobibot/commands/Users.kt new file mode 100644 index 0000000..33d6fef --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Users.kt @@ -0,0 +1,50 @@ +/* + * Users.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendList +import org.pircbotx.hooks.types.GenericMessageEvent + +class Users : AbstractCommand() { + override val name = "users" + override val help = listOf("To list the users present on the channel:", helpFormat("%c $name")) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val ch = event.bot().userChannelDao.getChannel(channel) + event.sendList(ch.users.map { if (it.channelsOpIn.contains(ch)) "@${it.nick}" else it.nick }, 8) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt b/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt new file mode 100644 index 0000000..896c569 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt @@ -0,0 +1,59 @@ +/* + * Versions.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands + +import net.thauvin.erik.mobibot.ReleaseInfo +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import org.pircbotx.PircBotX +import org.pircbotx.hooks.types.GenericMessageEvent + +class Versions : AbstractCommand() { + private val allVersions = listOf( + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + + ", JVM ${System.getProperty("java.runtime.version")}", + "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" + ) + override val name = "versions" + override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.sendList(allVersions, 1) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt new file mode 100644 index 0000000..1443d44 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -0,0 +1,151 @@ +/* + * Comment.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent + +class Comment : AbstractCommand() { + override val name = COMMAND + override val help = listOf( + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, use its label: ", + helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), + "To delete a comment, use its label and a minus sign: ", + helpFormat("${Constants.LINK_CMD}1.1:-") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val COMMAND = "comment" + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val cmds = args.substring(1).split("[.:]".toRegex(), 3) + val entryIndex = cmds[0].toInt() - 1 + + if (entryIndex < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { + val entry: EntryLink = LinksManager.entries.links[entryIndex] + val commentIndex = cmds[1].toInt() - 1 + if (commentIndex < entry.comments.size) { + when (val cmd = cmds[2].trim()) { + "" -> showComment(entry, entryIndex, commentIndex, event) // L1.1: + "-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:- + else -> { + if (cmd.startsWith('?')) { // L1.1:?<author> + changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event) + } else { // L1.1:<comment> + setComment(cmd, entry, entryIndex, commentIndex, event) + } + } + } + } + } + } + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + if (super.helpResponse(channel, topic, event)) { + if (event.isChannelOp(channel)) { + event.sendMessage("To change a comment's author:") + event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?<nick>")) + } + return true + } + return false + } + + override fun matches(message: String): Boolean { + return message.matches("^${Constants.LINK_CMD}\\d+\\.\\d+:.*".toRegex()) + } + + private fun changeAuthor( + channel: String, + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent + ) { + if (event.isChannelOp(channel) && cmd.length > 1) { + val comment = entry.getComment(commentIndex) + comment.nick = cmd.substring(1) + event.sendMessage(printComment(entryIndex, commentIndex, comment)) + LinksManager.entries.save() + } else { + event.sendMessage("Please ask a channel op to change the author of this comment for you.") + } + } + + private fun deleteComment( + channel: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent + ) { + if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { + entry.deleteComment(commentIndex) + event.sendMessage("Comment ${entryIndex.toLinkLabel()}.${commentIndex + 1} removed.") + LinksManager.entries.save() + } else { + event.sendMessage("Please ask a channel op to delete this comment for you.") + } + } + + private fun setComment( + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent + ) { + entry.setComment(commentIndex, cmd, event.user.nick) + event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + LinksManager.entries.save() + } + + private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) { + event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt new file mode 100644 index 0000000..fba6b99 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -0,0 +1,207 @@ +/* + * LinksManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Pinboard +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.today +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored +import net.thauvin.erik.mobibot.entries.Entries +import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.social.SocialManager +import org.jsoup.Jsoup +import org.pircbotx.hooks.types.GenericMessageEvent +import java.io.IOException + +class LinksManager : AbstractCommand() { + private val defaultTags: MutableList<String> = mutableListOf() + private val keywords: MutableList<String> = mutableListOf() + + override val name = Constants.LINK_CMD + override val help = emptyList<String>() + override val isOpOnly = false + override val isPublic = false + override val isVisible = false + + init { + initProperties(TAGS_PROP, KEYWORDS_PROP) + } + + companion object { + val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex() + const val KEYWORDS_PROP = "tags-keywords" + const val TAGS_PROP = "tags" + val TAG_MATCH = ", *| +".toRegex() + + /** + * Entries array + */ + @JvmField + val entries = Entries() + + /** + * Pinboard handler. + */ + @JvmField + val pinboard = Pinboard() + + /** + * Social Manager handler. + */ + @JvmField + val socialManager = SocialManager() + + /** + * Let the user know if the entries are too old to be modified. + */ + @JvmStatic + fun isUpToDate(event: GenericMessageEvent): Boolean { + if (entries.lastPubDate != today()) { + event.sendMessage("The links are too old to be updated.") + return false + } + return true + } + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val cmds = args.split(" ".toRegex(), 2) + val sender = event.user.nick + val botNick = event.bot().nick + val login = event.user.login + + if (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) { + val link = cmds[0].trim() + if (!isDupEntry(link, event)) { + var title = "" + val tags = ArrayList<String>(defaultTags) + if (cmds.size == 2) { + val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) + title = data[0].trim() + if (data.size > 1) { + tags.addAll(data[1].split(TAG_MATCH)) + } + } + + if (title.isBlank()) { + title = fetchTitle(link) + } + + if (title != Constants.NO_TITLE) { + matchTagKeywords(title, tags) + } + + // Links are old, clear them + if (entries.lastPubDate != today()) { + entries.links.clear() + } + + val entry = EntryLink(link, title, sender, login, channel, tags) + entries.links.add(entry) + val index = entries.links.lastIndexOf(entry) + event.sendMessage(printLink(index, entry)) + + pinboard.addPin(event.bot().serverHostname, entry) + + // Queue link for posting to social media. + socialManager.queueEntry(index) + + entries.save() + + if (Constants.NO_TITLE == entry.title) { + event.sendMessage("Please specify a title, by typing:") + event.sendMessage(helpFormat("${index.toLinkLabel()}:|This is the title")) + } + } + } + } + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false + + override fun matches(message: String): Boolean { + return message.matches(LINK_MATCH) + } + + internal fun fetchTitle(link: String): String { + try { + val html = Jsoup.connect(link) + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() + val title = html.title() + if (title.isNotBlank()) { + return title + } + } catch (ignore: IOException) { + // Do nothing + } + return Constants.NO_TITLE + } + + private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean { + synchronized(entries) { + return try { + val match = entries.links.single { it.link == link } + event.sendMessage( + "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) + ) + true + } catch (ignore: NoSuchElementException) { + false + } + } + } + + internal fun matchTagKeywords(title: String, tags: MutableList<String>) { + for (match in keywords) { + val m = Regex.escape(match) + if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { + tags.add(match) + } + } + } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (KEYWORDS_PROP == key) { + keywords.addAll(value.split(TAG_MATCH)) + } else if (TAGS_PROP == key) { + defaultTags.addAll(value.split(TAG_MATCH)) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt new file mode 100644 index 0000000..ff4278d --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -0,0 +1,164 @@ +/* + * Posting.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.LinksManager.Companion.entries +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent + +class Posting : AbstractCommand() { + override val name = "posting" + override val help = listOf( + "Post a URL, by saying it on a line on its own:", + helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", + "To add a title, use its label and a pipe:", + helpFormat("${Constants.LINK_CMD}1:|This is the title"), + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, see: ", + helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val cmds = args.substring(1).split(":", limit = 2) + val entryIndex = cmds[0].toInt() - 1 + + if (entryIndex < entries.links.size) { + val cmd = cmds[1].trim() + if (cmd.isBlank()) { + showEntry(entryIndex, event) // L1: + } else if (LinksManager.isUpToDate(event)) { + if (cmd == "-") { + removeEntry(channel, entryIndex, event) // L1:- + } else { + when (cmd[0]) { + '|' -> changeTitle(cmd, entryIndex, event) // L1:|<title> + '=' -> changeUrl(channel, cmd, entryIndex, event) // L1:=<url> + '?' -> changeAuthor(channel, cmd, entryIndex, event) // L1:?<author> + else -> addComment(cmd, entryIndex, event) // L1:<comment> + } + } + } + } + } + + override fun matches(message: String): Boolean { + return message.matches("${Constants.LINK_CMD}\\d+:.*".toRegex()) + } + + private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[entryIndex] + val commentIndex = entry.addComment(cmd, event.user.nick) + val comment = entry.getComment(commentIndex) + event.sendMessage(EntriesUtils.printComment(entryIndex, commentIndex, comment)) + entries.save() + } + + private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) { + if (cmd.length > 1) { + val entry: EntryLink = entries.links[entryIndex] + entry.title = cmd.substring(1).trim() + LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) + entries.save() + } + } + + private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[entryIndex] + if (entry.login == event.user.login || event.isChannelOp(channel)) { + val link = cmd.substring(1) + if (link.matches(LinksManager.LINK_MATCH)) { + val oldLink = entry.link + entry.link = link + LinksManager.pinboard.updatePin(event.bot().serverHostname, oldLink, entry) + event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) + entries.save() + } + } + } + + private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + if (cmd.length > 1) { + val entry: EntryLink = entries.links[index] + entry.nick = cmd.substring(1) + LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.printLink(index, entry)) + entries.save() + } + } else { + event.sendMessage("Please ask a channel op to change the author of this link for you.") + } + } + + private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[index] + if (entry.login == event.user.login || event.isChannelOp(channel)) { + LinksManager.pinboard.deletePin(entry) + LinksManager.socialManager.removeEntry(index) + entries.links.removeAt(index) + event.sendMessage("Entry ${index.toLinkLabel()} removed.") + entries.save() + } else { + event.sendMessage("Please ask a channel op to remove this entry for you.") + } + } + + private fun showEntry(index: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[index] + event.sendMessage(EntriesUtils.printLink(index, entry)) + if (entry.tags.isNotEmpty()) { + event.sendMessage(EntriesUtils.printTags(index, entry)) + } + if (entry.comments.isNotEmpty()) { + val comments = entry.comments + for (i in comments.indices) { + event.sendMessage(EntriesUtils.printComment(index, i, comments[i])) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt new file mode 100644 index 0000000..1662857 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -0,0 +1,87 @@ +/* + * Tags.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent + +class Tags : AbstractCommand() { + override val name = COMMAND + override val help = listOf( + "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", + helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val COMMAND = "tags" + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2) + val index = cmds[0].toInt() - 1 + + if (index < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { + val cmd = cmds[1].trim() + val entry: EntryLink = LinksManager.entries.links[index] + if (cmd.isNotEmpty()) { + if (entry.login == event.user.login || event.isChannelOp(channel)) { + entry.setTags(cmd) + LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.printTags(index, entry)) + LinksManager.entries.save() + } else { + event.sendMessage("Please ask a channel op to change the tags for you.") + } + } else { + if (entry.tags.isNotEmpty()) { + event.sendMessage(EntriesUtils.printTags(index, entry)) + } else { + event.sendMessage("The entry has no tags. Why don't add some?") + } + } + } + } + + override fun matches(message: String): Boolean { + return message.matches("^${Constants.LINK_CMD}\\d+${Constants.TAG_CMD}:.*".toRegex()) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt new file mode 100644 index 0000000..825e374 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt @@ -0,0 +1,120 @@ +/* + * View.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.lastOrEmpty +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.LinksManager.Companion.entries +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent + +class View : AbstractCommand() { + override val name = VIEW_CMD + override val help = listOf( + "To list or search the current URL posts:", + helpFormat("%c $name [<start>] [<query>]") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val MAX_ENTRIES = 6 + const val VIEW_CMD = "view" + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (entries.links.isNotEmpty()) { + val p = parseArgs(args) + showPosts(p.first, p.second, event) + } else { + event.sendMessage("There is currently nothing to view. Why don't you post something?") + } + } + + internal fun parseArgs(args: String): Pair<Int, String> { + var query = args.lowercase().trim() + var start = 0 + if (query.isEmpty() && entries.links.size > MAX_ENTRIES) { + start = entries.links.size - MAX_ENTRIES + } + if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>] + val split = query.split(" ", limit = 2) + try { + start = split[0].toInt() - 1 + query = split.lastOrEmpty().trim() + if (start > entries.links.size) { + start = 0 + } + } catch (ignore: NumberFormatException) { + // Do nothing + } + } + return Pair(start, query) + } + + private fun showPosts(start: Int, query: String, event: GenericMessageEvent) { + var index = start + var entry: EntryLink + var sent = 0 + while (index < entries.links.size && sent < MAX_ENTRIES) { + entry = entries.links[index] + if (query.isNotBlank()) { + if (entry.matches(query)) { + event.sendMessage(EntriesUtils.printLink(index, entry, true)) + sent++ + } + } else { + event.sendMessage(EntriesUtils.printLink(index, entry, true)) + sent++ + } + index++ + if (sent == MAX_ENTRIES && index < entries.links.size) { + event.sendMessage("To view more, try: ") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + ) + ) + } + } + if (sent == 0) { + event.sendMessage("No matches. Please try again.") + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt new file mode 100644 index 0000000..cfd2c27 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -0,0 +1,45 @@ +/* + * NickComparator.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.seen + +import java.io.Serializable + +class NickComparator : Comparator<String>, Serializable { + override fun compare(a: String, b: String): Int { + return a.lowercase().compareTo(b.lowercase()) + } + + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt new file mode 100644 index 0000000..c9ee0f3 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -0,0 +1,150 @@ +/* + * Seen.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.seen + +import com.google.common.collect.ImmutableSortedSet +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime +import org.pircbotx.User +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.util.* + + +class Seen(private val serialObject: String) : AbstractCommand() { + private val logger: Logger = LoggerFactory.getLogger(Seen::class.java) + private val allKeyword = "all" + val seenNicks = TreeMap<String, SeenNick>(NickComparator()) + + override val name = "seen" + override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) + private val helpOp = help.plus( + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isEnabled()) { + if (args.isNotBlank() && !args.contains(' ')) { + val ch = event.bot().userChannelDao.getChannel(channel) + if (args == allKeyword && ch.isOp(event.user) && seenNicks.isNotEmpty()) { + event.sendMessage("The ${"seen".bold()} nicks are:") + event.sendList(seenNicks.keys.toList(), 7, separator = ", ", isIndent = true) + return + } + ch.users.forEach { + if (args.equals(it.nick, true)) { + event.sendMessage("${it.nick} is on ${channel}.") + return + } + } + if (seenNicks.containsKey(args)) { + val seenNick = seenNicks.getValue(args) + val lastSeen = System.currentTimeMillis() - seenNick.lastSeen + event.sendMessage("${seenNick.nick} was last seen on $channel ${lastSeen.toUptime()} ago.") + return + } + event.sendMessage("I haven't seen $args on $channel lately.") + } else { + helpResponse(channel, args, event) + } + } + } + + fun add(nick: String) { + if (isEnabled()) { + seenNicks[nick] = SeenNick(nick, System.currentTimeMillis()) + save() + } + } + + fun add(users: ImmutableSortedSet<User>) { + if (isEnabled()) { + users.forEach { + seenNicks[it.nick] = SeenNick(it.nick, System.currentTimeMillis()) + } + save() + } + } + + fun clear() { + seenNicks.clear() + } + + fun count(): Int = seenNicks.size + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + return if (event.isChannelOp(channel)) { + for (h in helpOp) { + event.sendMessage(Utils.helpCmdSyntax(h, event.bot().nick, true)) + } + true + } else { + super.helpResponse(channel, topic, event) + } + } + + fun load() { + if (isEnabled()) { + @Suppress("UNCHECKED_CAST") + seenNicks.putAll( + loadSerialData( + serialObject, + TreeMap<String, SeenNick>(), + logger, + "seen nicknames" + ) as TreeMap<String, SeenNick> + ) + } + } + + fun save() { + saveSerialData(serialObject, seenNicks, logger, "seen nicknames") + } + + init { + load() + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt new file mode 100644 index 0000000..7924977 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -0,0 +1,41 @@ +/* + * SeenNick.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.seen + +import java.io.Serializable + +data class SeenNick(val nick: String, val lastSeen: Long) : Serializable { + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt new file mode 100644 index 0000000..061ca6a --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -0,0 +1,306 @@ +/* + * Tell.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.toIntOrDefault +import net.thauvin.erik.mobibot.Utils.toUtcDateTime +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.View +import org.pircbotx.PircBotX +import org.pircbotx.hooks.events.MessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent +import org.pircbotx.hooks.types.GenericUserEvent + +/** + * The `Tell` command. + */ +class Tell(private val serialObject: String) : AbstractCommand() { + // Messages queue + private val messages: MutableList<TellMessage> = mutableListOf() + + // Maximum number of days to keep messages + private var maxDays = 7 + + // Message maximum queue size + private var maxSize = 50 + + /** + * The tell command. + */ + override val name = "tell" + + override val help = listOf( + "To send a message to someone when they join the channel:", + helpFormat("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpFormat("%c $name ${View.VIEW_CMD}"), + "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' + ) + override val isOpOnly: Boolean = false + override val isPublic: Boolean = isEnabled() + override val isVisible: Boolean = isEnabled() + + /** + * Cleans the messages queue. + */ + private fun clean(): Boolean { + return TellManager.clean(messages, maxDays.toLong()) + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isEnabled()) { + when { + args.isBlank() -> { + helpResponse(channel, args, event) + } + + args.startsWith(View.VIEW_CMD) -> { + if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { + viewAll(event) + } else { + viewMessages(event) + } + } + + args.startsWith("$TELL_DEL_KEYWORD ") -> { + deleteMessage(channel, args, event) + } + + else -> { + newMessage(channel, args, event) + } + } + if (clean()) { + save() + } + } + } + + // Delete message. + private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) { + val split = args.split(" ") + if (split.size == 2) { + val id = split[1] + if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { + if (messages.removeIf { it.sender.equals(event.user.nick, true) && it.isReceived }) { + save() + event.sendMessage("Delivered messages have been deleted.") + } else { + event.sendMessage("No delivered messages were found.") + } + } else { + if (messages.removeIf { + it.id == id && + (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) + }) { + save() + event.sendMessage("The message was deleted from the queue.") + } else { + event.sendMessage("The specified message [ID $id] could not be found.") + } + } + } else { + helpResponse(channel, args, event) + } + } + + override fun isEnabled(): Boolean { + return maxSize > 0 && maxDays > 0 + } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (MAX_DAYS_PROP == key) { + maxDays = value.toIntOrDefault(maxDays) + } else if (MAX_SIZE_PROP == key) { + maxSize = value.toIntOrDefault(maxSize) + } + } + + // New message. + private fun newMessage(channel: String, args: String, event: GenericMessageEvent) { + val split = args.split(" ".toRegex(), 2) + if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) { + if (messages.size < maxSize) { + val message = TellMessage(event.user.nick, split[0], split[1].trim()) + messages.add(message) + save() + event.sendMessage("Message [ID ${message.id}] was queued for ${message.recipient.bold()}") + } else { + event.sendMessage("Sorry, the messages queue is currently full.") + } + } else { + helpResponse(channel, args, event) + } + } + + /** + * Saves the messages queue. + */ + private fun save() { + TellManager.save(serialObject, messages) + } + + /** + * Checks and sends messages. + */ + fun send(event: GenericUserEvent) { + val nickname = event.user.nick + if (isEnabled() && nickname != event.getBot<PircBotX>().nick) { + messages.filter { it.isMatch(nickname) }.forEach { message -> + if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { + if (message.sender == nickname) { + if (event !is MessageEvent) { + event.user.send().message( + "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" + ) + message.isReceived = true + message.isNotified = true + save() + } + } else { + event.user.send().message( + "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" + ) + message.isReceived = true + save() + } + } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived + && !message.isNotified + ) { + event.user.send().message( + "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " + + "${message.recipient.bold()} on ${message.receptionDate}" + ) + message.isNotified = true + save() + } + } + } + } + + /** + * Returns the messages queue size. + * + * @return The size. + */ + fun size(): Int = messages.size + + // View all messages. + private fun viewAll(event: GenericMessageEvent) { + if (messages.isNotEmpty()) { + for (message in messages) { + event.sendMessage( + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + + (if (message.isReceived) "DELIVERED]" else "QUEUED]") + ) + } + } else { + event.sendMessage("There are no messages in the queue.") + } + } + + // View messages. + private fun viewMessages(event: GenericMessageEvent) { + var hasMessage = false + for (message in messages.filter { it.isMatch(event.user.nick) }) { + if (!hasMessage) { + hasMessage = true + event.sendMessage("Here are your messages: ") + } + if (message.isReceived) { + event.sendMessage( + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" + ) + } else { + event.sendMessage( + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" + ) + } + event.sendMessage(helpFormat(message.message)) + } + if (!hasMessage) { + event.sendMessage("You have no messages in the queue.") + } else { + event.sendMessage("To delete one or all delivered messages:") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) + ) + ) + event.sendMessage(help.last()) + } + } + + companion object { + /** + * Max days property. + */ + const val MAX_DAYS_PROP = "tell-max-days" + + /** + * Max size property. + */ + const val MAX_SIZE_PROP = "tell-max-size" + + // Arrow + private const val ARROW = " --> " + + // All keyword + private const val TELL_ALL_KEYWORD = "all" + + //T he delete command. + private const val TELL_DEL_KEYWORD = "del" + } + + /** + * Creates a new instance. + */ + init { + initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP) + + // Load the message queue + messages.addAll(TellManager.load(serialObject)) + if (clean()) { + save() + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt new file mode 100644 index 0000000..b65a4da --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -0,0 +1,74 @@ +/* + * TellManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import net.thauvin.erik.mobibot.Utils.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.time.Clock +import java.time.LocalDateTime + +/** + * The Tell Messages Manager. + */ +object TellManager { + private val logger: Logger = LoggerFactory.getLogger(TellManager::class.java) + + /** + * Cleans the messages queue. + */ + @JvmStatic + fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean { + if (logger.isDebugEnabled) logger.debug("Cleaning the messages.") + val today = LocalDateTime.now(Clock.systemUTC()) + return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) } + } + + /** + * Loads the messages. + */ + @JvmStatic + fun load(file: String): List<TellMessage> { + @Suppress("UNCHECKED_CAST") + return loadSerialData(file, emptyList<TellMessage>(), logger, "message queue") as List<TellMessage> + } + + /** + * Saves the messages. + */ + @JvmStatic + fun save(file: String, messages: List<TellMessage?>?) { + if (messages != null) { + saveSerialData(file, messages, logger, "messages") + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt new file mode 100644 index 0000000..d17fbb5 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -0,0 +1,104 @@ +/* + * TellMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import java.io.Serializable +import java.time.Clock +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +/** + * Tell Message. + */ +class TellMessage( + /** + * Returns the message's sender. + */ + val sender: String, + + /** + * Returns the message's recipient. + */ + val recipient: String, + + /** + * Returns the message text. + */ + val message: String +) : Serializable { + /** + * Returns the queued date/time. + */ + var queued: LocalDateTime = LocalDateTime.now(Clock.systemUTC()) + + /** + * Returns the message id. + */ + var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + + /** + * Returns `true` if a notification was sent. + */ + var isNotified = false + + /** + * Returns `true` if the message was received. + */ + var isReceived = false + set(value) { + if (value) { + receptionDate = LocalDateTime.now(Clock.systemUTC()) + } + field = value + } + + /** + * Returns the message creating date. + */ + var receptionDate: LocalDateTime = LocalDateTime.MIN + + /** + * Matches the message sender or recipient. + */ + fun isMatch(nick: String?): Boolean { + return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true) + } + + override fun toString(): String { + return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " + + "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") + } + + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 2L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt b/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt new file mode 100644 index 0000000..e8676ec --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt @@ -0,0 +1,54 @@ +/* + * Entries.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.entries + +import net.thauvin.erik.mobibot.Utils.today + +class Entries( + var channel: String = "", + var ircServer: String = "", + var logsDir: String = "", + var backlogs: String = "" +) { + val links = mutableListOf<EntryLink>() + + var lastPubDate = today() + + fun load() { + lastPubDate = FeedsManager.loadFeed(this) + } + + fun save() { + lastPubDate = today() + FeedsManager.saveFeed(this) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt new file mode 100644 index 0000000..9c09626 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -0,0 +1,83 @@ +/* + * EntriesUtils.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.green + +/** + * Entries utilities. + */ +object EntriesUtils { + /** + * Prints an entry's comment for display on the channel. + */ + @JvmStatic + fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = + ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + + /** + * Prints an entry's link for display on the channel. + */ + @JvmStatic + @JvmOverloads + fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { + val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") + .append('[').append(entry.nick).append(']') + if (isView && entry.comments.isNotEmpty()) { + buff.append("[+").append(entry.comments.size).append(']') + } + buff.append(' ') + with(entry) { + if (Constants.NO_TITLE == title) { + buff.append(title) + } else { + buff.append(title.bold()) + } + buff.append(" ( ").append(link.green()).append(" )") + } + return buff.toString() + } + + /** + * Prints an entry's tags/categories for display on the channel. e.g. L1T: tag1, tag2 + */ + @JvmStatic + fun printTags(entryIndex: Int, entry: EntryLink): String = + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") + + /** + * Builds link label based on its index. e.g: L1 + */ + @JvmStatic + fun Int.toLinkLabel(): String = Constants.LINK_CMD + (this + 1) +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt new file mode 100644 index 0000000..e18d692 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -0,0 +1,52 @@ +/* + * EntryComment.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Entry comments data class. + */ +data class EntryComment(var comment: String, var nick: String) : Serializable { + /** + * Creation date. + */ + val date: LocalDateTime = LocalDateTime.now() + + override fun toString(): String = "EntryComment{comment='$comment', date=$date, nick='$nick'}" + + companion object { + // Serial version UID + @Suppress("ConstPropertyName") + private const val serialVersionUID: Long = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt new file mode 100644 index 0000000..4a69446 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -0,0 +1,213 @@ +/* + * EntryLink.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import com.rometools.rome.feed.synd.SyndCategory +import com.rometools.rome.feed.synd.SyndCategoryImpl +import net.thauvin.erik.mobibot.commands.links.LinksManager +import java.io.Serializable +import java.util.* + +/** + * The class used to store link entries. + */ +class EntryLink( + // Link's comments + val comments: MutableList<EntryComment> = mutableListOf(), + + // Tags/categories + val tags: MutableList<SyndCategory> = mutableListOf(), + + // Channel + var channel: String, + + // Creation date + var date: Date = Calendar.getInstance().time, + + // Link's URL + var link: String, + + // Author's login + var login: String = "", + + // Author's nickname + var nick: String, + + // Link's title + var title: String +) : Serializable { + /** + * Creates a new entry. + */ + constructor( + link: String, + title: String, + nick: String, + login: String, + channel: String, + tags: List<String?> + ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { + setTags(tags) + } + + /** + * Creates a new entry. + */ + constructor( + link: String, + title: String, + nick: String, + channel: String, + date: Date, + tags: List<SyndCategory> + ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { + this.tags.addAll(tags) + } + + /** + * Adds a new comment + */ + fun addComment(comment: EntryComment): Int { + comments.add(comment) + return comments.lastIndex + } + + /** + * Adds a new comment. + */ + fun addComment(comment: String, nick: String): Int { + return addComment(EntryComment(comment, nick)) + } + + /** + * Deletes a specific comment. + */ + fun deleteComment(index: Int): Boolean { + if (index < comments.size) { + comments.removeAt(index) + return true + } + return false + } + + /** + * Deletes a comment. + */ + fun deleteComment(entryComment: EntryComment): Boolean { + return comments.remove(entryComment) + } + + /** + * Formats the tags. + */ + fun formatTags(sep: String, prefix: String = ""): String { + return tags.joinToString(separator = sep, prefix = prefix) { it.name } + } + + /** + * Returns a comment. + */ + fun getComment(index: Int): EntryComment = comments[index] + + /** + * Returns true if a string is contained in the link, title, or nick. + */ + fun matches(match: String?): Boolean { + return if (match.isNullOrEmpty()) { + false + } else { + link.contains(match, true) || title.contains(match, true) || nick.contains(match, true) + } + } + + /** + * Sets a comment. + */ + fun setComment(index: Int, comment: String?, nick: String?) { + if (index < comments.size && !comment.isNullOrBlank() && !nick.isNullOrBlank()) { + comments[index] = EntryComment(comment, nick) + } + } + + /** + * Sets the tags. + */ + fun setTags(tags: String) { + setTags(tags.split(LinksManager.TAG_MATCH)) + } + + /** + * Sets the tags. + */ + private fun setTags(tags: List<String?>) { + if (tags.isNotEmpty()) { + var category: SyndCategoryImpl + for (tag in tags) { + if (!tag.isNullOrBlank()) { + val t = tag.lowercase() + val mod = t[0] + if (mod == '-') { + // Don't remove the channel tag + if (channel.substring(1) != t.substring(1)) { + category = SyndCategoryImpl() + category.name = t.substring(1) + this.tags.remove(category) + } + } else { + category = SyndCategoryImpl() + if (mod == '+') { + category.name = t.substring(1) + } else { + category.name = t + } + if (!this.tags.contains(category)) { + this.tags.add(category) + } + } + } + } + } + } + + /** + * Returns a string representation of the object. + */ + override fun toString(): String { + return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," + + "nick='$nick', tags=$tags, title='$title'}") + } + + companion object { + // Serial version UID + @Suppress("ConstPropertyName") + private const val serialVersionUID: Long = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt new file mode 100644 index 0000000..f786cb2 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -0,0 +1,187 @@ +/* + * FeedsManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import com.rometools.rome.feed.synd.* +import com.rometools.rome.io.FeedException +import com.rometools.rome.io.SyndFeedInput +import com.rometools.rome.io.SyndFeedOutput +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.Utils.today +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.io.InputStreamReader +import java.io.OutputStreamWriter +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* +import kotlin.io.path.exists + +/** + * Manages the RSS feeds. + */ +class FeedsManager private constructor() { + companion object { + private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) + + // The file containing the current entries. + private const val CURRENT_XML = "current.xml" + + // The .xml extension. + private const val DOT_XML = ".xml" + + /** + * Loads the current feed. + */ + @JvmStatic + @Throws(IOException::class, FeedException::class) + fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String { + entries.links.clear() + val xml = Paths.get("${entries.logsDir}${currentFile}") + var pubDate = today() + if (xml.exists()) { + val input = SyndFeedInput() + InputStreamReader( + Files.newInputStream(xml), StandardCharsets.UTF_8 + ).use { reader -> + val feed = input.build(reader) + pubDate = feed.publishedDate.toIsoLocalDate() + val items = feed.entries + var entry: EntryLink + for (i in items.indices.reversed()) { + with(items[i]) { + entry = EntryLink( + link, + title, + author.substring(author.lastIndexOf('(') + 1, author.length - 1), + entries.channel, + publishedDate, + categories + ) + var split: List<String> + for (comment in description.value.split("<br/>")) { + split = comment.split(": ".toRegex(), 2) + if (split.size == 2) { + entry.addComment(comment = split[1].trim(), nick = split[0].trim()) + } + } + } + entries.links.add(entry) + } + } + } else { + // Create an empty feed. + saveFeed(entries) + } + return pubDate + } + + /** + * Saves the feeds. + */ + @JvmStatic + fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) { + if (logger.isDebugEnabled) logger.debug("Saving the feeds...") + if (entries.logsDir.isNotBlank()) { + try { + val output = SyndFeedOutput() + val rss: SyndFeed = SyndFeedImpl() + val items: MutableList<SyndEntry> = mutableListOf() + var item: SyndEntry + OutputStreamWriter( + Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 + ).use { fw -> + with(rss) { + feedType = "rss_2.0" + title = "${entries.channel} IRC Links" + description = "Links from ${entries.ircServer} on ${entries.channel}" + if (entries.backlogs.isNotBlank()) link = entries.backlogs + publishedDate = Calendar.getInstance().time + language = "en" + } + val buff: StringBuilder = StringBuilder() + for (i in entries.links.indices.reversed()) { + with(entries.links[i]) { + buff.setLength(0) + buff.append("Posted by <b>") + .append(nick) + .append("</b> on <a href=\"irc://") + .append(entries.ircServer).append('/') + .append(channel) + .append("\"><b>") + .append(channel) + .append("</b></a>") + if (comments.isNotEmpty()) { + buff.append(" <br/><br/>") + for (j in comments.indices) { + if (j > 0) { + buff.append(" <br/>") + } + buff.append(comments[j].nick).append(": ").append(comments[j].comment) + } + } + item = SyndEntryImpl() + item.link = link + item.description = SyndContentImpl().apply { value = buff.toString() } + item.title = title + item.publishedDate = date + item.author = "${channel.removePrefix("#")}@${entries.ircServer} ($nick)" + item.categories = tags + items.add(item) + } + } + rss.entries = items + if (logger.isDebugEnabled) logger.debug("Writing the entries feed.") + output.output(rss, fw) + } + OutputStreamWriter( + Files.newOutputStream( + Paths.get( + entries.logsDir + today() + DOT_XML + ) + ), StandardCharsets.UTF_8 + ).use { fw -> output.output(rss, fw) } + } catch (e: FeedException) { + if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e) + } catch (e: IOException) { + if (logger.isWarnEnabled) + logger.warn("An IO error occurred while generating the entries feed.", e) + } + } else { + if (logger.isWarnEnabled) { + logger.warn("Unable to generate the entries feed. A required property is missing.") + } + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt new file mode 100644 index 0000000..8c8e736 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -0,0 +1,131 @@ +/* + * AbstractModule.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent + +/** + * The `Module` abstract class. + */ +abstract class AbstractModule { + /** + * The module name. + */ + abstract val name: String + + /** + * The module's commands, if any. + */ + @JvmField + val commands: MutableList<String> = mutableListOf() + + @JvmField + val help: MutableList<String> = mutableListOf() + val properties: MutableMap<String, String> = mutableMapOf() + + /** + * Responds to a command. + */ + abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) + + /** + * Returns the module's property keys. + */ + val propertyKeys: Set<String> + get() = properties.keys + + /** + * Returns `true` if the module has properties. + */ + fun hasProperties(): Boolean { + return properties.isNotEmpty() + } + + /** + * Responds with the module's help. + */ + open fun helpResponse(event: GenericMessageEvent): Boolean { + for (h in help) { + event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent)) + } + return true + } + + /** + * Initializes the properties. + */ + fun initProperties(vararg keys: String) { + for (key in keys) { + properties[key] = "" + } + } + + /** + * Returns `true` if the module is enabled. + */ + val isEnabled: Boolean + get() = if (hasProperties()) { + isValidProperties + } else { + true + } + + /** + * Returns `true` if the module responds to private messages. + */ + open val isPrivateMsgEnabled: Boolean = false + + /** + * Ensures that all properties have values. + */ + open val isValidProperties: Boolean + get() { + for (s in properties.keys) { + if (properties[s].isNullOrBlank()) { + return false + } + } + return true + } + + /** + * Sets a property key and value. + */ + fun setProperty(key: String, value: String) { + if (key.isNotBlank()) { + properties[key] = value + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt b/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt new file mode 100644 index 0000000..b7aae28 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt @@ -0,0 +1,87 @@ +/* + * Calc.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.objecthunter.exp4j.ExpressionBuilder +import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.text.DecimalFormat + +/** + * The Calc module. + */ +class Calc : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Calc::class.java) + + override val name = "Calc" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + event.respond(calculate(args)) + } catch (e: IllegalArgumentException) { + if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e) + event.respond("No idea. This is the kind of math I don't get.") + } catch (e: UnknownFunctionOrVariableException) { + if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e) + event.respond("No idea. I must've some form of Dyscalculia.") + } + } else { + helpResponse(event) + } + } + + companion object { + // Calc command + private const val CALC_CMD = "calc" + + /** + * Performs a calculation. e.g.: 1 + 1 * 2 + */ + @JvmStatic + @Throws(IllegalArgumentException::class) + fun calculate(query: String): String { + val decimalFormat = DecimalFormat("#.##") + val calc = ExpressionBuilder(query).build() + return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()).bold() + } + } + + init { + commands.add(CALC_CMD) + help.add("To solve a mathematical calculation:") + help.add(helpFormat("%c $CALC_CMD <calculation>")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt new file mode 100644 index 0000000..bd92332 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -0,0 +1,176 @@ +/* + * ChatGpt.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.apache.commons.text.WordUtils +import org.json.JSONException +import org.json.JSONObject +import org.json.JSONWriter +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +class ChatGpt : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) + + override val name = CHATGPT_NAME + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val answer = chat( + args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() + ) + if (answer.isNotBlank()) { + event.sendMessage(WordUtils.wrap(answer, 400)) + } else { + event.respond("$name is stumped.") + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } catch (e: NumberFormatException) { + if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e) + event.respond("The $name module is misconfigured.") + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The service name. + */ + const val CHATGPT_NAME = "ChatGPT" + + /** + * The API Key property. + */ + const val API_KEY_PROP = "chatgpt-api-key" + + /** + * The max tokens property. + */ + const val MAX_TOKENS_PROP = "chatgpt-max-tokens" + + // ChatGPT API URL + private const val API_URL = "https://api.openai.com/v1/completions" + + // ChatGPT command + private const val CHATGPT_CMD = "chatgpt" + + + @JvmStatic + @Throws(ModuleException::class) + fun chat(query: String, apiKey: String?, maxTokens: Int): String { + if (!apiKey.isNullOrEmpty()) { + val prompt = JSONWriter.valueToString("Q:$query\nA:") + val request = HttpRequest.newBuilder() + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) + .POST( + HttpRequest.BodyPublishers.ofString( + """{ + "model": "text-davinci-003", + "prompt": $prompt, + "temperature": 0, + "max_tokens": $maxTokens, + "top_p": 1, + "frequency_penalty": 0, + "presence_penalty": 0 + }""".trimIndent() + ) + ) + .build() + try { + val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) + if (response.statusCode() == 200) { + try { + val jsonResponse = JSONObject(response.body()) + val choices = jsonResponse.getJSONArray("choices") + return choices.getJSONObject(0).getString("text").trim() + } catch (e: JSONException) { + throw ModuleException( + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", + e + ) + } + } else { + if (response.statusCode() == 429) { + throw ModuleException( + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." + ) + } else { + throw IOException("HTTP Status Code: " + response.statusCode()) + } + } + } catch (e: IOException) { + throw ModuleException( + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", + e + ) + } + } else { + throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.") + } + } + } + + init { + commands.add(CHATGPT_CMD) + with(help) { + add("To get answers from $name:") + add(Utils.helpFormat("%c $CHATGPT_CMD <query>")) + add("For example:") + add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) + add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) + } + initProperties(API_KEY_PROP, MAX_TOKENS_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt new file mode 100644 index 0000000..d14056e --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -0,0 +1,159 @@ +/* + * CryptoPrices.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.crypto.CryptoException +import net.thauvin.erik.crypto.CryptoPrice +import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.json.JSONObject +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException + +/** + * The Cryptocurrency Prices module. + */ +class CryptoPrices : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) + + override val name = "CryptoPrices" + + /** + * Returns the cryptocurrency market price from + * [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price). + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (CURRENCIES.isEmpty()) { + try { + loadCurrencies() + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + } + } + + val debugMessage = "crypto($cmd $args)" + if (args == CODES_KEYWORD) { + event.sendMessage("The supported currencies are:") + event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true) + } else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { + try { + val price = currentPrice(args.split(' ')) + val amount = try { + price.toCurrency() + } catch (ignore: IllegalArgumentException) { + price.amount + } + event.respond("${price.base} current price is $amount [${CURRENCIES[price.currency]}]") + } catch (e: CryptoException) { + if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) + e.message?.let { + event.respond(it) + } + } catch (e: IOException) { + if (logger.isErrorEnabled) logger.error(debugMessage, e) + event.respond("An IO error has occurred while retrieving the cryptocurrency market price.") + } + } else { + helpResponse(event) + } + + } + + companion object { + // Crypto command + private const val CRYPTO_CMD = "crypto" + + // Fiat Currencies + private val CURRENCIES: MutableMap<String, String> = mutableMapOf() + + // Currency codes keyword + private const val CODES_KEYWORD = "codes" + + /** + * Get current market price. + */ + @JvmStatic + fun currentPrice(args: List<String>): CryptoPrice { + return if (args.size == 2) + spotPrice(args[0], args[1]) + else + spotPrice(args[0]) + } + + /** + * For testing purposes. + */ + fun getCurrencyName(code: String): String? { + return CURRENCIES[code] + } + + /** + * Loads the Fiat currencies.. + */ + @JvmStatic + @Throws(ModuleException::class) + fun loadCurrencies() { + try { + val json = JSONObject(CryptoPrice.apiCall(listOf("currencies"))) + val data = json.getJSONArray("data") + for (i in 0 until data.length()) { + val d = data.getJSONObject(i) + CURRENCIES[d.getString("id")] = d.getString("name") + } + } catch (e: CryptoException) { + throw ModuleException( + "loadCurrencies(): CE", + "An error has occurred while retrieving the currencies table.", + e + ) + } + } + } + + init { + commands.add(CRYPTO_CMD) + with(help) { + add("To retrieve a cryptocurrency's market price:") + add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) + add("For example:") + add(helpFormat("%c $CRYPTO_CMD BTC")) + add(helpFormat("%c $CRYPTO_CMD ETH EUR")) + add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) + add("To list the supported currencies:") + add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD")) + } + loadCurrencies() + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt new file mode 100644 index 0000000..da0efd8 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -0,0 +1,222 @@ +/* + * CurrencyConverter.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.json.JSONObject +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URL +import java.text.DecimalFormat +import java.util.* + + +/** + * The CurrencyConverter module. + */ +class CurrencyConverter : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) + + override val name = "CurrencyConverter" + + // Reload currency codes + private fun reload(apiKey: String?) { + if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) { + try { + loadSymbols(apiKey) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + } + } + } + + /** + * Converts the specified currencies. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + reload(properties[API_KEY_PROP]) + + when { + SYMBOLS.isEmpty() -> { + event.respond(EMPTY_SYMBOLS_TABLE) + } + + args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { + val msg = convertCurrency(properties[API_KEY_PROP], args) + event.respond(msg.msg) + if (msg.isError) { + helpResponse(event) + } + } + + args.contains(CODES_KEYWORD) -> { + event.sendMessage("The supported currency codes are:") + event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) + } + + else -> { + helpResponse(event) + } + } + } + + override fun helpResponse(event: GenericMessageEvent): Boolean { + reload(properties[API_KEY_PROP]) + + if (SYMBOLS.isEmpty()) { + event.sendMessage(EMPTY_SYMBOLS_TABLE) + } else { + val nick = event.bot().nick + event.sendMessage("To convert from one currency to another:") + event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled) + ) + ) + event.sendMessage("To list the supported currency codes:") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) + ) + ) + } + return true + } + + companion object { + /** + * The API Key property. + */ + const val API_KEY_PROP = "exchangerate-api-key" + + // Currency command + private const val CURRENCY_CMD = "currency" + + // Currency codes keyword + private const val CODES_KEYWORD = "codes" + + // Empty symbols table. + private const val EMPTY_SYMBOLS_TABLE = "Sorry, but the currency table is empty." + + // Currency symbols + private val SYMBOLS: TreeMap<String, String> = TreeMap() + + // Decimal format + private val DECIMAL_FORMAT = DecimalFormat("0.00#") + + /** + * Converts from a currency to another. + */ + @JvmStatic + fun convertCurrency(apiKey: String?, query: String): Message { + if (apiKey.isNullOrEmpty()) { + throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.") + } + + val cmds = query.split(" ") + return if (cmds.size == 4) { + if (cmds[3] == cmds[1] || "0" == cmds[0]) { + PublicMessage("You're kidding, right?") + } else { + val to = cmds[1].uppercase() + val from = cmds[3].uppercase() + if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) { + try { + val amt = cmds[0].replace(",", "") + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt") + val body = url.reader().body + val json = JSONObject(body) + + if (json.getString("result") == "success") { + val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result")) + PublicMessage( + "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" + ) + } else { + ErrorMessage("Sorry, an error occurred while converting the currencies.") + } + } catch (ignore: IOException) { + ErrorMessage("Sorry, an IO error occurred while converting the currencies.") + } + } else { + ErrorMessage("Sounds like monopoly money to me!") + } + } + } else { + ErrorMessage("Invalid query. Let's try again.") + } + } + + /** + * Loads the currency ISO symbols. + */ + @JvmStatic + @Throws(ModuleException::class) + fun loadSymbols(apiKey: String?) { + if (!apiKey.isNullOrEmpty()) { + try { + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes") + val json = JSONObject(url.reader().body) + if (json.getString("result") == "success") { + val codes = json.getJSONArray("supported_codes") + for (i in 0 until codes.length()) { + val code = codes.getJSONArray(i) + SYMBOLS[code.getString(0)] = code.getString(1) + } + } + } catch (e: IOException) { + throw ModuleException( + "loadCodes(): IOE", + "An IO error has occurred while retrieving the currencies.", + e + ) + } + } + } + } + + init { + commands.add(CURRENCY_CMD) + initProperties(API_KEY_PROP) + loadSymbols(properties[ChatGpt.API_KEY_PROP]) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt b/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt new file mode 100644 index 0000000..8420fb1 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt @@ -0,0 +1,87 @@ +/* + * Dice.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + +/** + * The Dice module. + */ +class Dice : AbstractModule() { + override val name = "Dice" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + val arg = if (args.isBlank()) "2d6" else args.trim() + val match = Regex("^([1-9]|[12]\\d|3[0-2])[dD]([1-9]|[12]\\d|3[0-2])$").find(arg) + if (match != null) { + val (dice, sides) = match.destructured + event.respond("you rolled " + roll(dice.toInt(), sides.toInt())) + } else { + helpResponse(event) + } + } + + companion object { + // Dice command + private const val DICE_CMD = "dice" + + @JvmStatic + fun roll(dice: Int, sides: Int): String { + val result = StringBuilder() + var total = 0 + + repeat(dice) { + val roll = (1..sides).random() + total += roll + + if (result.isNotEmpty()) { + result.append(" + ") + } + + result.append(roll.bold()) + } + + if (dice != 1) { + result.append(" = ${total.bold()}") + } + + return result.toString() + } + } + + init { + commands.add(DICE_CMD) + help.add("To roll 2 dice with 6 sides:") + help.add(helpFormat("%c $DICE_CMD [2d6]")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt new file mode 100644 index 0000000..f426d1e --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -0,0 +1,162 @@ +/* + * GoogleSearch.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.ReleaseInfo +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import org.json.JSONException +import org.json.JSONObject +import org.pircbotx.Colors +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URL + +/** + * The GoogleSearch module. + */ +class GoogleSearch : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) + + override val name = "GoogleSearch" + + /** + * Searches Google. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val results = searchGoogle( + args, + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], + event.user.nick + ) + for (msg in results) { + if (msg.isError) { + event.respond(msg.msg.colorize(msg.color)) + } else { + event.sendMessage(channel, msg) + } + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + // Google API Key property + const val API_KEY_PROP = "google-api-key" + + // Google Custom Search Engine ID property + const val CSE_KEY_PROP = "google-cse-cx" + + // Google command + private const val GOOGLE_CMD = "google" + + /** + * Performs a search on Google. + */ + @JvmStatic + @Throws(ModuleException::class) + fun searchGoogle( + query: String, + apiKey: String?, + cseKey: String?, + quotaUser: String = ReleaseInfo.PROJECT + ): List<Message> { + if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { + throw ModuleException( + "${GoogleSearch::class.java.name} is disabled.", + "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." + ) + } + val results = mutableListOf<Message>() + if (query.isNotBlank()) { + try { + val url = URL( + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + + ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" + ) + val json = JSONObject(url.reader().body) + if (json.has("items")) { + val ja = json.getJSONArray("items") + for (i in 0 until ja.length()) { + val j = ja.getJSONObject(i) + results.add(NoticeMessage(j.getString("title").unescapeXml())) + results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) + } + } else if (json.has("error")) { + val error = json.getJSONObject("error") + val message = error.getString("message") + throw ModuleException("searchGoogle($query): ${error.getInt("code")} : $message", message) + } else { + results.add(ErrorMessage("No results found.", Colors.RED)) + } + } catch (e: IOException) { + throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) + } catch (e: JSONException) { + throw ModuleException( + "searchGoogle($query): JSON", + "A JSON error has occurred searching Google.", + e + ) + } + } else { + results.add(ErrorMessage("Invalid query. Please try again.")) + } + return results + } + } + + init { + commands.add(GOOGLE_CMD) + help.add("To search Google:") + help.add(helpFormat("%c $GOOGLE_CMD <query>")) + initProperties(API_KEY_PROP, CSE_KEY_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt b/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt new file mode 100644 index 0000000..2760fa7 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt @@ -0,0 +1,105 @@ +/* + * Joke.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.jokeapi.exceptions.HttpErrorException +import net.thauvin.erik.jokeapi.exceptions.JokeException +import net.thauvin.erik.jokeapi.joke +import net.thauvin.erik.jokeapi.models.Type +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.json.JSONException +import org.pircbotx.Colors +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException + +/** + * The Joke module. + */ +class Joke : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) + + override val name = "Joke" + + /** + * Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/). + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + try { + randomJoke().forEach { + sendIRC().notice(channel, it.msg.colorize(it.color)) + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } + } + + companion object { + // Joke command + private const val JOKE_CMD = "joke" + + /** + * Retrieves a random joke. + */ + @JvmStatic + @Throws(ModuleException::class) + fun randomJoke(): List<Message> { + return try { + val joke = joke(safe = true, type = Type.SINGLE, splitNewLine = true) + joke.joke.map { PublicMessage(it, Colors.CYAN) } + } catch (e: JokeException) { + throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e) + } catch (e: HttpErrorException) { + throw ModuleException("randomJoke(): HTTP: ${e.statusCode}", e.message, e) + } catch (e: IOException) { + throw ModuleException("randomJoke(): IOE", "An IO error has occurred retrieving a random joke.", e) + } catch (e: JSONException) { + throw ModuleException("randomJoke(): JSON", "A parsing error has occurred retrieving a random joke.", e) + } + } + } + + init { + commands.add(JOKE_CMD) + help.add("To display a random joke:") + help.add(helpFormat("%c $JOKE_CMD")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt b/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt new file mode 100644 index 0000000..9ab2ead --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -0,0 +1,171 @@ +/* + * Lookup.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.apache.commons.net.whois.WhoisClient +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.InetAddress +import java.net.UnknownHostException + +/** + * The Lookup module. + */ +class Lookup : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java) + + override val name = "Lookup" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.matches("(\\S.)+(\\S)+".toRegex())) { + try { + event.respondWith(nslookup(args).prependIndent()) + } catch (ignore: UnknownHostException) { + if (args.matches( + ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + .toRegex() + ) + ) { + try { + val lines = whois(args) + if (lines.isNotEmpty()) { + var line: String + var hasData = false + for (rawLine in lines) { + line = rawLine.trim() + if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) { + if (!hasData) { + event.respondWith(line) + hasData = true + } else { + event.bot().sendIRC().notice(event.user.nick, line) + } + } + } + } else { + event.respond("Unknown host.") + } + } catch (ioe: IOException) { + if (logger.isWarnEnabled) { + logger.warn("Unable to perform whois IP lookup: $args", ioe) + } + event.respond("Unable to perform whois IP lookup: ${ioe.message}") + } + } else { + event.respond("Unknown host.") + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The whois default host. + */ + const val WHOIS_HOST = "whois.arin.net" + + // Lookup command + private const val LOOKUP_CMD = "lookup" + + /** + * Performs a DNS lookup on the specified query. + */ + @JvmStatic + @Throws(UnknownHostException::class) + fun nslookup(query: String): String { + val buffer = StringBuilder() + val results = InetAddress.getAllByName(query) + var hostInfo: String + for (result in results) { + if (result.hostAddress == query) { + hostInfo = result.hostName + if (hostInfo == query) { + throw UnknownHostException() + } + } else { + hostInfo = result.hostAddress + } + if (buffer.isNotEmpty()) { + buffer.append(", ") + } + buffer.append(hostInfo) + } + return buffer.toString() + } + + /** + * Performs a whois IP query. + */ + @Throws(IOException::class) + private fun whois(query: String): List<String> { + return whois(query, WHOIS_HOST) + } + + /** + * Performs a whois IP query. + */ + @JvmStatic + @Throws(IOException::class) + fun whois(query: String, host: String): List<String> { + val whoisClient = WhoisClient() + val lines: List<String> + with(whoisClient) { + try { + defaultTimeout = Constants.CONNECT_TIMEOUT + connect(host) + soTimeout = Constants.CONNECT_TIMEOUT + setSoLinger(false, 0) + lines = if (WHOIS_HOST == host) { + query("n - $query").split("\n") + } else { + query(query).split("\n") + } + } finally { + disconnect() + } + } + return lines + } + } + + init { + commands.add(LOOKUP_CMD) + help.add("To perform a DNS lookup query:") + help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt b/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt new file mode 100644 index 0000000..3be3a5f --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -0,0 +1,149 @@ +/* + * Mastodon.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.prefixIfMissing +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.social.SocialModule +import org.json.JSONException +import org.json.JSONObject +import org.json.JSONWriter +import java.io.IOException +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +class Mastodon : SocialModule() { + override val name = "Mastodon" + + override val handle: String? + get() = properties[HANDLE_PROP] + + override val isAutoPost: Boolean + get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() + + override val isValidProperties: Boolean + get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank()) + + /** + * Formats the entry for posting. + */ + override fun formatEntry(entry: EntryLink): String { + return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}" + } + + private fun formatTags(entry: EntryLink): String { + return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } + .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } + } + + /** + * Posts on Mastodon. + */ + @Throws(ModuleException::class) + override fun post(message: String, isDm: Boolean): String { + return toot( + apiKey = properties[ACCESS_TOKEN_PROP], + instance = properties[INSTANCE_PROP], + handle = handle, + message = message, + isDm = isDm + ) + } + + companion object { + // Property keys + const val ACCESS_TOKEN_PROP = "mastodon-access-token" + const val AUTO_POST_PROP = "mastodon-auto-post" + const val HANDLE_PROP = "mastodon-handle" + const val INSTANCE_PROP = "mastodon-instance" + + private const val MASTODON_CMD = "mastodon" + private const val TOOT_CMD = "toot" + + /** + * Post on Mastodon. + */ + @JvmStatic + @Throws(ModuleException::class) + fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { + val request = HttpRequest.newBuilder() + .uri(URI.create("https://$instance/api/v1/statuses")) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .POST( + HttpRequest.BodyPublishers.ofString( + JSONWriter.valueToString( + if (isDm) { + mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + } else { + mapOf("status" to message) + } + ) + ) + ) + .build() + try { + val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) + if (response.statusCode() == 200) { + return try { + val jsonResponse = JSONObject(response.body()) + if (isDm) { + jsonResponse.getString("content") + } else { + "Your message was posted to ${jsonResponse.getString("url")}" + } + } catch (e: JSONException) { + throw ModuleException("mastodonPost($message)", "A JSON error has occurred: ${e.message}", e) + } + } else { + throw IOException("Status Code: " + response.statusCode()) + } + } catch (e: IOException) { + throw ModuleException("mastodonPost($message)", "An IO error has occurred: ${e.message}", e) + } catch (e: InterruptedException) { + throw ModuleException("mastodonPost($message)", "An error has occurred: ${e.message}", e) + } + } + } + + init { + commands.add(MASTODON_CMD) + commands.add(TOOT_CMD) + help.add("To toot on Mastodon:") + help.add(Utils.helpFormat("%c $TOOT_CMD <message>")) + properties[AUTO_POST_PROP] = "false" + initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt b/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt new file mode 100644 index 0000000..a7416c2 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -0,0 +1,45 @@ +/* + * ModuleException.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +/** + * The `ModuleException` class. + */ +class ModuleException @JvmOverloads constructor( + val debugMessage: String, + message: String? = null, + cause: Throwable? = null +) : Exception(message, cause) { + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt b/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt new file mode 100644 index 0000000..944dbc1 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt @@ -0,0 +1,83 @@ +/* + * Ping.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + +/** + * The Ping module. + */ +class Ping : AbstractModule() { + override val name = "Ping" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + event.bot().sendIRC().action(channel, randomPing()) + } + + companion object { + /** + * The ping responses. + */ + @JvmField + val PINGS = listOf( + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!" + ) + + @JvmStatic + fun randomPing(): String { + return PINGS[PINGS.indices.random()] + } + + /** + * The ping command. + */ + private const val PING_CMD = "ping" + } + + init { + commands.add(PING_CMD) + help.add("To ping the bot:") + help.add(helpFormat("%c $PING_CMD")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt new file mode 100644 index 0000000..a299d8d --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -0,0 +1,114 @@ +/* + * RockPaperScissors.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + + +/** + * Simple module example in Kotlin. + */ +class RockPaperScissors : AbstractModule() { + override val name = "RockPaperScissors" + + init { + with(commands) { + add(Hands.ROCK.name.lowercase()) + add(Hands.PAPER.name.lowercase()) + add(Hands.SCISSORS.name.lowercase()) + } + + with(help) { + add("To play Rock Paper Scissors:") + add( + helpFormat( + "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" + ) + ) + } + } + + enum class Hands(val action: String) { + ROCK("crushes") { + override fun beats(hand: Hands): Boolean { + return hand == SCISSORS + } + }, + PAPER("covers") { + override fun beats(hand: Hands): Boolean { + return hand == ROCK + } + }, + SCISSORS("cuts") { + override fun beats(hand: Hands): Boolean { + return hand == PAPER + } + }; + + abstract fun beats(hand: Hands): Boolean + } + + companion object { + // For testing. + fun winLoseOrDraw(player: String, bot: String): String { + val hand = Hands.valueOf(player.uppercase()) + val botHand = Hands.valueOf(bot.uppercase()) + + return when { + hand == botHand -> "draw" + hand.beats(botHand) -> "win" + else -> "lose" + } + } + } + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + val hand = Hands.valueOf(cmd.uppercase()) + val botHand = Hands.entries[(0..Hands.entries.size).random()] + when { + hand == botHand -> { + event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.") + } + + hand.beats(botHand) -> { + event.respond("${hand.name.bold()} ${hand.action} ${botHand.name} » You ${"win".bold()}!") + } + + else -> { + event.respond("${botHand.name.bold()} ${botHand.action} ${hand.name} » You ${"lose".bold()}!") + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt b/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt new file mode 100644 index 0000000..dcae5e7 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -0,0 +1,236 @@ +/* + * StockQuote.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.json.JSONException +import org.json.JSONObject +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URL + +/** + * The StockQuote module. + */ +class StockQuote : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) + + override val name = "StockQuote" + + /** + * Returns the specified stock quote from Alpha Vantage. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val messages = getQuote(args, properties[API_KEY_PROP]) + for (msg in messages) { + event.sendMessage(channel, msg) + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The API property key. + */ + const val API_KEY_PROP = "alphavantage-api-key" + + /** + * The Invalid Symbol error string. + */ + const val INVALID_SYMBOL = "Invalid symbol." + + // API URL + private const val API_URL = "https://www.alphavantage.co/query?function=" + + // Quote command + private const val STOCK_CMD = "stock" + + @Throws(ModuleException::class) + private fun getJsonResponse(response: String, debugMessage: String): JSONObject { + return if (response.isNotBlank()) { + val json = JSONObject(response) + try { + val info = json.getString("Information") + if (info.isNotEmpty()) { + throw ModuleException(debugMessage, info.unescapeXml()) + } + } catch (ignore: JSONException) { + // Do nothing + } + try { + var error = json.getString("Note") + if (error.isNotEmpty()) { + throw ModuleException(debugMessage, error.unescapeXml()) + } + error = json.getString("Error Message") + if (error.isNotEmpty()) { + throw ModuleException(debugMessage, error.unescapeXml()) + } + } catch (ignore: JSONException) { + // Do nothing + } + json + } else { + throw ModuleException(debugMessage, "Empty Response.") + } + } + + /** + * Retrieves a stock quote. + */ + @JvmStatic + @Throws(ModuleException::class) + fun getQuote(symbol: String, apiKey: String?): List<Message> { + if (apiKey.isNullOrBlank()) { + throw ModuleException( + "${StockQuote::class.java.name} is disabled.", + "${STOCK_CMD.capitalise()} is disabled. The API key is missing." + ) + } + val messages = mutableListOf<Message>() + if (symbol.isNotBlank()) { + val debugMessage = "getQuote($symbol)" + var response: String + try { + with(messages) { + // Search for symbol/keywords + response = URL( + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + + apiKey.encodeUrl() + ).reader().body + var json = getJsonResponse(response, debugMessage) + val symbols = json.getJSONArray("bestMatches") + if (symbols.isEmpty) { + messages.add(ErrorMessage(INVALID_SYMBOL)) + } else { + val symbolInfo = symbols.getJSONObject(0) + + // Get quote for symbol + response = URL( + "${API_URL}GLOBAL_QUOTE&symbol=" + + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + + apiKey.encodeUrl() + ).reader().body + json = getJsonResponse(response, debugMessage) + val quote = json.getJSONObject("Global Quote") + if (quote.isEmpty) { + add(ErrorMessage(INVALID_SYMBOL)) + } else { + + add( + PublicMessage( + "Symbol: " + quote.getString("01. symbol").unescapeXml() + + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' + ) + ) + + val pad = 10 + + add( + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + quote.getString("05. price").unescapeXml() + ) + ) + add( + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + quote.getString("08. previous close").unescapeXml() + ) + ) + + val data = arrayOf( + "Open" to "02. open", + "High" to "03. high", + "Low" to "04. low", + "Volume" to "06. volume", + "Latest" to "07. latest trading day" + ) + + data.forEach { + add( + NoticeMessage( + "${it.first}:".padEnd(pad).prependIndent() + + quote.getString(it.second).unescapeXml() + ) + ) + } + + add( + NoticeMessage( + "Change:".padEnd(pad).prependIndent() + + quote.getString("09. change").unescapeXml() + + " [" + quote.getString("10. change percent").unescapeXml() + ']' + ) + ) + } + } + } + } catch (e: IOException) { + throw ModuleException("$debugMessage: IOE", "An IO error has occurred retrieving a stock quote.", e) + } catch (e: NullPointerException) { + throw ModuleException("$debugMessage: NPE", "An error has occurred retrieving a stock quote.", e) + } + } else { + messages.add(ErrorMessage(INVALID_SYMBOL)) + } + return messages + } + } + + init { + commands.add(STOCK_CMD) + help.add("To retrieve a stock quote:") + help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>")) + initProperties(API_KEY_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/War.class b/bin/main/net/thauvin/erik/mobibot/modules/War.class new file mode 100644 index 0000000000000000000000000000000000000000..6c36e541b639c5b04b624194b3b2df6b3d11ba55 GIT binary patch literal 2452 zcmeHJUvm>T5MMb??6`r@5CVjMYDuZxQXf#BNMXjcP07@D7&{5m;i02DE4IR&<VrfX zdE`?u)6T$qzXso<)79MtNOEL4Q<&*PAFN&Jw7b7u?N9QbfBpUv0Pe%LB`6SBliYfC z%;Kq#9@pZT7b#!(%Ay>`AvfL=rb|#HaAuE9nHMrS@;3cF9#{gi&Cd4s0|IBCcHVv* zSS{peoj|#@(dcgWTDW2EQM=LKa!S9^Yi)K3Tuv<v4`MCszBh0R?=v}65!%iT9yeNT zG$^*(%^wLYKg|tMdeZ3s-0ZdpEaf3M)l|}0ChZOjV_t?O0yDodjqbk_QrHJLt=4uh zNK*}Y3C<H(e49sGYo*I@krZBUOU;c6r+i2Q6-`9QtWc6pwDLn9nR~P{zcuDmL=&Yg z#fsLmiq>64>sdwX+g5~z224t2sn6+w(|>E-@Qu|hu-)Xuasp7RD5G|N!YlKbK!jD! z$1&#NB*(ro|K1NL=M-@}Rzo`Cw#On~tx>g`z@XSvG>VO-YRa`1Lr#@;^}}*<X%Vc6 zCtN$kH1(0zDD#))(C21?2h6K!%iW|$<qurq0R&xc3>)$G6kUHP2)+u3S=<^GhQ@Np zcZI|b*~LsOc0&Rl^E`z7KWkQj6}YqjXW=q|bJgQyax_`&V1m6k^10q(*!hliDq!Ib z)56WO#iAVxL*Pc|pIj}-^-|Uz$nt9Kcw_>X`mwYk;u~@*jKDw*$Do9R80UIN>5;d` zt*@Do#!Kjxe)M|E_Tn&HFT)K2zno0t0~UY4o+WVYDD*9C=|k`rk2EuZQBL`fmnCP# z)nOuZfVcN=-dL$;#&b<*yc`~^y8~T2itaJf$WzPpI}poolaS{p*Y3>~_&-5MAL%+E zP#LIL2mGOM%q!!~Qg@FP(se@ycnu0;@qT+GxMn&S@0Z{<fzyYd1_BFRVGKUct_=;` z27CeW&p-)QVHW04TSYyU<xI{)C0YL@n=inrWPTbJ(fSPP1^5)b;J*M^#_=4k+)d^H zl|NwRr=<%oVD(k9?+T7{xCh{KxQgRKqF3M=P@EC?4A*DiI_hrM8p`9|zR2;`&GGgn zT9<(=L*j=(6>8}FD_p%;%$%no-W?CZ&EmEc{MR|*w%-%(Dil}H5OJ?yj!qO(fi-r? U%)mD|7N8CuN`O1~b69Tu4Ue7BiU0rr literal 0 HcmV?d00001 diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt b/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt new file mode 100644 index 0000000..80a06fa --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -0,0 +1,250 @@ +/* + * Weather2.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.aksingh.owmjapis.api.APIException +import net.aksingh.owmjapis.core.OWM +import net.aksingh.owmjapis.core.OWM.Country +import net.aksingh.owmjapis.model.CurrentWeather +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.capitalizeWords +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.pircbotx.Colors +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import kotlin.math.roundToInt + +/** + * The `Weather2` module. + */ +class Weather2 : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) + + override val name = "Weather" + + /** + * Fetches the weather data from a specific city. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val messages = getWeather(args, properties[API_KEY_PROP]) + if (messages[0].isError) { + helpResponse(event) + } else { + for (msg in messages) { + event.sendMessage(channel, msg) + } + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The OpenWeatherMap API Key property. + */ + const val API_KEY_PROP = "owm-api-key" + + // Weather command + private const val WEATHER_CMD = "weather" + + /** + * Converts and rounds temperature from °F to °C. + */ + fun ftoC(d: Double): Pair<Int, Int> { + val c = (d - 32) * 5 / 9 + return d.roundToInt() to c.roundToInt() + } + + /** + * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. + */ + fun getCountry(countryCode: String): Country { + for (c in Country.entries) { + if (c.value.equals(countryCode, ignoreCase = true)) { + return c + } + } + return Country.UNITED_STATES + } + + /** + * Retrieves the weather data. + */ + @JvmStatic + @Throws(ModuleException::class) + fun getWeather(query: String, apiKey: String?): List<Message> { + if (apiKey.isNullOrBlank()) { + throw ModuleException( + "${Weather2::class.java.name} is disabled.", + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." + ) + } + val owm = OWM(apiKey) + val messages = mutableListOf<Message>() + owm.unit = OWM.Unit.IMPERIAL + if (query.isNotBlank()) { + val argv = query.split(",") + if (argv.size in 1..2) { + val city = argv[0].trim() + val code: String = if (argv.size > 1 && argv[1].isNotBlank()) { + argv[1].trim() + } else { + "US" + } + try { + val country = getCountry(code) + val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) { + owm.currentWeatherByZipCode(city.toInt(), country) + } else { + owm.currentWeatherByCityName(city, country) + } + if (cwd.hasCityName()) { + messages.add( + PublicMessage( + "City: ${cwd.cityName}, " + + country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" + ) + ) + cwd.mainData?.let { + with(it) { + if (hasTemp()) { + temp?.let { t -> + val (f, c) = ftoC(t) + messages.add(PublicMessage("Temperature: ${f}°F, ${c}°C")) + } + } + if (hasHumidity()) { + humidity?.let { h -> + messages.add(NoticeMessage("Humidity: ${h.roundToInt()}%")) + } + } + } + } + if (cwd.hasWindData()) { + cwd.windData?.let { + if (it.hasSpeed()) { + it.speed?.let { s -> + val w = mphToKmh(s) + messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h")) + } + } + } + } + if (cwd.hasWeatherList()) { + val condition = StringBuilder("Condition:") + cwd.weatherList?.let { + for (w in it) { + w?.let { + condition.append(' ') + .append(w.getDescription().capitalise()) + .append('.') + } + } + messages.add(NoticeMessage(condition.toString())) + } + } + if (cwd.hasCityId()) { + cwd.cityId?.let { + if (it > 0) { + messages.add( + NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) + ) + } else { + messages.add( + NoticeMessage( + "https://openweathermap.org/find?q=" + + "$city,${code.uppercase()}".encodeUrl(), + Colors.GREEN + ) + ) + } + } + } + } + } catch (e: APIException) { + if (e.code == 404) { + throw ModuleException( + "getWeather($query): API ${e.code}", + "The requested city was not found.", + e + ) + } else { + throw ModuleException("getWeather($query): API ${e.code}", e.message, e) + } + } catch (e: NullPointerException) { + throw ModuleException("getWeather($query): NPE", "Unable to perform weather lookup.", e) + } + } + } + if (messages.isEmpty()) { + messages.add(ErrorMessage("Invalid syntax.")) + } + return messages + } + + /** + * Converts and rounds temperature from mph to km/h. + */ + fun mphToKmh(w: Double): Pair<Int, Int> { + val kmh = w * 1.60934 + return w.roundToInt() to kmh.roundToInt() + } + } + + init { + commands.add(WEATHER_CMD) + with(help) { + add("To display weather information:") + add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) + add("For example:") + add(helpFormat("%c $WEATHER_CMD paris, fr")) + add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") + } + initProperties(API_KEY_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt new file mode 100644 index 0000000..a72efab --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -0,0 +1,142 @@ +/* + * WolframAlpha.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.isHttpSuccess +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URL + +class WolframAlpha : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java) + + override val name = "WolframAlpha" + + private fun getUnits(unit: String?): String { + return if (unit?.lowercase() == METRIC) { + METRIC + } else { + IMPERIAL + } + } + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val query = args.trim().split("units=", limit = 2, ignoreCase = true) + event.sendMessage( + queryWolfram( + query[0].trim(), + units = if (query.size == 2) { + getUnits(query[1].trim()) + } else { + getUnits(properties[UNITS_PROP]) + }, + appId = properties[APPID_KEY_PROP] + ) + ) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The Wolfram Alpha API Key property. + */ + const val APPID_KEY_PROP = "wolfram-appid" + + /** + * The Wolfram units properties + */ + const val UNITS_PROP = "wolfram-units" + + const val METRIC = "metric" + const val IMPERIAL = "imperial" + + // Wolfram command + private const val WOLFRAM_CMD = "wolfram" + + // Wolfram Alpha API URL + private const val API_URL = "http://api.wolframalpha.com/v1/spoken?appid=" + + @JvmStatic + @Throws(ModuleException::class) + fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String { + if (!appId.isNullOrEmpty()) { + try { + val urlReader = URL("${API_URL}${appId}&units=${units}&i=" + query.encodeUrl()).reader() + if (urlReader.responseCode.isHttpSuccess()) { + return urlReader.body + } else { + throw ModuleException( + "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", + urlReader.body.ifEmpty { + "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" + } + ) + } + } catch (ioe: IOException) { + throw ModuleException( + "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe + ) + } + } else { + throw ModuleException("wolfram($query): No API Key", "No Wolfram Alpha API key specified.") + } + } + } + + init { + commands.add(WOLFRAM_CMD) + with(help) { + add("To get answers from Wolfram Alpha:") + add(Utils.helpFormat("%c $WOLFRAM_CMD <query> [units=(${METRIC}|${IMPERIAL})]")) + add("For example:") + add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas")) + add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric")) + } + initProperties(APPID_KEY_PROP, UNITS_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt b/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt new file mode 100644 index 0000000..18072bc --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -0,0 +1,390 @@ +/* + * WorldTime.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.pircbotx.hooks.types.GenericMessageEvent +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField + +/** + * The WorldTime module. + */ +class WorldTime : AbstractModule() { + override val name = "WorldTime" + + companion object { + /** + * Beats (Internet Time) keyword + */ + const val BEATS_KEYWORD = ".beats" + + /** + * Supported countries + */ + val COUNTRIES_MAP = buildMap<String, String> { + put("AG", "America/Antigua") + put("AI", "America/Anguilla") + put("AE", "Asia/Dubai") + put("AD", "Europe/Andorra") + put("AKDT", "America/Anchorage") + put("AF", "Asia/Kabul") + put("AKST", "America/Anchorage") + put("AL", "Europe/Tirane") + put("AM", "Asia/Yerevan") + put("AO", "Africa/Luanda") + put("AQ", "Antarctica/South_Pole") + put("AR", "America/Argentina/Buenos_Aires") + put("AS", "Pacific/Pago_Pago") + put("AT", "Europe/Vienna") + put("AU", "Australia/Sydney") + put("AW", "America/Aruba") + put("AX", "Europe/Mariehamn") + put("AZ", "Asia/Baku") + put("BA", "Europe/Sarajevo") + put("BB", "America/Barbados") + put("BD", "Asia/Dhaka") + put("BE", "Europe/Brussels") + put("BEAT", BEATS_KEYWORD) + put("BF", "Africa/Ouagadougou") + put("BG", "Europe/Sofia") + put("BH", "Asia/Bahrain") + put("BI", "Africa/Bujumbura") + put("BJ", "Africa/Porto-Novo") + put("BL", "America/St_Barthelemy") + put("BM", "Atlantic/Bermuda") + put("BMT", BEATS_KEYWORD) + put("BN", "Asia/Brunei") + put("BO", "America/La_Paz") + put("BQ", "America/Kralendijk") + put("BR", "America/Sao_Paulo") + put("BS", "America/Nassau") + put("BT", "Asia/Thimphu") + put("BW", "Africa/Gaborone") + put("BY", "Europe/Minsk") + put("BZ", "America/Belize") + put("CA", "America/Montreal") + put("CC", "Indian/Cocos") + put("CD", "Africa/Kinshasa") + put("CDT", "America/Chicago") + put("CET", "CET") + put("CF", "Africa/Bangui") + put("CG", "Africa/Brazzaville") + put("CH", "Europe/Zurich") + put("CI", "Africa/Abidjan") + put("CK", "Pacific/Rarotonga") + put("CL", "America/Santiago") + put("CM", "Africa/Douala") + put("CN", "Asia/Shanghai") + put("CO", "America/Bogota") + put("CR", "America/Costa_Rica") + put("CST", "America/Chicago") + put("CU", "Cuba") + put("CV", "Atlantic/Cape_Verde") + put("CW", "America/Curacao") + put("CX", "Indian/Christmas") + put("CY", "Asia/Nicosia") + put("CZ", "Europe/Prague") + put("DE", "Europe/Berlin") + put("DJ", "Africa/Djibouti") + put("DK", "Europe/Copenhagen") + put("DM", "America/Dominica") + put("DO", "America/Santo_Domingo") + put("DZ", "Africa/Algiers") + put("EC", "Pacific/Galapagos") + put("EDT", "America/New_York") + put("EE", "Europe/Tallinn") + put("EG", "Africa/Cairo") + put("EH", "Africa/El_Aaiun") + put("ER", "Africa/Asmara") + put("ES", "Europe/Madrid") + put("EST", "America/New_York") + put("ET", "Africa/Addis_Ababa") + put("FI", "Europe/Helsinki") + put("FJ", "Pacific/Fiji") + put("FK", "Atlantic/Stanley") + put("FM", "Pacific/Yap") + put("FO", "Atlantic/Faroe") + put("FR", "Europe/Paris") + put("GA", "Africa/Libreville") + put("GB", "Europe/London") + put("GD", "America/Grenada") + put("GE", "Asia/Tbilisi") + put("GF", "America/Cayenne") + put("GG", "Europe/Guernsey") + put("GH", "Africa/Accra") + put("GI", "Europe/Gibraltar") + put("GL", "America/Thule") + put("GM", "Africa/Banjul") + put("GMT", "GMT") + put("GN", "Africa/Conakry") + put("GP", "America/Guadeloupe") + put("GQ", "Africa/Malabo") + put("GR", "Europe/Athens") + put("GS", "Atlantic/South_Georgia") + put("GT", "America/Guatemala") + put("GU", "Pacific/Guam") + put("GW", "Africa/Bissau") + put("GY", "America/Guyana") + put("HK", "Asia/Hong_Kong") + put("HN", "America/Tegucigalpa") + put("HR", "Europe/Zagreb") + put("HST", "Pacific/Honolulu") + put("HT", "America/Port-au-Prince") + put("HU", "Europe/Budapest") + put("ID", "Asia/Jakarta") + put("IE", "Europe/Dublin") + put("IL", "Asia/Tel_Aviv") + put("IM", "Europe/Isle_of_Man") + put("IN", "Asia/Kolkata") + put("IO", "Indian/Chagos") + put("IQ", "Asia/Baghdad") + put("IR", "Asia/Tehran") + put("IS", "Atlantic/Reykjavik") + put("IT", "Europe/Rome") + put("JE", "Europe/Jersey") + put("JM", "Jamaica") + put("JO", "Asia/Amman") + put("JP", "Asia/Tokyo") + put("KE", "Africa/Nairobi") + put("KG", "Asia/Bishkek") + put("KH", "Asia/Phnom_Penh") + put("KI", "Pacific/Tarawa") + put("KM", "Indian/Comoro") + put("KN", "America/St_Kitts") + put("KP", "Asia/Pyongyang") + put("KR", "Asia/Seoul") + put("KW", "Asia/Riyadh") + put("KY", "America/Cayman") + put("KZ", "Asia/Oral") + put("LA", "Asia/Vientiane") + put("LB", "Asia/Beirut") + put("LC", "America/St_Lucia") + put("LI", "Europe/Vaduz") + put("LK", "Asia/Colombo") + put("LR", "Africa/Monrovia") + put("LS", "Africa/Maseru") + put("LT", "Europe/Vilnius") + put("LU", "Europe/Luxembourg") + put("LV", "Europe/Riga") + put("LY", "Africa/Tripoli") + put("MA", "Africa/Casablanca") + put("MC", "Europe/Monaco") + put("MD", "Europe/Chisinau") + put("MDT", "America/Denver") + put("ME", "Europe/Podgorica") + put("MF", "America/Marigot") + put("MG", "Indian/Antananarivo") + put("MH", "Pacific/Majuro") + put("MK", "Europe/Skopje") + put("ML", "Africa/Timbuktu") + put("MM", "Asia/Yangon") + put("MN", "Asia/Ulaanbaatar") + put("MO", "Asia/Macau") + put("MP", "Pacific/Saipan") + put("MQ", "America/Martinique") + put("MR", "Africa/Nouakchott") + put("MS", "America/Montserrat") + put("MST", "America/Denver") + put("MT", "Europe/Malta") + put("MU", "Indian/Mauritius") + put("MV", "Indian/Maldives") + put("MW", "Africa/Blantyre") + put("MX", "America/Mexico_City") + put("MY", "Asia/Kuala_Lumpur") + put("MZ", "Africa/Maputo") + put("NA", "Africa/Windhoek") + put("NC", "Pacific/Noumea") + put("NE", "Africa/Niamey") + put("NF", "Pacific/Norfolk") + put("NG", "Africa/Lagos") + put("NI", "America/Managua") + put("NL", "Europe/Amsterdam") + put("NO", "Europe/Oslo") + put("NP", "Asia/Kathmandu") + put("NR", "Pacific/Nauru") + put("NU", "Pacific/Niue") + put("NZ", "Pacific/Auckland") + put("OM", "Asia/Muscat") + put("PA", "America/Panama") + put("PDT", "America/Los_Angeles") + put("PE", "America/Lima") + put("PF", "Pacific/Tahiti") + put("PG", "Pacific/Port_Moresby") + put("PH", "Asia/Manila") + put("PK", "Asia/Karachi") + put("PL", "Europe/Warsaw") + put("PM", "America/Miquelon") + put("PN", "Pacific/Pitcairn") + put("PR", "America/Puerto_Rico") + put("PS", "Asia/Gaza") + put("PST", "America/Los_Angeles") + put("PT", "Europe/Lisbon") + put("PW", "Pacific/Palau") + put("PY", "America/Asuncion") + put("QA", "Asia/Qatar") + put("RE", "Indian/Reunion") + put("RO", "Europe/Bucharest") + put("RS", "Europe/Belgrade") + put("RU", "Europe/Moscow") + put("RW", "Africa/Kigali") + put("SA", "Asia/Riyadh") + put("SB", "Pacific/Guadalcanal") + put("SC", "Indian/Mahe") + put("SD", "Africa/Khartoum") + put("SE", "Europe/Stockholm") + put("SG", "Asia/Singapore") + put("SH", "Atlantic/St_Helena") + put("SI", "Europe/Ljubljana") + put("SJ", "Atlantic/Jan_Mayen") + put("SK", "Europe/Bratislava") + put("SL", "Africa/Freetown") + put("SM", "Europe/San_Marino") + put("SN", "Africa/Dakar") + put("SO", "Africa/Mogadishu") + put("SR", "America/Paramaribo") + put("SS", "Africa/Juba") + put("ST", "Africa/Sao_Tome") + put("SV", "America/El_Salvador") + put("SX", "America/Lower_Princes") + put("SY", "Asia/Damascus") + put("SZ", "Africa/Mbabane") + put("TC", "America/Grand_Turk") + put("TD", "Africa/Ndjamena") + put("TF", "Indian/Kerguelen") + put("TG", "Africa/Lome") + put("TH", "Asia/Bangkok") + put("TJ", "Asia/Dushanbe") + put("TK", "Pacific/Fakaofo") + put("TL", "Asia/Dili") + put("TM", "Asia/Ashgabat") + put("TN", "Africa/Tunis") + put("TO", "Pacific/Tongatapu") + put("TR", "Europe/Istanbul") + put("TT", "America/Port_of_Spain") + put("TV", "Pacific/Funafuti") + put("TW", "Asia/Taipei") + put("TZ", "Africa/Dar_es_Salaam") + put("UA", "Europe/Kiev") + put("UG", "Africa/Kampala") + put("UK", "Europe/London") + put("UM", "Pacific/Wake") + put("US", "America/New_York") + put("UTC", "UTC") + put("UY", "America/Montevideo") + put("UZ", "Asia/Tashkent") + put("VA", "Europe/Vatican") + put("VC", "America/St_Vincent") + put("VE", "America/Caracas") + put("VG", "America/Tortola") + put("VI", "America/St_Thomas") + put("VN", "Asia/Ho_Chi_Minh") + put("VU", "Pacific/Efate") + put("WF", "Pacific/Wallis") + put("WS", "Pacific/Apia") + put("YE", "Asia/Aden") + put("YT", "Indian/Mayotte") + put("ZA", "Africa/Johannesburg") + put("ZM", "Africa/Lusaka") + put("ZULU", "Zulu") + put("ZW", "Africa/Harare") + ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } + .forEach { tz -> put(tz, tz) } + } + + // The Time command + private const val TIME_CMD = "time" + + // The zones arguments + private const val ZONES_ARGS = "zones" + + // The default zone + private const val DEFAULT_ZONE = "PST" + + // Date/Time Format + private var dtf = + DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") + + /** + * Returns the current Internet (beat) Time. + */ + private fun internetTime(): String { + val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) + val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 + + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() + return "%c%03d".format('@', beats) + } + + /** + * Returns the time for the given timezone/city. + */ + @JvmStatic + fun time(query: String = DEFAULT_ZONE): String { + val tz = COUNTRIES_MAP[(if (query.isNotBlank()) query.trim().uppercase() else DEFAULT_ZONE)] + return if (tz != null) { + if (BEATS_KEYWORD == tz) { + "The current Internet Time is ${internetTime().bold()} $BEATS_KEYWORD" + } else { + (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) + + tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ').bold()) + } + } else { + "Unsupported country/zone. Please try again." + } + } + } + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.equals(ZONES_ARGS, true)) { + event.sendMessage("The supported countries/zones are: ") + event.sendList(COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, 14, isIndent = true) + } else { + event.respond(time(args)) + } + } + + override val isPrivateMsgEnabled = true + + init { + with(help) { + add("To display a country's current date/time:") + add(helpFormat("%c $TIME_CMD [<country code or zone>]")) + add("For a listing of the supported countries/zones:") + add(helpFormat("%c $TIME_CMD $ZONES_ARGS")) + } + commands.add(TIME_CMD) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt new file mode 100644 index 0000000..0607936 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -0,0 +1,37 @@ +/* + * ErrorMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg + +/** + * The `ErrorMessage` class. + */ +class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isError = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/Message.kt b/bin/main/net/thauvin/erik/mobibot/msg/Message.kt new file mode 100644 index 0000000..23a33b9 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/Message.kt @@ -0,0 +1,65 @@ +/* + * Message.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg + +import net.thauvin.erik.semver.Constants + +/** + * The `Message` class. + */ +open class Message @JvmOverloads constructor( + var msg: String, + var color: String = DEFAULT_COLOR, + var isNotice: Boolean = false, + isError: Boolean = false, + var isPrivate: Boolean = false +) { + companion object { + var DEFAULT_COLOR = Constants.EMPTY + } + + init { + if (isError) { + isNotice = true + } + } + + /** Error flag. */ + var isError = isError + set(value) { + if (value) isNotice = true + field = value + } + + override fun toString(): String { + return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, msg='$msg')" + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt new file mode 100644 index 0000000..037d504 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -0,0 +1,38 @@ +/* + * NoticeMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg + +/** + * The `NoticeMessage` class. + */ +class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isNotice = true) + diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt new file mode 100644 index 0000000..b424fdf --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -0,0 +1,37 @@ +/* + * PrivateMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg + +/** + * The `PrivateMessage` class. + */ +class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isPrivate = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt new file mode 100644 index 0000000..9c5e088 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -0,0 +1,36 @@ +/* + * PublicMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.msg + +/** + * The `PublicMessage` class. + */ +class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color) diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt new file mode 100644 index 0000000..91f2dd9 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -0,0 +1,116 @@ +/* + * SocialManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.social + +import net.thauvin.erik.mobibot.Addons +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.util.* + +/** + * Social Manager. + */ +class SocialManager { + private val entries: MutableSet<Int> = HashSet() + private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) + private val modules = ArrayList<SocialModule>() + private val timer = Timer(true) + + /** + * Adds social modules. + */ + fun add(addons: Addons, vararg modules: SocialModule) { + modules.forEach { + if (addons.add(it)) { + this.modules.add(it) + } + } + } + + /** + * Returns the number of entries. + */ + fun entriesCount(): Int = entries.size + + /** + * Sends a social notification (dm, etc.) + */ + fun notification(msg: String) { + modules.forEach { + it.notification(msg) + } + } + + /** + * Posts to social media. + */ + fun postEntry(index: Int) { + if (entries.contains(index)) { + modules.forEach { + it.postEntry(index) + } + entries.remove(index) + } + } + + /** + * Queues an entry for posting to social media. + */ + fun queueEntry(index: Int) { + if (modules.isNotEmpty()) { + entries.add(index) + if (logger.isDebugEnabled) { + logger.debug("Scheduling {} for posting on social media.", index.toLinkLabel()) + } + timer.schedule(SocialTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L) + } + } + + /** + * Removes entries from queue. + */ + fun removeEntry(index: Int) { + entries.remove(index) + } + + /** + * Posts all entries on shutdown. + */ + fun shutdown() { + timer.cancel() + entries.forEach { + postEntry(it) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt new file mode 100644 index 0000000..b594670 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -0,0 +1,96 @@ +/* + * SocialModule.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.social + +import net.thauvin.erik.mobibot.commands.links.LinksManager +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.modules.AbstractModule +import net.thauvin.erik.mobibot.modules.ModuleException +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +abstract class SocialModule : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) + + abstract val handle: String? + abstract val isAutoPost: Boolean + + abstract fun formatEntry(entry: EntryLink): String + + /** + * Sends a DM. + */ + fun notification(msg: String) { + if (isEnabled && !handle.isNullOrBlank()) { + try { + post(message = msg, isDm = true) + if (logger.isDebugEnabled) logger.debug("Notified $handle on $name: $msg") + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn("Failed to notify $handle on $name: $msg", e) + } + } + } + + abstract fun post(message: String, isDm: Boolean): String + + /** + * Post entry to social media. + */ + fun postEntry(index: Int) { + if (isAutoPost && LinksManager.entries.links.size >= index) { + try { + if (logger.isDebugEnabled) { + logger.debug("Posting {} to $name.", index.toLinkLabel()) + } + post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn( + "Failed to post entry ${index.toLinkLabel()} on $name.", + e + ) + } + } + } + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + try { + event.respond(post("$args (by ${event.user.nick} on $channel)", false)) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt new file mode 100644 index 0000000..3fd315e --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -0,0 +1,40 @@ +/* + * SocialTimer.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.social + +import java.util.* + +class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() { + override fun run() { + socialManager.postEntry(index) + } +} diff --git a/bin/test/current.xml b/bin/test/current.xml new file mode 100644 index 0000000..535a400 --- /dev/null +++ b/bin/test/current.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ current.xml + ~ + ~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + ~ + ~ Redistribution and use in source and binary forms, with or without + ~ modification, are permitted provided that the following conditions are met: + ~ + ~ Redistributions of source code must retain the above copyright notice, this + ~ list of conditions and the following disclaimer. + ~ + ~ Redistributions in binary form must reproduce the above copyright notice, + ~ this list of conditions and the following disclaimer in the documentation + ~ and/or other materials provided with the distribution. + ~ + ~ Neither the name of this project nor the names of its contributors may be + ~ used to endorse or promote products derived from this software without + ~ specific prior written permission. + ~ + ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + ~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + ~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + ~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + ~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + ~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + ~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + --> + +<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> + <channel> + <title>#mobibot IRC Links + https://www.mobitopia.org/mobibot/logs + Links from irc.example.com on #mobibot + en + Sun, 31 Oct 2021 21:45:11 GMT + 2021-10-31T21:45:11Z + en + + Example 2 + https://www.example.com/2 + Posted by <b>Skynx</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> + tag2-1 + tag2-2 + Sun, 31 Oct 2021 21:45:11 GMT + https://www.foo.com + mobibot@irc.libera.chat (Skynx) + 2021-10-31T00:01:00Z + + + Example 1 + https://www.example.com/1 + Posted by <b>ErikT</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> + <br/><br/>ErikT: This is comment 1. <br/>Skynx: This is comment 2. + + tag1-1 + tag1-2 + Sun, 31 Oct 2021 21:43:15 GMT + https://www.example.com/ + mobibot@irc.libera.chat (ErikT) + 2021-10-31T00:00:00Z + + + diff --git a/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt b/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt new file mode 100644 index 0000000..afb8ce6 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt @@ -0,0 +1,86 @@ +/* + * AddonsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import assertk.assertThat +import assertk.assertions.containsExactly +import assertk.assertions.isEqualTo +import assertk.assertions.size +import net.thauvin.erik.mobibot.commands.ChannelFeed +import net.thauvin.erik.mobibot.commands.Cycle +import net.thauvin.erik.mobibot.commands.Die +import net.thauvin.erik.mobibot.commands.Ignore +import net.thauvin.erik.mobibot.commands.links.Comment +import net.thauvin.erik.mobibot.commands.links.View +import net.thauvin.erik.mobibot.modules.* +import kotlin.test.Test +import java.util.* + +class AddonsTest { + private val p = Properties().apply { + put("disabled-modules", "war,dice Lookup") + put("disabled-commands", "View | comment") + } + private val addons = Addons(p) + + @Test + fun addTest() { + // Modules + addons.add(Joke()) + addons.add(RockPaperScissors()) + addons.add(War()) + addons.add(Dice()) + addons.add(Lookup()) + assertThat(addons::modules).size().isEqualTo(2) + assertThat(addons.names.modules, "names.modules").containsExactly("Joke", "RockPaperScissors") + + // Commands + addons.add(View()) + addons.add(Comment()) + addons.add(Cycle()) + addons.add(Die()) // invisible + addons.add(ChannelFeed("channel")) // no properties, disabled + p[Ignore.IGNORE_PROP] = "nick" + addons.add(Ignore()) + assertThat(addons::commands).size().isEqualTo(3) + + assertThat(addons.names.ops, "names.ops").containsExactly("cycle") + + assertThat(addons.names.commands, "names.command").containsExactly( + "joke", + "rock", + "paper", + "scissors", + "ignore" + ) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt new file mode 100644 index 0000000..a3994ec --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -0,0 +1,60 @@ +/* + * ExceptionSanitizer.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.Utils.obfuscate +import net.thauvin.erik.mobibot.Utils.replaceEach +import net.thauvin.erik.mobibot.modules.ModuleException + +object ExceptionSanitizer { + /** + * Returns a sanitized exception to avoid displaying api keys, etc. in CI logs. + */ + fun ModuleException.sanitize(vararg sanitize: String): ModuleException { + val search = sanitize.filter { it.isNotBlank() }.toTypedArray() + if (search.isNotEmpty()) { + val obfuscate = search.map { it.obfuscate() }.toTypedArray() + with(this) { + if (!cause?.message.isNullOrBlank()) { + return ModuleException( + debugMessage, + cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), + this + ) + } else if (!message.isNullOrBlank()) { + return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) + } + } + } + return this + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt b/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt new file mode 100644 index 0000000..6e5fa8f --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -0,0 +1,78 @@ +/* + * FeedReaderTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import com.rometools.rome.io.FeedException +import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed +import net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test +import java.io.IOException +import java.net.MalformedURLException +import java.net.UnknownHostException + +/** + * The `FeedReader Test` class. + */ +class FeedReaderTest { + @Test + fun readFeedTest() { + var messages = readFeed("https://feeds.thauvin.net/ethauvin") + assertThat(messages, "messages").all { + size().isEqualTo(10) + index(1).prop(Message::msg).contains("erik.thauvin.net") + } + + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") + assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing") + + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) + assertThat(messages, "messages").size().isEqualTo(84) + messages.forEachIndexed { i, m -> + if (i % 2 == 0) { + assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum") + } else { + assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/") + } + } + + assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) + + assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java) + + assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) + + assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt b/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt new file mode 100644 index 0000000..1384a72 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt @@ -0,0 +1,85 @@ +/* + * LocalProperties.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import org.testng.annotations.BeforeSuite +import java.io.IOException +import java.net.InetAddress +import java.net.UnknownHostException +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* + +/** + * Access to `local.properties`. + */ +open class LocalProperties { + @BeforeSuite(alwaysRun = true) + fun loadProperties() { + val localPath = Paths.get("local.properties") + if (Files.exists(localPath)) { + try { + Files.newInputStream(localPath).use { stream -> localProps.load(stream) } + } catch (ignore: IOException) { + // Do nothing + } + } + } + + companion object { + private val localProps = Properties() + + fun getHostName(): String { + val ciName = System.getenv("CI_NAME") + return ciName ?: try { + InetAddress.getLocalHost().hostName + } catch (ignore: UnknownHostException) { + "Unknown Host" + } + } + + fun getProperty(key: String): String { + return if (localProps.containsKey(key)) { + localProps.getProperty(key) + } else { + val env = System.getenv(keyToEnv(key)) + env?.let { + localProps.setProperty(key, env) + } + env + } + } + + private fun keyToEnv(key: String): String { + return key.replace('-', '_').uppercase() + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt b/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt new file mode 100644 index 0000000..b527fc5 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt @@ -0,0 +1,81 @@ +/* + * PinboardTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot + +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.entries.EntryLink +import org.testng.Assert.assertFalse +import org.testng.Assert.assertTrue +import kotlin.test.Test +import java.net.URL + +class PinboardTest : LocalProperties() { + private val pinboard = Pinboard() + + @Test + fun testPinboard() { + val apiToken = getProperty("pinboard-api-token") + val url = "https://www.example.com/${(1000..5000).random()}" + val ircServer = "irc.test.com" + val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) + + pinboard.setApiToken(apiToken) + + pinboard.addPin(ircServer, entry) + assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin") + + entry.link = "https://www.example.com/${(5001..9999).random()}" + pinboard.updatePin(ircServer, url, entry) + assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin") + + entry.title = "Foo Title" + pinboard.updatePin(ircServer, entry.link, entry) + assertTrue(validatePin(apiToken, url = entry.link, entry.title), "updatePin(${entry.title}") + + pinboard.deletePin(entry) + assertFalse(validatePin(apiToken, url = entry.link), "deletePin") + } + + private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { + val response = + URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + + matches.forEach { + if (!response.contains(it)) { + return false + } + } + + return response.contains(url) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt new file mode 100644 index 0000000..7b7ba8c --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt @@ -0,0 +1,278 @@ +/* + * UtilsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.length +import net.thauvin.erik.mobibot.Utils.appendIfMissing +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.capitalizeWords +import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.cyan +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.lastOrEmpty +import net.thauvin.erik.mobibot.Utils.obfuscate +import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.red +import net.thauvin.erik.mobibot.Utils.replaceEach +import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.toIntOrDefault +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.Utils.toUtcDateTime +import net.thauvin.erik.mobibot.Utils.today +import net.thauvin.erik.mobibot.Utils.underline +import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import org.pircbotx.Colors +import org.testng.annotations.BeforeClass +import kotlin.test.Test +import java.io.File +import java.io.IOException +import java.net.URL +import java.time.LocalDateTime +import java.util.* + +/** + * The `Utils Test` class. + */ +class UtilsTest { + private val ascii = + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + private val cal = Calendar.getInstance() + private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) + private val test = "This is a test." + + @BeforeClass + fun setUp() { + cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 + } + + @Test + fun testAppendIfMissing() { + val dir = "dir" + val sep = '/' + val url = "https://erik.thauvin.net" + assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") + .isEqualTo(dir + File.separatorChar) + assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") + assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") + } + + @Test + fun testBold() { + assertThat(1.bold(), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(2L.bold(), "bold(2L)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) + assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) + } + + + @Test + fun testCapitalise() { + assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test") + assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test") + assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test) + assertThat("".capitalise(), "capitalize()").isEqualTo("") + } + + @Test + fun textCapitaliseWords() { + assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") + assertThat("Already Capitalized".capitalizeWords(), "already capitalized") + .isEqualTo("Already Capitalized") + assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") + } + + @Test + fun testColorize() { + assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( + Colors.REVERSE + ascii + Colors.REVERSE + ) + assertThat(ascii.colorize(Colors.RED), "red.colorize()") + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") + .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") + assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") + assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) + assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") + .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) + } + + @Test + fun testCyan() { + assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) + } + + @Test + fun testEncodeUrl() { + assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello%20G%C3%BCnter") + } + + @Test + fun testGetIntProperty() { + val p = Properties() + p["one"] = "1" + p["two"] = "two" + assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1) + assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2) + assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3) + } + + @Test + fun testGreen() { + assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) + } + + @Test + fun testHelpCmdSyntax() { + val bot = "mobibot" + assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") + .isEqualTo("$bot: $test $bot $test") + assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + } + + @Test + fun testHelpFormat() { + assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") + .isEqualTo(test.prependIndent()) + assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") + .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) + + } + + @Test + fun testIsoLocalDate() { + assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17") + assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17") + } + + @Test + fun testLastOrEmpty() { + val two = listOf("1", "2") + assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2") + val one = listOf("1") + assertThat(one.lastOrEmpty(), "lastOrEmpty(1)").isEqualTo("") + } + + @Test + fun testObfuscate() { + assertThat(ascii.obfuscate(), "obfuscate()").all { + length().isEqualTo(ascii.length) + isEqualTo(("x".repeat(ascii.length))) + } + assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ") + } + + @Test + fun testPlural() { + val week = "week" + val weeks = "weeks" + + for (i in -1..3) { + assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week) + } + } + + @Test + fun testReplaceEach() { + val search = arrayOf("one", "two", "three") + val replace = arrayOf("1", "2", "3") + assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") + .isEqualTo(replace.joinToString(",")) + + assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) + + assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") + .isEqualTo(test.replace("t", "").replace("e", "E")) + + assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") + .isEqualTo(test) + } + + @Test + fun testRed() { + assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED)) + } + + @Test + fun testReverseColor() { + assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) + } + + @Test + fun testToday() { + assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate()) + } + + @Test + fun testToIntOrDefault() { + assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10) + assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2) + } + + @Test + fun testUnderline() { + assertThat(ascii.underline()).isEqualTo(ascii.colorize(Colors.UNDERLINE)) + } + + @Test + fun testUnescapeXml() { + assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( + "" + ) + } + + @Test + @Throws(IOException::class) + fun testUrlReader() { + val reader = URL("https://postman-echo.com/status/200").reader() + assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}") + assertThat(reader.responseCode).isEqualTo(200) + } + + @Test + fun testUtcDateTime() { + assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30") + assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt new file mode 100644 index 0000000..99ba951 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -0,0 +1,58 @@ +/* + * InfoTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import assertk.assertThat +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime +import kotlin.test.Test + +class InfoTest { + @Test + fun testToUptime() { + assertThat( + 547800300076L.toUptime(), + "upTime(full)" + ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") + assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") + assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") + assertThat( + 1320300000L.toUptime(), + "upTime(weeks days hours minutes)" + ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") + assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") + assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") + assertThat(59000L.toUptime(), "upTime(59 seconds)").isEqualTo("59 seconds") + assertThat(0L.toUptime(), "upTime(0 second)").isEqualTo("0 second") + + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt new file mode 100644 index 0000000..b6fbbff --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -0,0 +1,60 @@ +/* + * RecapTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.matches +import assertk.assertions.prop +import assertk.assertions.size +import kotlin.test.Test + +class RecapTest { + @Test + fun storeRecapTest() { + for (i in 1..20) { + Recap.storeRecap("sender$i", "test $i", false) + } + assertThat(Recap.recaps, "Recap.recaps").all { + size().isEqualTo(Recap.MAX_RECAPS) + prop(MutableList::first) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) + prop(MutableList::last) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) + } + + Recap.storeRecap("sender", "test action", true) + assertThat(Recap.recaps.last()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt new file mode 100644 index 0000000..fa2bb70 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -0,0 +1,77 @@ +/* + * LinksManagerTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isTrue +import assertk.assertions.size +import net.thauvin.erik.mobibot.Constants +import kotlin.test.Test + +class LinksManagerTest { + private val linksManager = LinksManager() + + @Test + fun fetchTitle() { + assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") + assertThat( + linksManager.fetchTitle("https://www.google.com/foo"), + "fetchTitle(Foo)" + ).isEqualTo(Constants.NO_TITLE) + } + + @Test + fun testMatches() { + assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue() + assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue() + } + + @Test + fun matchTagKeywordsTest() { + linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3") + val tags = mutableListOf() + + linksManager.matchTagKeywords("Test title with key2", tags) + assertThat(tags, "tags").contains("key2") + tags.clear() + + linksManager.matchTagKeywords("Test key3 title with key1", tags) + assertThat(tags, "tags(key1, key3)").all { + contains("key1") + contains("key3") + size().isEqualTo(2) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt new file mode 100644 index 0000000..e315891 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -0,0 +1,111 @@ +/* + * ViewTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.prop +import net.thauvin.erik.mobibot.entries.EntryLink +import kotlin.test.Test + +class ViewTest { + @Test + fun testParseArgs() { + val view = View() + + for (i in 1..10) { + LinksManager.entries.links.add( + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) + ) + } + + assertThat(view.parseArgs("1"), "parseArgs(1)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + + assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all { + prop(Pair::first).isEqualTo(1) + prop(Pair::second).isEqualTo("foo") + } + + assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all { + prop(Pair::first).isEqualTo(2) + prop(Pair::second).isEqualTo("foo") + } + + assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all { + prop(Pair::first).isEqualTo(3) + prop(Pair::second).isEqualTo("foo bar") + } + + assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("foo bar") + } + + assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("${Int.MAX_VALUE}1") + } + + assertThat(view.parseArgs("1a"), "parseArgs(1a)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("1a") + } + + assertThat(view.parseArgs("20"), "parseArgs(20)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + + assertThat(view.parseArgs(""), "parseArgs()").all { + prop(Pair::first).isEqualTo(LinksManager.entries.links.size - View.MAX_ENTRIES) + prop(Pair::second).isEqualTo("") + } + + LinksManager.entries.links.clear() + + assertThat(view.parseArgs("4"), "parseArgs(4)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt new file mode 100644 index 0000000..37b884b --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -0,0 +1,85 @@ +/* + * SeenTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.seen + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import org.testng.annotations.AfterClass +import org.testng.annotations.BeforeClass +import kotlin.test.Test +import kotlin.io.path.deleteIfExists +import kotlin.io.path.fileSize + +class SeenTest { + private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") + private val seen = Seen(tmpFile.toAbsolutePath().toString()) + private val nick = "ErikT" + + @BeforeClass + fun saveTest() { + seen.add("ErikT") + assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) + } + + @AfterClass(alwaysRun = true) + fun afterClass() { + tmpFile.deleteIfExists() + } + + @Test(priority = 1, groups = ["commands"]) + fun loadTest() { + seen.clear() + assertThat(seen::seenNicks).isEmpty() + seen.load() + assertThat(seen::seenNicks).key(nick).isNotNull() + } + + @Test + fun addTest() { + val last = seen.seenNicks[nick]?.lastSeen + seen.add(nick.lowercase()) + assertThat(seen).all { + prop(Seen::seenNicks).size().isEqualTo(1) + prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) + prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(nick.lowercase()) + } + } + + @Test(priority = 10, groups = ["commands"]) + fun clearTest() { + seen.clear() + seen.save() + seen.load() + assertThat(seen::seenNicks).size().isEqualTo(0) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt new file mode 100644 index 0000000..cc09237 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -0,0 +1,72 @@ +/* + * TellMessageTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.commands.tell + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import assertk.assertions.prop +import kotlin.test.Test +import java.time.Duration +import java.time.LocalDateTime +import java.time.temporal.Temporal + +/** + * The `TellMessageTest` class. + */ +class TellMessageTest { + private fun isValidDate(date: Temporal): Boolean { + return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 + } + + @Test + fun testTellMessage() { + val message = "Test message." + val recipient = "recipient" + val sender = "sender" + val tellMessage = TellMessage(sender, recipient, message) + assertThat(tellMessage).all { + prop(TellMessage::sender).isEqualTo(sender) + prop(TellMessage::recipient).isEqualTo(recipient) + prop(TellMessage::message).isEqualTo(message) + } + assertThat(isValidDate(tellMessage.queued), "isValidDate()").isTrue() + assertThat(tellMessage.isMatch(sender), "isMatch(sender)").isTrue() + assertThat(tellMessage.isMatch(recipient), "isMatch(recipient)").isTrue() + assertThat(tellMessage.isMatch("foo"), "isMatch(foo)").isFalse() + tellMessage.isReceived = false + assertThat(tellMessage.receptionDate, "receptionDate").isEqualTo(LocalDateTime.MIN) + tellMessage.isReceived = true + assertThat(isValidDate(tellMessage.receptionDate), "isValidDate(creationDate)").isTrue() + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt new file mode 100644 index 0000000..5acba78 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -0,0 +1,87 @@ +/* + * TellMessagesMgrTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.tell + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import org.testng.annotations.AfterClass +import org.testng.annotations.BeforeClass +import kotlin.test.Test +import java.time.LocalDateTime +import kotlin.io.path.createTempFile +import kotlin.io.path.deleteIfExists +import kotlin.io.path.fileSize + +class TellMessagesMgrTest { + private val testFile = createTempFile(suffix = ".ser") + private val maxDays = 10L + private val testMessages = mutableListOf().apply { + for (i in 0..5) { + this.add(i, TellMessage("sender$i", "recipient$i", "message $i")) + } + } + + @BeforeClass + fun saveTest() { + TellManager.save(testFile.toAbsolutePath().toString(), testMessages) + assertThat(testFile.fileSize()).isGreaterThan(0) + } + + @AfterClass + fun afterClass() { + testFile.deleteIfExists() + } + + @Test + fun cleanTest() { + testMessages.add(TellMessage("sender", "recipient", "message").apply { + queued = LocalDateTime.now().minusDays(maxDays) + }) + val size = testMessages.size + assertThat(TellManager.clean(testMessages, maxDays + 2), "clean(maxDays=${maxDays + 2})").isFalse() + assertThat(TellManager.clean(testMessages, maxDays), "clean(maxDays=$maxDays)").isTrue() + assertThat(testMessages, "testMessages").size().isEqualTo(size - 1) + } + + @Test + fun loadTest() { + val messages = TellManager.load(testFile.toAbsolutePath().toString()) + for (i in messages.indices) { + assertThat(messages).index(i).all { + prop(TellMessage::sender).isEqualTo(testMessages[i].sender) + prop(TellMessage::recipient).isEqualTo(testMessages[i].recipient) + prop(TellMessage::message).isEqualTo(testMessages[i].message) + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt new file mode 100644 index 0000000..0ad26b5 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -0,0 +1,91 @@ +/* + * EntriesUtilsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.entries + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import kotlin.test.Test + +class EntriesUtilsTest { + private val comment = EntryComment("comment", "nick") + private val links = buildList { + for (i in 0..5) { + add( + EntryLink( + "https://www.mobitopia.org/$i", + "Mobitopia$i", + "Skynx$i", + "JimH$i", + "#mobitopia$i", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) + ) + } + } + + @Test + fun printCommentTest() { + assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") + } + + @Test + fun printLinkTest() { + for (i in links.indices) { + assertThat( + printLink(i - 1, links[i]), "link $i" + ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") + } + + assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0) + assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]") + } + + @Test + fun printTagsTest() { + for (i in links.indices) { + assertThat( + printTags(i - 1, links[i]), "tag $i" + ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") + } + } + + @Test + fun toLinkLabelTest() { + assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt new file mode 100644 index 0000000..f421099 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -0,0 +1,133 @@ +/* + * EntryLinkTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.entries + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import com.rometools.rome.feed.synd.SyndCategory +import com.rometools.rome.feed.synd.SyndCategoryImpl +import kotlin.test.Test +import java.security.SecureRandom +import java.util.* + +/** + * The `EntryUtilsTest` class. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @created 2019-04-19 + * @since 1.0 + */ +class EntryLinkTest { + private val entryLink = EntryLink( + "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) + + @Test + fun testAddDeleteComment() { + var i = 0 + while (i < 5) { + entryLink.addComment("c$i", "u$i") + i++ + } + assertThat(entryLink.comments, "comments").size().isEqualTo(i) + i = 0 + for (comment in entryLink.comments) { + assertThat(comment).all { + prop(EntryComment::comment).isEqualTo("c$i") + prop(EntryComment::nick).isEqualTo("u$i") + } + i++ + } + + val r = SecureRandom() + while (entryLink.comments.size > 0) { + entryLink.deleteComment(r.nextInt(entryLink.comments.size)) + } + assertThat(entryLink.comments, "hasComments()").isEmpty() + entryLink.addComment("nothing", "nobody") + entryLink.setComment(0, "something", "somebody") + val comment = entryLink.getComment(0) + assertThat(comment, "comment[first]").all { + prop(EntryComment::nick).isEqualTo("somebody") + prop(EntryComment::comment).isEqualTo("something") + } + assertThat(entryLink.deleteComment(comment), "deleteComment").isTrue() + assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse() + } + + @Test + fun testConstructor() { + val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" }) + val link = EntryLink("link", "title", "nick", "channel", Date(), tags) + assertThat(link, "link").all { + prop(EntryLink::tags).size().isEqualTo(tags.size) + prop(EntryLink::tags).index(0).prop(SyndCategory::getName).isEqualTo("tag1") + } + } + + @Test + fun testMatches() { + assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue() + assertThat(entryLink.matches("skynx"), "match(nick)").isTrue() + assertThat(entryLink.matches("www.mobitopia.org"), "matches(url)").isTrue() + assertThat(entryLink.matches("foo"), "matches(foo)").isFalse() + assertThat(entryLink.matches(""), "matches(empty)").isFalse() + assertThat(entryLink.matches(null), "matches(null)").isFalse() + } + + + @Test + fun testTags() { + val tags: List = entryLink.tags + for ((i, tag) in tags.withIndex()) { + assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}") + } + assertThat(entryLink::tags).size().isEqualTo(5) + entryLink.setTags("-tag5, tag4") + entryLink.setTags("+mobitopia") + entryLink.setTags("-mobitopia") + assertThat( + entryLink.formatTags(","), + "formatTags(',')" + ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") + entryLink.setTags("-tag4 tag5") + assertThat( + entryLink.formatTags(" ", ","), "formatTag(' ',',')" + ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") + val size = entryLink.tags.size + entryLink.setTags("") + assertThat(entryLink.tags, "setTags('')").size().isEqualTo(size) + entryLink.setTags(" ") + assertThat(entryLink.tags, "setTags(' ')").size().isEqualTo(size) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt new file mode 100644 index 0000000..1611009 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -0,0 +1,115 @@ +/* + * FeedMgrTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.entries + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.Utils.today +import org.testng.annotations.BeforeSuite +import kotlin.test.Test +import java.nio.file.Paths +import java.util.* +import kotlin.io.path.deleteIfExists +import kotlin.io.path.fileSize +import kotlin.io.path.name + +class FeedMgrTest { + private val entries = Entries() + private val channel = "mobibot" + + @BeforeSuite(alwaysRun = true) + fun beforeSuite() { + entries.logsDir = "src/test/resources/" + entries.ircServer = "irc.example.com" + entries.channel = channel + entries.backlogs = "https://www.mobitopia.org/mobibot/logs" + } + + @Test + fun testFeedMgr() { + // Load the feed + assertThat(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31") + + assertThat(entries.links, "entries.links").size().isEqualTo(2) + entries.links.forEachIndexed { i, entryLink -> + assertThat(entryLink, "entryLink[${i + 1}]").all { + prop(EntryLink::title).isEqualTo("Example ${i + 1}") + prop(EntryLink::link).isEqualTo("https://www.example.com/${i + 1}") + prop(EntryLink::channel).isEqualTo(channel) + } + entryLink.tags.forEachIndexed { y, tag -> + assertThat(tag.name, "tag${i + 1}-${y + 1}").isEqualTo("tag${i + 1}-${y + 1}") + } + } + + with(entries.links.first()) { + assertThat(nick, "nick[first]").isEqualTo("ErikT") + assertThat(date, "date[first]").isEqualTo(Date(1635638400000L)) + assertThat(comments.first(), "comments[first]").all { + prop(EntryComment::comment).endsWith("comment 1.") + prop(EntryComment::nick).isEqualTo("ErikT") + } + assertThat(comments.last(), "comments[last]").all { + prop(EntryComment::comment).endsWith("comment 2.") + prop(EntryComment::nick).isEqualTo("Skynx") + } + } + + assertThat(entries.links, "links").index(1).all { + prop(EntryLink::nick).isEqualTo("Skynx") + prop(EntryLink::date).isEqualTo(Date(1635638460000L)) + } + + val currentFile = Paths.get("${entries.logsDir}test.xml") + val backlogFile = Paths.get("${entries.logsDir}${today()}.xml") + + // Save the feed + FeedsManager.saveFeed(entries, currentFile.name) + + assertThat(currentFile, "currentFile").exists() + assertThat(backlogFile, "backlogFile").exists() + + assertThat(currentFile.fileSize(), "currentFile == backlogFile").isEqualTo(backlogFile.fileSize()) + + // Load the test feed + entries.links.clear() + FeedsManager.loadFeed(entries, currentFile.name) + + entries.links.forEachIndexed { i, entryLink -> + assertThat(entryLink.title, "entryLink.title[${i + 1}]").isEqualTo("Example ${i + 1}") + } + + assertThat(currentFile.deleteIfExists(), "currentFile.deleteIfExists()").isTrue() + assertThat(backlogFile.deleteIfExists(), "backlogFile.deleteIfExists()").isTrue() + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt new file mode 100644 index 0000000..3bd4c0f --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -0,0 +1,53 @@ +/* + * CalcTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isInstanceOf +import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate +import kotlin.test.Test + +/** + * The `CalcTest` class. + */ +class CalcTest { + @Test + fun testCalculate() { + assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") + assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") + assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}") + assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt new file mode 100644 index 0000000..085f228 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -0,0 +1,62 @@ +/* + * ChatGptTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.hasNoCause +import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.LocalProperties +import kotlin.test.Test + +class ChatGptTest : LocalProperties() { + @Test + fun testApiKey() { + assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } + .isInstanceOf(ModuleException::class.java) + .hasNoCause() + } + + @Test(groups = ["modules", "no-ci"]) + fun testChat() { + val apiKey = getProperty(ChatGpt.API_KEY_PROP) + assertThat( + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ).contains("XMLHttpRequest") + assertThat( + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ).contains("URLEncoder") + + assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } + .isInstanceOf(ModuleException::class.java) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt new file mode 100644 index 0000000..d0ecbc9 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -0,0 +1,78 @@ +/* + * CryptoPricesTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThan +import assertk.assertions.prop +import net.thauvin.erik.crypto.CryptoPrice +import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice +import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName +import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies +import org.testng.annotations.BeforeClass +import kotlin.test.Test + +/** + * The `CryptoPricesTest` class. + */ +class CryptoPricesTest { + @BeforeClass + @Throws(ModuleException::class) + fun before() { + loadCurrencies() + } + + @Test + @Throws(ModuleException::class) + fun testMarketPrice() { + var price = currentPrice(listOf("BTC")) + assertThat(price, "currentPrice(BTC)").all { + prop(CryptoPrice::base).isEqualTo("BTC") + prop(CryptoPrice::currency).isEqualTo("USD") + prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) + } + + price = currentPrice(listOf("ETH", "EUR")) + assertThat(price, "currentPrice(ETH, EUR)").all { + prop(CryptoPrice::base).isEqualTo("ETH") + prop(CryptoPrice::currency).isEqualTo("EUR") + prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) + } + } + + @Test + fun testGetCurrencyName() { + assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") + assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt new file mode 100644 index 0000000..8d8c997 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -0,0 +1,85 @@ +/* + * CurrencyConverterTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isInstanceOf +import assertk.assertions.matches +import assertk.assertions.prop +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency +import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.testng.annotations.BeforeClass +import kotlin.test.Test + + +/** + * The `CurrencyConvertTest` class. + */ +class CurrencyConverterTest : LocalProperties() { + + @BeforeClass + @Throws(ModuleException::class) + fun before() { + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) + loadSymbols(apiKey) + } + + @Test + fun testConvertCurrency() { + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) + assertThat( + convertCurrency(apiKey, "100 USD to EUR").msg, + "convertCurrency(100 USD to EUR)" + ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) + assertThat( + convertCurrency(apiKey, "1 USD to GBP").msg, + "convertCurrency(1 USD to BGP)" + ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) + assertThat( + convertCurrency(apiKey, "100,000.00 CAD to USD").msg, + "convertCurrency(100,000.00 GBP to USD)" + ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) + assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all { + prop(Message::msg).contains("You're kidding, right?") + isInstanceOf(PublicMessage::class.java) + } + assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all { + prop(Message::msg).contains("Invalid query.") + isInstanceOf(ErrorMessage::class.java) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt new file mode 100644 index 0000000..8bafede --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -0,0 +1,53 @@ +/* + * DiceTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.matches +import kotlin.test.Test + +class DiceTest { + @Test + fun testRoll() { + assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") + assertThat(Dice.roll(2, 1), "roll(2d1)") + .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") + assertThat(Dice.roll(5, 1), "roll(5d1)") + .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") + assertThat(Dice.roll(2, 6), "roll(2d6)") + .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) + assertThat(Dice.roll(3, 7), "roll(3d7)") + .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt new file mode 100644 index 0000000..85c990c --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -0,0 +1,95 @@ +/* + * GoogleSearchTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test + +/** + * The `GoogleSearchTest` class. + */ +class GoogleSearchTest : LocalProperties() { + @Test + fun testAPIKeys() { + assertThat( + searchGoogle("", "apikey", "cssKey").first(), + "searchGoogle(empty)" + ).isInstanceOf(ErrorMessage::class.java) + + assertFailure { searchGoogle("test", "", "apiKey") } + .isInstanceOf(ModuleException::class.java).hasNoCause() + + assertFailure { searchGoogle("test", "apiKey", "") } + .isInstanceOf(ModuleException::class.java).hasNoCause() + + assertFailure { searchGoogle("test", "apiKey", "cssKey") } + .isInstanceOf(ModuleException::class.java) + .hasMessage("API key not valid. Please pass a valid API key.") + } + + @Test\n@DisableOnCi + @Throws(ModuleException::class) + fun testSearchGoogle() { + val apiKey = getProperty(GoogleSearch.API_KEY_PROP) + val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP) + + try { + var query = "mobibot" + var messages = searchGoogle(query, apiKey, cseKey) + assertThat(messages, "searchGoogle($query)").all { + isNotEmpty() + index(0).prop(Message::msg).contains(query, true) + } + + query = "adadflkjl" + messages = searchGoogle(query, apiKey, cseKey) + assertThat(messages, "searchGoogle($query)").index(0).all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo("No results found.") + } + } catch (e: ModuleException) { + // Avoid displaying api keys in CI logs + if ("true" == System.getenv("CI")) { + throw e.sanitize(apiKey, cseKey) + } else { + throw e + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt new file mode 100644 index 0000000..0af8e75 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -0,0 +1,57 @@ +/* + * JokeTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import kotlin.test.Test + +/** + * The `JokeTest` class. + */ +class JokeTest { + @Test + @Throws(ModuleException::class) + fun testRandomJoke() { + val joke = randomJoke() + assertThat(joke, "randomJoke()").all { + size().isGreaterThan(0) + each { + it.isInstanceOf(PublicMessage::class.java) + it.prop(Message::msg).doesNotContain("\n") + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt new file mode 100644 index 0000000..a3fc226 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -0,0 +1,60 @@ +/* + * LookupTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.assertThat +import assertk.assertions.any +import assertk.assertions.contains +import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup +import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois +import kotlin.test.Test + +/** + * The `Lookup Test` class. + */ +class LookupTest { + @Test + @Throws(Exception::class) + fun testLookup() { + var result = nslookup("apple.com") + assertThat(result, "lookup(apple.com)").contains("17.253.144.10") + + result = nslookup("204.122.16.136") + assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") + } + + @Test + @Throws(Exception::class) + fun testWhois() { + val result = whois("17.178.96.59", Lookup.WHOIS_HOST) + assertThat(result, "whois(17.178.96.59").any { it.contains("Apple Inc.") } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt new file mode 100644 index 0000000..f4b5e99 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -0,0 +1,54 @@ +/* + * MastodonTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.assertThat +import assertk.assertions.contains +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot +import kotlin.test.Test + +class MastodonTest : LocalProperties() { + @Test + @Throws(ModuleException::class) + fun testToot() { + val msg = "Testing Mastodon API from ${getHostName()}" + assertThat( + toot( + getProperty(Mastodon.ACCESS_TOKEN_PROP), + getProperty(Mastodon.INSTANCE_PROP), + getProperty(Mastodon.HANDLE_PROP), + msg, + true + ) + ).contains(msg) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt new file mode 100644 index 0000000..1416eec --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -0,0 +1,105 @@ +/* + * ModuleExceptionTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import org.testng.annotations.DataProvider +import kotlin.test.Test +import java.io.IOException +import java.lang.reflect.Method + +/** + * The `ModuleExceptionTest` class. + */ +class ModuleExceptionTest { + companion object { + const val DEBUG_MESSAGE = "debugMessage" + const val MESSAGE = "message" + } + + @DataProvider(name = "dp") + fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array> { + return arrayOf( + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) + ) + } + + @Test(dataProvider = "dp") + fun testGetDebugMessage(e: ModuleException) { + assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) + } + + @Test(dataProvider = "dp") + fun testGetMessage(e: ModuleException) { + assertThat(e).hasMessage(MESSAGE) + } + + @Test + fun testSanitizeMessage() { + val apiKey = "1234567890" + var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) + assertThat( + e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" + ).isNotNull().all { + contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") + doesNotContain(apiKey, "me") + } + + e = ModuleException(DEBUG_MESSAGE, MESSAGE, null) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE) + + e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException()) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE) + + e = ModuleException(DEBUG_MESSAGE, apiKey) + assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() + .doesNotContain(apiKey) + + val msg: String? = null + e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg)) + assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull() + + e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey")) + assertThat( + e.sanitize(" ", apiKey, "foo").message, + "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" + ).isNotNull().all { + doesNotContain(apiKey) + endsWith("xxx is xxxxxxxxxx") + } + assertThat(e.sanitize(), "exception should be unchanged").isEqualTo(e) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt new file mode 100644 index 0000000..d1399e0 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -0,0 +1,54 @@ +/* + * PingTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isNotEmpty +import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing +import kotlin.test.Test + +/** + * The `PingTest` class. + */ +class PingTest { + @Test + fun testPingsArray() { + assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty() + } + + @Test + fun testRandomPing() { + for (i in 0..9) { + assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing()) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt new file mode 100644 index 0000000..f836b0e --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -0,0 +1,50 @@ +/* + * RockPaperScissorsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import assertk.assertThat +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw +import kotlin.test.Test + +class RockPaperScissorsTest { + @Test + fun testWinLoseOrDraw() { + assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win") + assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win") + assertThat(winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win") + assertThat(winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose") + assertThat(winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose") + assertThat(winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose") + assertThat(winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt new file mode 100644 index 0000000..d96812b --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -0,0 +1,85 @@ +/* + * StockQuoteTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test + +/** + * The `StockQuoteTest` class. + */ +class StockQuoteTest : LocalProperties() { + private fun buildMatch(label: String): String { + return "${label}:[ ]+[0-9.]+".prependIndent() + } + + @Test + @Throws(ModuleException::class) + fun testGetQuote() { + val apiKey = getProperty(StockQuote.API_KEY_PROP) + try { + var symbol = "apple inc" + val messages = getQuote(symbol, apiKey) + assertThat(messages, "response not empty").isNotEmpty() + assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) + assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) + assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) + .matches(buildMatch("Previous").toRegex()) + assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) + + symbol = "blahfoo" + assertThat(getQuote(symbol, apiKey).first(), "getQuote($symbol)").all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) + } + assertThat(getQuote("", "apikey").first(), "getQuote(empty)").all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) + } + assertFailure { getQuote("test", "") }.isInstanceOf(ModuleException::class.java).hasNoCause() + } catch (e: ModuleException) { + // Avoid displaying api keys in CI logs + if ("true" == System.getenv("CI")) { + throw e.sanitize(apiKey) + } else { + throw e + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt new file mode 100644 index 0000000..66f4d22 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -0,0 +1,117 @@ +/* + * Weather2Test.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import net.aksingh.owmjapis.api.APIException +import net.aksingh.owmjapis.core.OWM +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP +import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC +import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry +import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather +import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh +import net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test + +/** + * The `Weather2Test` class. + */ +class Weather2Test : LocalProperties() { + @Test + fun testFtoC() { + val t = ftoC(32.0) + assertThat(t.second, "32 °F is 0 °C").isEqualTo(0) + } + + @Test + fun testGetCountry() { + assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) + assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) + + val country = OWM.Country.entries.toTypedArray() + repeat(3) { + val rand = country[(country.indices).random()] + assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) + } + } + + @Test + fun testMphToKmh() { + val w = mphToKmh(0.62) + assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1) + } + + @Test + @Throws(ModuleException::class) + fun testWeather() { + var query = "98204" + var messages = getWeather(query, getProperty(API_KEY_PROP)) + assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { + contains("Everett, United States") + contains("US") + } + assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS") + + query = "San Francisco" + messages = getWeather(query, getProperty(API_KEY_PROP)) + assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { + contains("San Francisco") + contains("US") + } + assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959") + + query = "London, GB" + messages = getWeather(query, getProperty(API_KEY_PROP)) + assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { + contains("London, United Kingdom") + contains("GB") + } + assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("2643743") + + try { + query = "Foo, US" + getWeather(query, getProperty(API_KEY_PROP)) + } catch (e: ModuleException) { + assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java) + } + + query = "test" + assertFailure { getWeather(query, "") }.isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getWeather(query, null) }.isInstanceOf(ModuleException::class.java).hasNoCause() + + messages = getWeather("", "apikey") + assertThat(messages, "getWeather(empty)").index(0).prop(Message::isError).isTrue() + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt new file mode 100644 index 0000000..855522c --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -0,0 +1,77 @@ +/* + * WolframAlphaTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.modules + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.hasMessage +import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram +import kotlin.test.Test + +class WolframAlphaTest : LocalProperties() { + @Test + fun testAppId() { + assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } + .isInstanceOf(ModuleException::class.java) + .hasMessage("Error 1: Invalid appid") + + assertFailure { queryWolfram("1 gallon to liter", appId = "") } + .isInstanceOf(ModuleException::class.java) + } + + @Test(groups = ["modules", "no-ci"]) + @Throws(ModuleException::class) + fun queryWolframTest() { + val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) + try { + var query = "SFO to SEA" + assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles") + + query = "SFO to LAX" + assertThat( + queryWolfram(query, WolframAlpha.METRIC, apiKey), + "queryWolfram($query)" + ).contains("kilometers") + } catch (e: ModuleException) { + // Avoid displaying api key in CI logs + if ("true" == System.getenv("CI")) { + throw e.sanitize(apiKey) + } else { + throw e + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt new file mode 100644 index 0000000..7931f33 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -0,0 +1,70 @@ +/* + * WordTimeTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import assertk.assertThat +import assertk.assertions.endsWith +import assertk.assertions.matches +import assertk.assertions.startsWith +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD +import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP +import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time +import org.pircbotx.Colors +import kotlin.test.Test +import java.time.ZoneId + +/** + * The `WordTimeTest` class. + */ +class WordTimeTest { + @Test + fun testTime() { + assertThat(time(), "time()").matches( + ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + + "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + + "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() + ) + assertThat(time(""), "time()").endsWith("Los Angeles".bold()) + assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) + assertThat(time("GB"), "time(GB)").endsWith("London".bold()) + assertThat(time("FR"), "time(FR)").endsWith("Paris".bold()) + assertThat(time("BLAH"), "time(BLAH)").startsWith("Unsupported") + assertThat(time("BEAT"), "time($BEATS_KEYWORD)").matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) + } + + @Test + fun testZones() { + COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { + assertThat(ZoneId.of(it.value)) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt b/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt new file mode 100644 index 0000000..32a0495 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -0,0 +1,109 @@ +/* + * MessageTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.msg + +import assertk.all +import assertk.assertThat +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import assertk.assertions.prop +import kotlin.test.Test + +class MessageTest { + @Test + fun testConstructor() { + var msg = Message("foo") + + msg.isError = true + assertThat(msg.isNotice, "message is notice").isTrue() + + msg = Message("foo", isError = true) + assertThat(msg.isNotice, "message is notice too").isTrue() + } + + @Test + fun testErrorMessage() { + val msg = ErrorMessage("foo") + assertThat(msg).all { + prop(Message::isError).isTrue() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testIsError() { + val msg = Message("foo") + msg.isError = true + assertThat(msg).all { + prop(Message::isError).isTrue() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + msg.isError = false + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testNoticeMessage() { + val msg = NoticeMessage("food") + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testPrivateMessage() { + val msg = PrivateMessage("foo") + assertThat(msg).all { + prop(Message::isPrivate).isTrue() + prop(Message::isError).isFalse() + prop(Message::isNotice).isFalse() + } + } + + @Test + fun testPublicMessage() { + val msg = PublicMessage("foo") + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isFalse() + prop(Message::isPrivate).isFalse() + } + } +} diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 623b3c3..7c85194 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,9 +1,10 @@ -image: maven:3-eclipse-temurin-17 +image: openjdk:17 pipelines: default: - step: - caches: - - gradle + name: Test with bld script: - - bash ./gradlew check + - ./bld download + - ./bld compile + - ./bld test diff --git a/bld b/bld new file mode 100755 index 0000000..77721d6 --- /dev/null +++ b/bld @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build net.thauvin.erik.MobibotBuild "$@" \ No newline at end of file diff --git a/bld.bat b/bld.bat new file mode 100644 index 0000000..12ffa36 --- /dev/null +++ b/bld.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build net.thauvin.erik.MobibotBuild %* \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 6507e20..0000000 --- a/build.gradle +++ /dev/null @@ -1,247 +0,0 @@ -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import io.gitlab.arturbosch.detekt.Detekt -import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask - -plugins { - id 'application' - id 'com.github.ben-manes.versions' version '0.49.0' - id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.23.3' - id 'java' - id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.9.20' - id 'org.jetbrains.kotlin.kapt' version '1.9.20' - id 'org.jetbrains.kotlinx.kover' version '0.7.4' - id 'org.sonarqube' version '4.4.1.3373' - id 'pmd' -} - -defaultTasks 'deploy' - -final def packageName = 'net.thauvin.erik.mobibot' -final def deployDir = 'deploy' -final def semverProcessor = "net.thauvin.erik:semver:1.2.1" - -final def isCI = (System.getenv('CI') != null) - -def isNonStable = { String version -> - def stableKeyword = ['RELEASE', 'FINAL', 'GA', 'JRE'].any { it -> version.toUpperCase().contains(it) } - def regex = /^[0-9,.v-]+(-r)?$/ - return !stableKeyword && !(version ==~ regex) -} - -mainClassName = packageName + '.Mobibot' - -ext.versions = [ - log4j: '2.21.1', - pmd : '6.55.0', -] - -repositories { - mavenLocal() - mavenCentral() - maven { url 'https://jitpack.io' } - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } -} - -dependencies { - kapt(semverProcessor) - compileOnly(semverProcessor) - - // PircBotX - implementation 'com.github.pircbotx:pircbotx:2.3.1' - // implementation fileTree(dir: 'lib', include: '*.jar') - - // Commons (mostly for PircBotX) - implementation 'org.apache.commons:commons-lang3:3.13.0' - implementation 'org.apache.commons:commons-text:1.11.0' - implementation 'commons-codec:commons-codec:1.16.0' - implementation 'commons-net:commons-net:3.10.0' - - // Google - implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.1.3-jre' - - // Kotlin - implementation platform('org.jetbrains.kotlin:kotlin-bom') - implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' - implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6' - - // Logging - implementation 'org.slf4j:slf4j-api:2.0.9' - implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" - implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" - implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" - - implementation 'com.rometools:rome:2.1.0' - implementation 'com.squareup.okhttp3:okhttp:4.12.0' - implementation 'net.aksingh:owm-japis:2.5.3.0' - implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20231013' - implementation 'org.jsoup:jsoup:1.16.2' - - // Thauvin - implementation 'net.thauvin.erik:cryptoprice:1.0.1' - implementation 'net.thauvin.erik:jokeapi:0.9.0' - implementation 'net.thauvin.erik:pinboard-poster:1.1.0' - implementation 'net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0' - - testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.27.0' -// testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' -// testImplementation "org.mockito:mockito-core:4.0.0" - testImplementation 'org.testng:testng:7.8.0' -} - -test { - useTestNG() { - // excludeGroups.add('twitter') - if (isCI) { - excludeGroups.add('no-ci') - } - if (!excludeGroups.isEmpty()) { - println "Excluded test groups: ${excludeGroups}" - } - } -} - -tasks.withType(Test).configureEach { - testLogging { - exceptionFormat = 'full' - events('skipped', 'failed') - } -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -kotlin { - jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} - -kapt { - includeCompileClasspath = false - arguments { - arg('semver.project.dir', projectDir) - } -} - -tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' -} - - -compileJava { - dependsOn 'incrementBuildMeta' - options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation'] -} - -tasks.named("dependencyUpdates").configure { - rejectVersionIf { - isNonStable(it.candidate.version) - } -} - -pmd { - toolVersion = versions.pmd - ignoreFailures = true - ruleSets = [] - ruleSetFiles = files("${projectDir}/config/pmd.xml") - consoleOutput = true -} - -detekt { - //toolVersion = "main-SNAPSHOT" - baseline = file("${projectDir}/config/detekt/baseline.xml") -} - -tasks.withType(Detekt).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - -tasks.withType(DetektCreateBaselineTask).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - -jar { - manifest.attributes('Main-Class': mainClassName, - 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) - archiveVersion.set("") - exclude('log4j2.xml') -} - -clean { - doFirst { - project.delete(fileTree(deployDir)) - } -} - -run { - args('-h') -} - -incrementBuildMeta { - doFirst { - if (isCI) { - println 'No increment with CI.' - } else { - buildMeta = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) - } - } -} - -koverReport { - defaults { - xml { - onCheck = true - } - html { - onCheck = true - } - } -} - -sonarqube { - properties { - property('sonar.organization', 'ethauvin-github') - property('sonar.projectKey', 'ethauvin_mobibot') - property('sonar.host.url', 'https://sonarcloud.io') - property('sonar.coverage.jacoco.xmlReportPaths', "${layout.buildDirectory.get()}/reports/kover/report.xml") - } -} - -tasks.register('copyToDeploy', Copy) { - from('properties', jar) - into deployDir -} - -tasks.register('copyToDeployLib', Copy) { - from(configurations.runtimeClasspath) { - exclude 'annotations-*.jar' - } - into(deployDir + '/lib') -} - -tasks.register('deploy') { - description = "Copies all needed files to the ${deployDir} directory." - group = 'Publishing' - dependsOn(assemble, jar) - outputs.dir deployDir - inputs.files(copyToDeploy, copyToDeployLib) - doLast { - file(deployDir + '/logs').mkdir() - } - mustRunAfter(clean) -} - -tasks.register('release') { - group = 'Publishing' - description = 'Releases new version.' - dependsOn(clean, check, deploy) - mustRunAfter clean -} diff --git a/deploy.sh b/deploy.sh index ce3fde5..58dd32d 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,6 @@ #!/bin/bash -./gradlew release +./bld jar deploy [ $? -eq 0 ] && sftp nix3.thauvin.us <nW$ZR+`W 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鯊%1Et zwk~hMkhq;MOw^Q5`7oC{CUUyTw9x>^%*FHx^qJw(LB+E0WBX@{Ghw;)6aA-KyYg8p z7XDveQOpEr;B4je@2~usI5BlFadedX^ma{b{ypd|RNYqo#~d*mj&y`^iojR}s%~vF z(H!u`yx68D1Tj(3(m;Q+Ma}s2n#;O~bcB1`lYk%Irx60&-nWIUBr2x&@}@76+*zJ5 ze&4?q8?m%L9c6h=J$WBzbiTf1Z-0Eb5$IZs>lvm$>1n_Mezp*qw_pr8<8$6f)5f<@ zyV#tzMCs51nTv_5ca`x`yfE5YA^*%O_H?;tWYdM_kHPubA%vy47i=9>Bq) zRQ&0UwLQHeswmB1yP)+BiR;S+Vc-5TX84KUA;8VY9}yEj0eESSO`7HQ4lO z4(CyA8y1G7_C;6kd4U3K-aNOK!sHE}KL_-^EDl(vB42P$2Km7$WGqNy=%fqB+ zSLdrlcbEH=T@W8V4(TgoXZ*G1_aq$K^@ek=TVhoKRjw;HyI&coln|uRr5mMOy2GXP zwr*F^Y|!Sjr2YQXX(Fp^*`Wk905K%$bd03R4(igl0&7IIm*#f`A!DCarW9$h$z`kYk9MjjqN&5-DsH@8xh63!fTNPxWsFQhNv z#|3RjnP$Thdb#Ys7M+v|>AHm0BVTw)EH}>x@_f4zca&3tXJhTZ8pO}aN?(dHo)44Z z_5j+YP=jMlFqwvf3lq!57-SAuRV2_gJ*wsR_!Y4Z(trO}0wmB9%f#jNDHPdQGHFR; zZXzS-$`;7DQ5vF~oSgP3bNV$6Z(rwo6W(U07b1n3UHqml>{=6&-4PALATsH@Bh^W? z)ob%oAPaiw{?9HfMzpGb)@Kys^J$CN{uf*HX?)z=g`J(uK1YO^8~s1(ZIbG%Et(|q z$D@_QqltVZu9Py4R0Ld8!U|#`5~^M=b>fnHthzKBRr=i+w@0Vr^l|W;=zFT#PJ?*a zbC}G#It}rQP^Ait^W&aa6B;+0gNvz4cWUMzpv(1gvfw-X4xJ2Sv;mt;zb2Tsn|kSS zo*U9N?I{=-;a-OybL4r;PolCfiaL=y@o9{%`>+&FI#D^uy#>)R@b^1ue&AKKwuI*` zx%+6r48EIX6nF4o;>)zhV_8(IEX})NGU6Vs(yslrx{5fII}o3SMHW7wGtK9oIO4OM&@@ECtXSICLcPXoS|{;=_yj>hh*%hP27yZwOmj4&Lh z*Nd@OMkd!aKReoqNOkp5cW*lC)&C$P?+H3*%8)6HcpBg&IhGP^77XPZpc%WKYLX$T zsSQ$|ntaVVOoRat$6lvZO(G-QM5s#N4j*|N_;8cc2v_k4n6zx9c1L4JL*83F-C1Cn zaJhd;>rHXB%%ZN=3_o3&Qd2YOxrK~&?1=UuN9QhL$~OY-Qyg&})#ez*8NpQW_*a&kD&ANjedxT0Ar z<6r{eaVz3`d~+N~vkMaV8{F?RBVemN(jD@S8qO~L{rUw#=2a$V(7rLE+kGUZ<%pdr z?$DP|Vg#gZ9S}w((O2NbxzQ^zTot=89!0^~hE{|c9q1hVzv0?YC5s42Yx($;hAp*E zyoGuRyphQY{Q2ee0Xx`1&lv(l-SeC$NEyS~8iil3_aNlnqF_G|;zt#F%1;J)jnPT& z@iU0S;wHJ2$f!juqEzPZeZkjcQ+Pa@eERSLKsWf=`{R@yv7AuRh&ALRTAy z8=g&nxsSJCe!QLchJ=}6|LshnXIK)SNd zRkJNiqHwKK{SO;N5m5wdL&qK`v|d?5<4!(FAsDxR>Ky#0#t$8XCMptvNo?|SY?d8b z`*8dVBlXTUanlh6n)!EHf2&PDG8sXNAt6~u-_1EjPI1|<=33T8 zEnA00E!`4Ave0d&VVh0e>)Dc}=FfAFxpsC1u9ATfQ`-Cu;mhc8Z>2;uyXtqpLb7(P zd2F9<3cXS} znMg?{&8_YFTGRQZEPU-XPq55%51}RJpw@LO_|)CFAt62-_!u_Uq$csc+7|3+TV_!h z+2a7Yh^5AA{q^m|=KSJL+w-EWDBc&I_I1vOr^}P8i?cKMhGy$CP0XKrQzCheG$}G# zuglf8*PAFO8%xop7KSwI8||liTaQ9NCAFarr~psQt)g*pC@9bORZ>m`_GA`_K@~&% zijH0z;T$fd;-Liw8%EKZas>BH8nYTqsK7F;>>@YsE=Rqo?_8}UO-S#|6~CAW0Oz1} z3F(1=+#wrBJh4H)9jTQ_$~@#9|Bc1Pd3rAIA_&vOpvvbgDJOM(yNPhJJq2%PCcMaI zrbe~toYzvkZYQ{ea(Wiyu#4WB#RRN%bMe=SOk!CbJZv^m?Flo5p{W8|0i3`hI3Np# zvCZqY%o258CI=SGb+A3yJe~JH^i{uU`#U#fvSC~rWTq+K`E%J@ zasU07&pB6A4w3b?d?q}2=0rA#SA7D`X+zg@&zm^iA*HVi z009#PUH<%lk4z~p^l0S{lCJk1Uxi=F4e_DwlfHA`X`rv(|JqWKAA5nH+u4Da+E_p+ zVmH@lg^n4ixs~*@gm_dgQ&eDmE1mnw5wBz9Yg?QdZwF|an67Xd*x!He)Gc8&2!urh z4_uXzbYz-aX)X1>&iUjGp;P1u8&7TID0bTH-jCL&Xk8b&;;6p2op_=y^m@Nq*0{#o!!A;wNAFG@0%Z9rHo zcJs?Th>Ny6+hI`+1XoU*ED$Yf@9f91m9Y=#N(HJP^Y@ZEYR6I?oM{>&Wq4|v0IB(p zqX#Z<_3X(&{H+{3Tr|sFy}~=bv+l=P;|sBz$wk-n^R`G3p0(p>p=5ahpaD7>r|>pm zv;V`_IR@tvZreIuv2EM7ZQHhO+qUgw#kOs%*ekY^n|=1#x9&c;Ro&I~{rG-#_3ZB1 z?|9}IFdbP}^DneP*T-JaoYHt~r@EfvnPE5EKUwIxjPbsr$% zfWW83pgWST7*B(o=kmo)74$8UU)v0{@4DI+ci&%=#90}!CZz|rnH+Mz=HN~97G3~@ z;v5(9_2%eca(9iu@J@aqaMS6*$TMw!S>H(b z4(*B!|H|8&EuB%mITr~O?vVEf%(Gr)6E=>H~1VR z&1YOXluJSG1!?TnT)_*YmJ*o_Q@om~(GdrhI{$Fsx_zrkupc#y{DK1WOUR>tk>ZE) ziOLoBkhZZ?0Uf}cm>GsA>Rd6V8@JF)J*EQlQ<=JD@m<)hyElXR0`pTku*3MU`HJn| zIf7$)RlK^pW-$87U;431;Ye4Ie+l~_B3*bH1>*yKzn23cH0u(i5pXV! z4K?{3oF7ZavmmtTq((wtml)m6i)8X6ot_mrE-QJCW}Yn!(3~aUHYG=^fA<^~`e3yc z-NWTb{gR;DOUcK#zPbN^D*e=2eR^_!(!RKkiwMW@@yYtEoOp4XjOGgzi`;=8 zi3`Ccw1%L*y(FDj=C7Ro-V?q)-%p?Ob2ZElu`eZ99n14-ZkEV#y5C+{Pq87Gu3&>g zFy~Wk7^6v*)4pF3@F@rE__k3ikx(hzN3@e*^0=KNA6|jC^B5nf(XaoQaZN?Xi}Rn3 z$8&m*KmWvPaUQ(V<#J+S&zO|8P-#!f%7G+n_%sXp9=J%Z4&9OkWXeuZN}ssgQ#Tcj z8p6ErJQJWZ+fXLCco=RN8D{W%+*kko*2-LEb))xcHwNl~Xmir>kmAxW?eW50Osw3# zki8Fl$#fvw*7rqd?%E?}ZX4`c5-R&w!Y0#EBbelVXSng+kUfeUiqofPehl}$ormli zg%r)}?%=?_pHb9`Cq9Z|B`L8b>(!+8HSX?`5+5mm81AFXfnAt1*R3F z%b2RPIacKAddx%JfQ8l{3U|vK@W7KB$CdLqn@wP^?azRks@x8z59#$Q*7q!KilY-P zHUbs(IFYRGG1{~@RF;Lqyho$~7^hNC`NL3kn^Td%A7dRgr_&`2k=t+}D-o9&C!y^? z6MsQ=tc3g0xkK(O%DzR9nbNB(r@L;1zQrs8mzx&4dz}?3KNYozOW5;=w18U6$G4U2 z#2^qRLT*Mo4bV1Oeo1PKQ2WQS2Y-hv&S|C7`xh6=Pj7MNLC5K-zokZ67S)C;(F0Dd zloDK2_o1$Fmza>EMj3X9je7e%Q`$39Dk~GoOj89-6q9|_WJlSl!!+*{R=tGp z8u|MuSwm^t7K^nUe+^0G3dkGZr3@(X+TL5eah)K^Tn zXEtHmR9UIaEYgD5Nhh(s*fcG_lh-mfy5iUF3xxpRZ0q3nZ=1qAtUa?(LnT9I&~uxX z`pV?+=|-Gl(kz?w!zIieXT}o}7@`QO>;u$Z!QB${a08_bW0_o@&9cjJUXzVyNGCm8 zm=W+$H!;_Kzp6WQqxUI;JlPY&`V}9C$8HZ^m?NvI*JT@~BM=()T()Ii#+*$y@lTZBkmMMda>7s#O(1YZR+zTG@&}!EXFG{ zEWPSDI5bFi;NT>Yj*FjH((=oe%t%xYmE~AGaOc4#9K_XsVpl<4SP@E!TgC0qpe1oi zNpxU2b0(lEMcoibQ-G^cxO?ySVW26HoBNa;n0}CWL*{k)oBu1>F18X061$SP{Gu67 z-v-Fa=Fl^u3lnGY^o5v)Bux}bNZ~ z5pL+7F_Esoun8^5>z8NFoIdb$sNS&xT8_|`GTe8zSXQzs4r^g0kZjg(b0bJvz`g<70u9Z3fQILX1Lj@;@+##bP|FAOl)U^9U>0rx zGi)M1(Hce)LAvQO-pW!MN$;#ZMX?VE(22lTlJrk#pB0FJNqVwC+*%${Gt#r_tH9I_ z;+#)#8cWAl?d@R+O+}@1A^hAR1s3UcW{G+>;X4utD2d9X(jF555}!TVN-hByV6t+A zdFR^aE@GNNgSxxixS2p=on4(+*+f<8xrwAObC)D5)4!z7)}mTpb7&ofF3u&9&wPS< zB62WHLGMhmrmOAgmJ+|c>qEWTD#jd~lHNgT0?t-p{T=~#EMcB| z=AoDKOL+qXCfk~F)-Rv**V}}gWFl>liXOl7Uec_8v)(S#av99PX1sQIVZ9eNLkhq$ zt|qu0b?GW_uo}TbU8!jYn8iJeIP)r@;!Ze_7mj{AUV$GEz6bDSDO=D!&C9!M@*S2! zfGyA|EPlXGMjkH6x7OMF?gKL7{GvGfED=Jte^p=91FpCu)#{whAMw`vSLa`K#atdN zThnL+7!ZNmP{rc=Z>%$meH;Qi1=m1E3Lq2D_O1-X5C;!I0L>zur@tPAC9*7Jeh)`;eec}1`nkRP(%iv-`N zZ@ip-g|7l6Hz%j%gcAM}6-nrC8oA$BkOTz^?dakvX?`^=ZkYh%vUE z9+&)K1UTK=ahYiaNn&G5nHUY5niLGus@p5E2@RwZufRvF{@$hW{;{3QhjvEHMvduO z#Wf-@oYU4ht?#uP{N3utVzV49mEc9>*TV_W2TVC`6+oI)zAjy$KJrr=*q##&kobiQ z1vNbya&OVjK`2pdRrM?LuK6BgrLN7H_3m z!qpNKg~87XgCwb#I=Q&0rI*l$wM!qTkXrx1ko5q-f;=R2fImRMwt5Qs{P*p^z@9ex z`2#v(qE&F%MXlHpdO#QEZyZftn4f05ab^f2vjxuFaat2}jke{j?5GrF=WYBR?gS(^ z9SBiNi}anzBDBRc+QqizTTQuJrzm^bNA~A{j%ugXP7McZqJ}65l10({wk++$=e8O{ zxWjG!Qp#5OmI#XRQQM?n6?1ztl6^D40hDJr?4$Wc&O_{*OfMfxe)V0=e{|N?J#fgE>j9jAajze$iN!*yeF%jJU#G1c@@rm zolGW!j?W6Q8pP=lkctNFdfgUMg92wlM4E$aks1??M$~WQfzzzXtS)wKrr2sJeCN4X zY(X^H_c^PzfcO8Bq(Q*p4c_v@F$Y8cHLrH$`pJ2}=#*8%JYdqsqnGqEdBQMpl!Ot04tUGSXTQdsX&GDtjbWD=prcCT9(+ z&UM%lW%Q3yrl1yiYs;LxzIy>2G}EPY6|sBhL&X&RAQrSAV4Tlh2nITR?{6xO9ujGu zr*)^E`>o!c=gT*_@6S&>0POxcXYNQd&HMw6<|#{eSute2C3{&h?Ah|cw56-AP^f8l zT^kvZY$YiH8j)sk7_=;gx)vx-PW`hbSBXJGCTkpt;ap(}G2GY=2bbjABU5)ty%G#x zAi07{Bjhv}>OD#5zh#$0w;-vvC@^}F! z#X$@)zIs1L^E;2xDAwEjaXhTBw2<{&JkF*`;c3<1U@A4MaLPe{M5DGGkL}#{cHL%* zYMG+-Fm0#qzPL#V)TvQVI|?_M>=zVJr9>(6ib*#z8q@mYKXDP`k&A4A};xMK0h=yrMp~JW{L?mE~ph&1Y1a#4%SO)@{ zK2juwynUOC)U*hVlJU17%llUxAJFuKZh3K0gU`aP)pc~bE~mM!i1mi!~LTf>1Wp< zuG+ahp^gH8g8-M$u{HUWh0m^9Rg@cQ{&DAO{PTMudV6c?ka7+AO& z746QylZ&Oj`1aqfu?l&zGtJnpEQOt;OAFq19MXTcI~`ZcoZmyMrIKDFRIDi`FH)w; z8+*8tdevMDv*VtQi|e}CnB_JWs>fhLOH-+Os2Lh!&)Oh2utl{*AwR)QVLS49iTp{6 z;|172Jl!Ml17unF+pd+Ff@jIE-{Oxv)5|pOm@CkHW?{l}b@1>Pe!l}VccX#xp@xgJ zyE<&ep$=*vT=}7vtvif0B?9xw_3Gej7mN*dOHdQPtW5kA5_zGD zpA4tV2*0E^OUimSsV#?Tg#oiQ>%4D@1F5@AHwT8Kgen$bSMHD3sXCkq8^(uo7CWk`mT zuslYq`6Yz;L%wJh$3l1%SZv#QnG3=NZ=BK4yzk#HAPbqXa92;3K5?0kn4TQ`%E%X} z&>Lbt!!QclYKd6+J7Nl@xv!uD%)*bY-;p`y^ZCC<%LEHUi$l5biu!sT3TGGSTPA21 zT8@B&a0lJHVn1I$I3I1I{W9fJAYc+8 zVj8>HvD}&O`TqU2AAb={?eT;0hyL(R{|h23=4fDSZKC32;wWxsVj`P z3J3{M$PwdH!ro*Cn!D&=jnFR>BNGR<<|I8CI@+@658Dy(lhqbhXfPTVecY@L8%`3Q z1Fux2w?2C3th60jI~%OC9BtpNF$QPqcG+Pz96qZJ71_`0o0w_q7|h&O>`6U+^BA&5 zXd5Zp1Xkw~>M%RixTm&OqpNl8Q+ue=92Op_>T~_9UON?ZM2c0aGm=^A4ejrXj3dV9 zhh_bCt-b9`uOX#cFLj!vhZ#lS8Tc47OH>*)y#{O9?AT~KR9LntM|#l#Dlm^8{nZdk zjMl#>ZM%#^nK2TPzLcKxqx24P7R1FPlBy7LSBrRvx>fE$9AJ;7{PQm~^LBX^k#6Zq zw*Z(zJC|`!6_)EFR}8|n8&&Rbj8y028~P~sFXBFRt+tmqH-S3<%N;C&WGH!f3{7cm zy_fCAb9@HqaXa1Y5vFbxWf%#zg6SI$C+Uz5=CTO}e|2fjWkZ;Dx|84Ow~bkI=LW+U zuq;KSv9VMboRvs9)}2PAO|b(JCEC_A0wq{uEj|3x@}*=bOd zwr{TgeCGG>HT<@Zeq8y}vTpwDg#UBvD)BEs@1KP$^3$sh&_joQPn{hjBXmLPJ{tC) z*HS`*2+VtJO{|e$mM^|qv1R*8i(m1`%)}g=SU#T#0KlTM2RSvYUc1fP+va|4;5}Bfz98UvDCpq7}+SMV&;nX zQw~N6qOX{P55{#LQkrZk(e5YGzr|(B;Q;ju;2a`q+S9bsEH@i1{_Y0;hWYn1-79jl z5c&bytD*k)GqrVcHn6t-7kinadiD>B{Tl`ZY@`g|b~pvHh5!gKP4({rp?D0aFd_cN zhHRo4dd5^S6ViN(>(28qZT6E>??aRhc($kP`>@<+lIKS5HdhjVU;>f7<4))E*5|g{ z&d1}D|vpuV^eRj5j|xx9nwaCxXFG?Qbjn~_WSy=N}P0W>MP zG-F%70lX5Xr$a)2i6?i|iMyM|;Jtf*hO?=Jxj12oz&>P=1#h~lf%#fc73M2_(SUM- zf&qnjS80|_Y0lDgl&I?*eMumUklLe_=Td!9G@eR*tcPOgIShJipp3{A10u(4eT~DY zHezEj8V+7m!knn7)W!-5QI3=IvC^as5+TW1@Ern@yX| z7Nn~xVx&fGSr+L%4iohtS3w^{-H1A_5=r&x8}R!YZvp<2T^YFvj8G_vm}5q;^UOJf ztl=X3iL;;^^a#`t{Ae-%5Oq{?M#s6Npj+L(n-*LMI-yMR{)qki!~{5z{&`-iL}lgW zxo+tnvICK=lImjV$Z|O_cYj_PlEYCzu-XBz&XC-JVxUh9;6*z4fuBG+H{voCC;`~GYV|hj%j_&I zDZCj>Q_0RCwFauYoVMiUSB+*Mx`tg)bWmM^SwMA+?lBg12QUF_x2b)b?qb88K-YUd z0dO}3k#QirBV<5%jL$#wlf!60dizu;tsp(7XLdI=eQs?P`tOZYMjVq&jE)qK*6B^$ zBe>VvH5TO>s>izhwJJ$<`a8fakTL!yM^Zfr2hV9`f}}VVUXK39p@G|xYRz{fTI+Yq z20d=)iwjuG9RB$%$^&8#(c0_j0t_C~^|n+c`Apu|x7~;#cS-s=X1|C*YxX3ailhg_|0`g!E&GZJEr?bh#Tpb8siR=JxWKc{#w7g zWznLwi;zLFmM1g8V5-P#RsM@iX>TK$xsWuujcsVR^7TQ@!+vCD<>Bk9tdCo7Mzgq5 zv8d>dK9x8C@Qoh01u@3h0X_`SZluTb@5o;{4{{eF!-4405x8X7hewZWpz z2qEi4UTiXTvsa(0X7kQH{3VMF>W|6;6iTrrYD2fMggFA&-CBEfSqPlQDxqsa>{e2M z(R5PJ7uOooFc|9GU0ELA%m4&4Ja#cQpNw8i8ACAoK6?-px+oBl_yKmenZut#Xumjz zk8p^OV2KY&?5MUwGrBOo?ki`Sxo#?-Q4gw*Sh0k`@ zFTaYK2;}%Zk-68`#5DXU$2#=%YL#S&MTN8bF+!J2VT6x^XBci6O)Q#JfW{YMz) zOBM>t2rSj)n#0a3cjvu}r|k3od6W(SN}V-cL?bi*Iz-8uOcCcsX0L>ZXjLqk zZu2uHq5B|Kt>e+=pPKu=1P@1r9WLgYFq_TNV1p9pu0erHGd!+bBp!qGi+~4A(RsYN@CyXNrC&hxGmW)u5m35OmWwX`I+0yByglO`}HC4nGE^_HUs^&A(uaM zKPj^=qI{&ayOq#z=p&pnx@@k&I1JI>cttJcu@Ihljt?6p^6{|ds`0MoQwp+I{3l6` zB<9S((RpLG^>=Kic`1LnhpW2=Gu!x`m~=y;A`Qk!-w`IN;S8S930#vBVMv2vCKi}u z6<-VPrU0AnE&vzwV(CFC0gnZYcpa-l5T0ZS$P6(?9AM;`Aj~XDvt;Jua=jIgF=Fm? zdp=M$>`phx%+Gu};;-&7T|B1AcC#L4@mW5SV_^1BRbo6;2PWe$r+npRV`yc;T1mo& z+~_?7rA+(Um&o@Tddl zL_hxvWk~a)yY}%j`Y+200D%9$bWHy&;(yj{jpi?Rtz{J66ANw)UyPOm;t6FzY3$hx zcn)Ir79nhFvNa7^a{SHN7XH*|Vlsx`CddPnA&Qvh8aNhEA;mPVv;Ah=k<*u!Zq^7 z<=xs*iQTQOMMcg|(NA_auh@x`3#_LFt=)}%SQppP{E>mu_LgquAWvh<>L7tf9+~rO znwUDS52u)OtY<~!d$;m9+87aO+&`#2ICl@Y>&F{jI=H(K+@3M1$rr=*H^dye#~TyD z!){#Pyfn+|ugUu}G;a~!&&0aqQ59U@UT3|_JuBlYUpT$2+11;}JBJ`{+lQN9T@QFY z5+`t;6(TS0F?OlBTE!@7D`8#URDNqx2t6`GZ{ZgXeS@v%-eJzZOHz18aS|svxII$a zZeFjrJ*$IwX$f-Rzr_G>xbu@euGl)B7pC&S+CmDJBg$BoV~jxSO#>y z33`bupN#LDoW0feZe0%q8un0rYN|eRAnwDHQ6e_)xBTbtoZtTA=Fvk){q}9Os~6mQ zKB80VI_&6iSq`LnK7*kfHZoeX6?WE}8yjuDn=2#JG$+;-TOA1%^=DnXx%w{b=w}tS zQbU3XxtOI8E(!%`64r2`zog;5<0b4i)xBmGP^jiDZ2%HNSxIf3@wKs~uk4%3Mxz;~ zts_S~E4>W+YwI<-*-$U8*^HKDEa8oLbmqGg?3vewnaNg%Mm)W=)lcC_J+1ov^u*N3 zXJ?!BrH-+wGYziJq2Y#vyry6Z>NPgkEk+Ke`^DvNRdb>Q2Nlr#v%O@<5hbflI6EKE z9dWc0-ORk^T}jP!nkJ1imyjdVX@GrjOs%cpgA8-c&FH&$(4od#x6Y&=LiJZPINVyW z0snY$8JW@>tc2}DlrD3StQmA0Twck~@>8dSix9CyQOALcREdxoM$Sw*l!}bXKq9&r zysMWR@%OY24@e`?+#xV2bk{T^C_xSo8v2ZI=lBI*l{RciPwuE>L5@uhz@{!l)rtVlWC>)6(G)1~n=Q|S!{E9~6*fdpa*n z!()-8EpTdj=zr_Lswi;#{TxbtH$8*G=UM`I+icz7sr_SdnHXrv=?iEOF1UL+*6O;% zPw>t^kbW9X@oEXx<97%lBm-9?O_7L!DeD)Me#rwE54t~UBu9VZ zl_I1tBB~>jm@bw0Aljz8! zXBB6ATG6iByKIxs!qr%pz%wgqbg(l{65DP4#v(vqhhL{0b#0C8mq`bnqZ1OwFV z7mlZZJFMACm>h9v^2J9+^_zc1=JjL#qM5ZHaThH&n zXPTsR8(+)cj&>Un{6v*z?@VTLr{TmZ@-fY%*o2G}*G}#!bmqpoo*Ay@U!JI^Q@7gj;Kg-HIrLj4}#ec4~D2~X6vo;ghep-@&yOivYP zC19L0D`jjKy1Yi-SGPAn94(768Tcf$urAf{)1)9W58P`6MA{YG%O?|07!g9(b`8PXG1B1Sh0?HQmeJtP0M$O$hI z{5G`&9XzYhh|y@qsF1GnHN|~^ru~HVf#)lOTSrv=S@DyR$UKQk zjdEPFDz{uHM&UM;=mG!xKvp;xAGHOBo~>_=WFTmh$chpC7c`~7?36h)7$fF~Ii}8q zF|YXxH-Z?d+Q+27Rs3X9S&K3N+)OBxMHn1u(vlrUC6ckBY@@jl+mgr#KQUKo#VeFm zFwNYgv0<%~Wn}KeLeD9e1$S>jhOq&(e*I@L<=I5b(?G(zpqI*WBqf|Zge0&aoDUsC zngMRA_Kt0>La+Erl=Uv_J^p(z=!?XHpenzn$%EA`JIq#yYF?JLDMYiPfM(&Csr#f{ zdd+LJL1by?xz|D8+(fgzRs~(N1k9DSyK@LJygwaYX8dZl0W!I&c^K?7)z{2is;OkE zd$VK-(uH#AUaZrp=1z;O*n=b?QJkxu`Xsw&7yrX0?(CX=I-C#T;yi8a<{E~?vr3W> zQrpPqOW2M+AnZ&p{hqmHZU-;Q(7?- zP8L|Q0RM~sB0w1w53f&Kd*y}ofx@c z5Y6B8qGel+uT1JMot$nT1!Tim6{>oZzJXdyA+4euOLME?5Fd_85Uk%#E*ln%y{u8Q z$|?|R@Hpb~yTVK-Yr_S#%NUy7EBfYGAg>b({J|5b+j-PBpPy$Ns`PaJin4JdRfOaS zE|<HjH%NuJgsd2wOlv>~y=np%=2)$M9LS|>P)zJ+Fei5vYo_N~B0XCn+GM76 z)Xz3tg*FRVFgIl9zpESgdpWAavvVViGlU8|UFY{{gVJskg*I!ZjWyk~OW-Td4(mZ6 zB&SQreAAMqwp}rjy`HsG({l2&q5Y52<@AULVAu~rWI$UbFuZs>Sc*x+XI<+ez%$U)|a^unjpiW0l0 zj1!K0(b6$8LOjzRqQ~K&dfbMIE=TF}XFAi)$+h}5SD3lo z%%Qd>p9se=VtQG{kQ;N`sI)G^u|DN#7{aoEd zkksYP%_X$Rq08);-s6o>CGJ<}v`qs%eYf+J%DQ^2k68C%nvikRsN?$ap--f+vCS`K z#&~)f7!N^;sdUXu54gl3L=LN>FB^tuK=y2e#|hWiWUls__n@L|>xH{%8lIJTd5`w? zSwZbnS;W~DawT4OwSJVdAylbY+u5S+ZH{4hAi2&}Iv~W(UvHg(1GTZRPz`@{SOqzy z(8g&Dz=$PfRV=6FgxN~zo+G8OoPI&d-thcGVR*_^(R8COTM@bq?fDwY{}WhsQS1AK zF6R1t8!RdFmfocpJ6?9Yv~;WYi~XPgs(|>{5})j!AR!voO7y9&cMPo#80A(`za@t>cx<0;qxM@S*m(jYP)dMXr*?q0E`oL;12}VAep179uEr8c<=D zr5?A*C{eJ`z9Ee;E$8)MECqatHkbHH z&Y+ho0B$31MIB-xm&;xyaFCtg<{m~M-QDbY)fQ>Q*Xibb~8ytxZQ?QMf9!%cV zU0_X1@b4d+Pg#R!`OJ~DOrQz3@cpiGy~XSKjZQQ|^4J1puvwKeScrH8o{bscBsowomu z^f12kTvje`yEI3eEXDHJ6L+O{Jv$HVj%IKb|J{IvD*l6IG8WUgDJ*UGz z3!C%>?=dlfSJ>4U88)V+`U-!9r^@AxJBx8R;)J4Fn@`~k>8>v0M9xp90OJElWP&R5 zM#v*vtT}*Gm1^)Bv!s72T3PB0yVIjJW)H7a)ilkAvoaH?)jjb`MP>2z{%Y?}83 zUIwBKn`-MSg)=?R)1Q0z3b>dHE^)D8LFs}6ASG1|daDly_^lOSy&zIIhm*HXm1?VS=_iacG);_I9c zUQH1>i#*?oPIwBMJkzi_*>HoUe}_4o>2(SHWzqQ=;TyhAHS;Enr7!#8;sdlty&(>d zl%5cjri8`2X^Ds`jnw7>A`X|bl=U8n+3LKLy(1dAu8`g@9=5iw$R0qk)w8Vh_Dt^U zIglK}sn^)W7aB(Q>HvrX=rxB z+*L)3DiqpQ_%~|m=44LcD4-bxO3OO*LPjsh%p(k?&jvLp0py57oMH|*IMa(<|{m1(0S|x)?R-mqJ=I;_YUZA>J z62v*eSK;5w!h8J+6Z2~oyGdZ68waWfy09?4fU&m7%u~zi?YPHPgK6LDwphgaYu%0j zurtw)AYOpYKgHBrkX189mlJ`q)w-f|6>IER{5Lk97%P~a-JyCRFjejW@L>n4vt6#hq;!|m;hNE||LK3nw1{bJOy+eBJjK=QqNjI;Q6;Rp5 z&035pZDUZ#%Oa;&_7x0T<7!RW`#YBOj}F380Bq?MjjEhrvlCATPdkCTTl+2efTX$k zH&0zR1n^`C3ef~^sXzJK-)52(T}uTG%OF8yDhT76L~|^+hZ2hiSM*QA9*D5odI1>& z9kV9jC~twA5MwyOx(lsGD_ggYmztXPD`2=_V|ks_FOx!_J8!zM zTzh^cc+=VNZ&(OdN=y4Juw)@8-85lwf_#VMN!Ed(eQiRiLB2^2e`4dp286h@v@`O%_b)Y~A; zv}r6U?zs&@uD_+(_4bwoy7*uozNvp?bXFoB8?l8yG0qsm1JYzIvB_OH4_2G*IIOwT zVl%HX1562vLVcxM_RG*~w_`FbIc!(T=3>r528#%mwwMK}uEhJ()3MEby zQQjzqjWkwfI~;Fuj(Lj=Ug0y`>~C7`w&wzjK(rPw+Hpd~EvQ-ufQOiB4OMpyUKJhw zqEt~jle9d7S~LI~$6Z->J~QJ{Vdn3!c}g9}*KG^Kzr^(7VI5Gk(mHLL{itj_hG?&K4Ws0+T4gLfi3eu$N=`s36geNC?c zm!~}vG6lx9Uf^5M;bWntF<-{p^bruy~f?sk9 zcETAPQZLoJ8JzMMg<-=ju4keY@SY%Wo?u9Gx=j&dfa6LIAB|IrbORLV1-H==Z1zCM zeZcOYpm5>U2fU7V*h;%n`8 zN95QhfD994={1*<2vKLCNF)feKOGk`R#K~G=;rfq}|)s20&MCa65 zUM?xF5!&e0lF%|U!#rD@I{~OsS_?=;s_MQ_b_s=PuWdC)q|UQ&ea)DMRh5>fpQjXe z%9#*x=7{iRCtBKT#H>#v%>77|{4_slZ)XCY{s3j_r{tdpvb#|r|sbS^dU1x70$eJMU!h{Y7Kd{dl}9&vxQl6Jt1a` zHQZrWyY0?!vqf@u-fxU_@+}u(%Wm>0I#KP48tiAPYY!TdW(o|KtVI|EUB9V`CBBNaBLVih7+yMVF|GSoIQD0Jfb{ z!OXq;(>Z?O`1gap(L~bUcp>Lc@Jl-})^=6P%<~~9ywY=$iu8pJ0m*hOPzr~q`23eX zgbs;VOxxENe0UMVeN*>uCn9Gk!4siN-e>x)pIKAbQz!G)TcqIJ0`JBBaX>1-4_XO_-HCS^vr2vjv#7KltDZdyQ{tlWh4$Gm zB>|O1cBDC)yG(sbnc*@w6e%e}r*|IhpXckx&;sQCwGdKH+3oSG-2)Bf#x`@<4ETAr z0My%7RFh6ZLiZ_;X6Mu1YmXx7C$lSZ^}1h;j`EZd6@%JNUe=btBE z%s=Xmo1Ps?8G`}9+6>iaB8bgjUdXT?=trMu|4yLX^m0Dg{m7rpKNJey|EwHI+nN1e zL^>qN%5Fg)dGs4DO~uwIdXImN)QJ*Jhpj7$fq_^`{3fwpztL@WBB}OwQ#Epo-mqMO zsM$UgpFiG&d#)lzEQ{3Q;)&zTw;SzGOah-Dpm{!q7<8*)Ti_;xvV2TYXa}=faXZy? z3y?~GY@kl)>G&EvEijk9y1S`*=zBJSB1iet>0;x1Ai)*`^{pj0JMs)KAM=@UyOGtO z3y0BouW$N&TnwU6!%zS%nIrnANvZF&vB1~P5_d`x-giHuG zPJ;>XkVoghm#kZXRf>qxxEix;2;D1CC~NrbO6NBX!`&_$iXwP~P*c($EVV|669kDO zKoTLZNF4Cskh!Jz5ga9uZ`3o%7Pv`d^;a=cXI|>y;zC3rYPFLQkF*nv(r>SQvD*## z(Vo%^9g`%XwS0t#94zPq;mYGLKu4LU3;txF26?V~A0xZbU4Lmy`)>SoQX^m7fd^*E z+%{R4eN!rIk~K)M&UEzxp9dbY;_I^c} zOc{wlIrN_P(PPqi51k_$>Lt|X6A^|CGYgKAmoI#Li?;Wq%q~q*L7ehZkUrMxW67Jl zhsb~+U?33QS>eqyN{(odAkbopo=Q$Az?L+NZW>j;#~@wCDX?=L5SI|OxI~7!Pli;e zELMFcZtJY3!|=Gr2L4>z8yQ-{To>(f80*#;6`4IAiqUw`=Pg$%C?#1 z_g@hIGerILSU>=P>z{gM|DS91A4cT@PEIB^hSop!uhMo#2G;+tQSpDO_6nOnPWSLU zS;a9m^DFMXR4?*X=}d7l;nXuHk&0|m`NQn%d?8|Ab3A9l9Jh5s120ibWBdB z$5YwsK3;wvp!Kn@)Qae{ef`0#NwlRpQ}k^r>yos_Ne1;xyKLO?4)t_G4eK~wkUS2A&@_;)K0-03XGBzU+5f+uMDxC z(s8!8!RvdC#@`~fx$r)TKdLD6fWEVdEYtV#{ncT-ZMX~eI#UeQ-+H(Z43vVn%Yj9X zLdu9>o%wnWdvzA-#d6Z~vzj-}V3FQ5;axDIZ;i(95IIU=GQ4WuU{tl-{gk!5{l4_d zvvb&uE{%!iFwpymz{wh?bKr1*qzeZb5f6e6m_ozRF&zux2mlK=v_(_s^R6b5lu?_W4W3#<$zeG~Pd)^!4tzhs}-Sx$FJP>)ZGF(hVTH|C3(U zs0PO&*h_ zNA-&qZpTP$$LtIgfiCn07}XDbK#HIXdmv8zdz4TY;ifNIH-0jy(gMSByG2EF~Th#eb_TueZC` zE?3I>UTMpKQ})=C;6p!?G)M6w^u*A57bD?2X`m3X^6;&4%i_m(uGJ3Z5h`nwxM<)H z$I5m?wN>O~8`BGnZ=y^p6;0+%_0K}Dcg|K;+fEi|qoBqvHj(M&aHGqNF48~XqhtU? z^ogwBzRlOfpAJ+Rw7IED8lRbTdBdyEK$gPUpUG}j-M42xDj_&qEAQEtbs>D#dRd7Y z<&TpSZ(quQDHiCFn&0xsrz~4`4tz!CdL8m~HxZM_agu@IrBpyeL1Ft}V$HX_ZqDPm z-f89)pjuEzGdq-PRu`b1m+qBGY{zr_>{6Ss>F|xHZlJj9dt5HD$u`1*WZe)qEIuDSR)%z+|n zatVlhQ?$w#XRS7xUrFE;Y8vMGhQS5*T{ZnY=q1P?w5g$OKJ#M&e??tAmPWHMj3xhS ziGxapy?kn@$~2%ZY;M8Bc@%$pkl%Rvj!?o%agBvpQ-Q61n9kznC4ttrRNQ4%GFR5u zyv%Yo9~yxQJWJSfj z?#HY$y=O~F|2pZs22pu|_&Ajd+D(Mt!nPUG{|1nlvP`=R#kKH zO*s$r_%ss5h1YO7k0bHJ2CXN)Yd6CHn~W!R=SqkWe=&nAZu(Q1G!xgcUilM@YVei@2@a`8he z9@pM`)VB*=e7-MWgLlXlc)t;fF&-AwM{E-EX}pViFn0I0CNw2bNEnN2dj!^4(^zS3 zobUm1uQnpqk_4q{pl*n06=TfK_C>UgurKFjRXsK_LEn};=79`TB12tv6KzwSu*-C8 z;=~ohDLZylHQ|Mpx-?yql>|e=vI1Z!epyUpAcDCp4T|*RV&X`Q$0ogNwy6mFALo^@ z9=&(9txO8V@E!@6^(W0{*~CT>+-MA~vnJULBxCTUW>X5>r7*eXYUT0B6+w@lzw%n> z_VjJ<2qf|(d6jYq2(x$(ZDf!yVkfnbvNmb5c|hhZ^2TV_LBz`9w!e_V*W_(MiA7|= z&EeIIkw*+$Xd!)j8<@_<}A5;~A_>3JT*kX^@}cDoLd>Qj<`Se^wdUa(j0dp+Tl8EptwBm{9OGsdFEq zM`!pjf(Lm(`$e3FLOjqA5LnN5o!}z{ zNf}rJuZh@yUtq&ErjHeGzX4(!luV!jB&;FAP|!R_QHYw#^Z1LwTePAKJ6X&IDNO#; z)#I@Xnnzyij~C@UH~X51JCgQeF0&hTXnuoElz#m{heZRexWc0k4<>0+ClX7%0 zEBqCCld1tD9Zwkr4{?Nor19#E5-YKfB8d?qgR82-Ow2^AuNevly2*tHA|sK!ybYkX zm-sLQH72P&{vEAW6+z~O5d0qd=xW~rua~5a?ymYFSD@8&gV)E5@RNNBAj^C99+Z5Z zR@Pq55mbCQbz+Mn$d_CMW<-+?TU960agEk1J<>d>0K=pF19yN))a~4>m^G&tc*xR+yMD*S=yip-q=H zIlredHpsJV8H(32@Zxc@bX6a21dUV95Th--8pE6C&3F>pk=yv$yd6@Haw;$v4+Fcb zRwn{Qo@0`7aPa2LQOP}j9v>sjOo5Kqvn|`FLizX zB+@-u4Lw|jsvz{p^>n8Vo8H2peIqJJnMN}A)q6%$Tmig7eu^}K2 zrh$X?T|ZMsoh{6pdw1G$_T<`Ds-G=jc;qcGdK4{?dN2-XxjDNbb(7pk|3JUVCU4y; z)?LXR>f+AAu)JEiti_Zy#z5{RgsC}R(@jl%9YZ>zu~hKQ*AxbvhC378-I@{~#%Y`Z zy=a=9YpewPIC+gkEUUwtUL7|RU7=!^Aa}Mk^6uxOgRGA#JXjWLsjFUnix|Mau{hDT z7mn*z1m5g`vP(#tjT0Zy4eAY(br&!RiiXE=ZI!{sE1#^#%x^Z7t1U)b<;%Y}Q9=5v z;wpDCEZ@OE36TWT=|gxigT@VaW9BvHS05;_P(#s z8zI4XFQys}q)<`tkX$WnSarn{3e!s}4(J!=Yf>+Y>cP3f;vr63f2{|S^`_pWc)^5_!R z*(x-fuBxL51@xe!lnDBKi}Br$c$BMZ3%f2Sa6kLabiBS{pq*yj;q|k(86x`PiC{p6 z_bxCW{>Q2BA8~Ggz&0jkrcU+-$ANBsOop*ms>34K9lNYil@}jC;?cYP(m^P}nR6FV zk(M%48Z&%2Rx$A&FhOEirEhY0(dn;-k(qkTU)sFQ`+-ih+s@A8g?r8Pw+}2;35WYf zi}VO`jS`p(tc)$X$a>-#WXoW!phhatC*$}|rk>|wUU71eUJG^$c6_jwX?iSHM@6__ zvV|6%U*$sSXJu9SX?2%M^kK|}a2QJ8AhF{fuXrHZxXsI~O zGKX45!K7p*MCPEQ=gp?eu&#AW*pR{lhQR##P_*{c_DjMGL|3T3-bSJ(o$|M{ytU}> zAV>wq*uE*qFo9KvnA^@juy{x<-u*#2NvkV={Ly}ysKYB-k`K3@K#^S1Bb$8Y#0L0# z`6IkSG&|Z$ODy|VLS+y5pFJx&8tvPmMd8c9FhCyiU8~k6FwkakUd^(_ml8`rnl>JS zZV){9G*)xBqPz^LDqRwyS6w86#D^~xP4($150M)SOZRe9sn=>V#aG0Iy(_^YcPpIz8QYM-#s+n% z@Jd?xQq?Xk6=<3xSY7XYP$$yd&Spu{A#uafiIfy8gRC`o0nk{ezEDjb=q_qRAlR1d zFq^*9Gn)yTG4b}R{!+3hWQ+u3GT~8nwl2S1lpw`s0X_qpxv)g+JIkVKl${sYf_nV~B>Em>M;RlqGb5WVil(89 zs=ld@|#;dq1*vQGz=7--Br-|l) zZ%Xh@v8>B7P?~}?Cg$q9_={59l%m~O&*a6TKsCMAzG&vD>k2WDzJ6!tc!V)+oxF;h zJH;apM=wO?r_+*#;ulohuP=E>^zon}a$NnlcQ{1$SO*i=jnGVcQa^>QOILc)e6;eNTI>os=eaJ{*^DE+~jc zS}TYeOykDmJ=6O%>m`i*>&pO_S;qMySJIyP=}4E&J%#1zju$RpVAkZbEl+p%?ZP^C z*$$2b4t%a(e+%>a>d_f_<JjxI#J1x;=hPd1zFPx=6T$;;X1TD*2(edZ3f46zaAoW>L53vS_J*N8TMB|n+;LD| zC=GkQPpyDY#Am4l49chDv*gojhRj_?63&&8#doW`INATAo(qY#{q}%nf@eTIXmtU< zdB<7YWfyCmBs|c)cK>1)v&M#!yNj#4d$~pVfDWQc_ke1?fw{T1Nce_b`v|Vp5ig(H zJvRD^+ps46^hLX;=e2!2e;w9y1D@!D$c@Jc&%%%IL=+xzw55&2?darw=9g~>P z9>?Kdc$r?6c$m%x2S$sdpPl>GQZ{rC9mPS63*qjCVa?OIBj!fW zm|g?>CVfGXNjOfcyqImXR_(tXS(F{FcoNzKvG5R$IgGaxC@)i(e+$ME}vPVIhd|mx2IIE+f zM?9opQHIVgBWu)^A|RzXw!^??S!x)SZOwZaJkGjc<_}2l^eSBm!eAJG9T>EC6I_sy z?bxzDIAn&K5*mX)$RQzDA?s)-no-XF(g*yl4%+GBf`##bDXJ==AQk*xmnatI;SsLp zP9XTHq5mmS=iWu~9ES>b%Q=1aMa|ya^vj$@qz9S!ih{T8_PD%Sf_QrNKwgrXw9ldm zHRVR98*{C?_XNpJn{abA!oix_mowRMu^2lV-LPi;0+?-F(>^5#OHX-fPED zCu^l7u3E%STI}c4{J2!)9SUlGP_@!d?5W^QJXOI-Ea`hFMKjR7TluLvzC-ozCPn1`Tpy z!vlv@_Z58ILX6>nDjTp-1LlFMx~-%GA`aJvG$?8*Ihn;mH37eK**rmOEwqegf-Ccx zrIX4;{c~RK>XuTXxYo5kMiWMy)!IC{*DHG@E$hx?RwP@+wuad(P1{@%tRkyJRqD)3 zMHHHZ4boqDn>-=DgR5VlhQTpfVy182Gk;A_S8A1-;U1RR>+$62>(MUx@Nox$vTjHq z%QR=j!6Gdyb5wu7y(YUktwMuW5<@jl?m4cv4BODiT5o8qVdC0MBqGr@-YBIwnpZAY znX9(_uQjP}JJ=!~Ve9#5I~rUnN|P_3D$LqZcvBnywYhjlMSFHm`;u9GPla{5QD7(7*6Tb3Svr8;(nuAd81q$*uq6HC_&~je*Ca7hP4sJp0av{M8480wF zxASi7Qv+~@2U%Nu1Ud;s-G4CTVWIPyx!sg&8ZG0Wq zG_}i3C(6_1>q3w!EH7$Kwq8uBp2F2N7}l65mk1p*9v0&+;th=_E-W)E;w}P(j⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3fa8f86..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index 1aa94a4..0000000 --- a/gradlew +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 93e3f59..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d17ad8982355ae5516129c6b07841609641eb492 GIT binary patch literal 27321 zcmaI7Q*bUmyoOt?+IG8Y+qP}nwr$(CZQE|Y+P2-U;{5mQIWyh{4%nZPFPLhVMO^E`8YC(ERa*>)|iiSdhb_phYp+~1j z2S`C00`f&P443=ArTG8r3je&8(}rm z#AQ%yvL!?^iz@`N;KL7Sg%;$13Gh27YnrSX7B%&kabaH7TP>l%jC?Z1xB<@9vPzX1 zmHk7ioj?0y>?Szc#7AXYEW)P*{3baw+m?%t8pd8O!W2EU>%CNnTbtN1|18UxJtJkE zD^e^)Sk^HjLk|-xnC~d&d(P|0Y1%7=K2IUs$AS+uD3Ie^rF>Im#{oKJMYj{32YC_& z-E#%;F}>0#{)WhfUUWY)^Rr?Y~BRhtbn@!$?}aLR^W zrmc$>2I|5TVc#@mr0BG*7pPUTOhR4Kus&!JlHz4YQfw@+X5Azx9T8EZgCHv{IvNIl z)U*#Rb@4AQ1|ey%YzU*ZECH1*(9U5)jUEN%M z&MTjmILdnR7D{ZyOKlgiG78H9J}r9Gi;KGLjjd#PoC}%gxu(kGIq~5bR8oWLe#1xX5_%oI1p^BtsIn<^=0lAKO++k+Xo)0ec#J zK$l5xzE{OsQG+bfB`9WX(P5&5yv&gytwl(l-+nbW=gRY71vXdXz4dY#W?)=#8f(qD zj_%e{XIWcqZCxo;&QYO1O9yF(vKu;r*s#VuJl9%LF56Af6VowEF@eG^PK3KHT#&<4 zZ--7FE21r&-5o+$cM#V06|B7r_zJ23rM>|mvtn8YUbs`|^PlCcNfK69EKk$~59Xkpl87wO?v=`jDU6h?k)uRG$ zdjstn1^5-Qj}ALlr&mYVQzTTesb<1~8Vb`H>B zz#n%qNVY_re}hKDfM`>Pu1hcTU$b`FW~JwqhgzFJppeW|0j zw!EserEptQU0PRI*VbCMk`Gr1ACr*>mWE5U3tUp|4pz*xc$j(Ct&j2P`|K(aQV!yE zW|)m%$g2S6z-7fgO2qs9P|$$6Y^h{P`k~atZw5F@3CM~P$4%wzGtm&`FaYpfWRw5M z2yDYe!w+Pbx`i)Ags~Z_@k-h@9hZ||P)5UVKEr*QB{eGtmr%3pVUDYKyuvdpCE4=g zV8>alUTf*DKDNm2mE8@fU&6nF9SwfYy4L08CAju+UeS7?uS-&wIfE!xlrorz$^0^> z5XawbKJ$73;{ZotgBChi{RQ%oiBuP#x_6<*{Q36`8trERkbY(Yq#SU~*+4%-)8bfX z56IWrO8%H-anU&NmW_$?haqKYd-mk(mF8pe8ISx zcf5g{Sa7EczQRiHvvEkW_z9l*L~l9^%)iDV)%~hWr{f z;_KhTnPsPfX>(?Ke;R9-j2Gdahb?X9&9Wt_EI25A9wSwW%X1~@qbp{Q3tZb%%2TUSC zvM*}z%kM{`V{Gt5l9Lw;wd1Hj%~)d6$*w=lW|F0v2uZcCyn8|uSU;#_M|)~1)x%zd zQ5p=djRj4EI`|tMmeoTDF}j7#GGh&{NZ6F|9+Yp_!PJVWP8iErgpRVV(ZiS9jj+HDk1 zX=bB=CSLvsJQ~N=?$E5vl5%?S4+KOBsQ90uoj{8>r}7v_PU|f-H=?GTWrmXGtx7SC ze=dTWFw0zQ+&=T1SC5^sYg#>Xr*2T}VrJmp0%{a$Yg-t5q~BaNdh<92*TsXo6LAG3 z=$Fmsw(oy@%4W?YaTv8wT?lcZ!n;6ti=b%48dxcL%S!oi5njLnx0LQnwIGGkJ%1!9 zofl4Li~7C;TIU>9OT4OQ|K{!_m`o>dD0|s7cZ#Dco`> zGIkUQuHG->{Al6MDzM?Nam638y;*A?J>-vo-am8}?3Dc|$hXJ;MFD4_&mrR5JB2Zc zbtrJLi=Y?MrrA6>k`E9!K_>7==2Ko?;b0`t55tde@%8ybv^*t6;1BJAf6e>(ZiVP? z&9jS9U00Qq)|uKHh&Sk9O43y-p5TjmUVe(mC7gTH6^bY_CYhMuSu z7}Gy}+WqmA)D{(TRG@X;vR@}?Sy!UnsQ1b-HU7rr*a-mIL2O7gZ**@?LX{Q#)65Djb6)<5nT?+s$Z7Cgpx?vPVDiog zfE5^PaPcQw|M*ULjxco5Lx$+hFe&Nk&m)4ZpCnp{(?N_fL7^aI=$>&SFY*aj9qfWwJ^|!NoNR?}Q6<&ucs2aJ^ z6L8wj(k0xu>QF%6#)|y*ke|6q{&zesUmGiO6p=Cv^g-8G5Kd17>RRJOl}MG*-RGL3 z%y;XwA9_i=tFT90wcq$@kCdJn3e}bMB}%Zddh}Q3)RreigX~iYb)4U#CVnp*R{bFR z*Y?T(9aJ&`M5RO9sF>y_o4Z3y%!h)Q&!~!{>bldMBtvVkkQQ-`uh;P5)>q0Kgi04HVCa@EPCTku(15suSmRm@a`aX#YWTZ18}aacnZ!Ws$yC&y0skC+1T;x2 zRWq(}1@$28t3;qC_H{rKnLkmrQ5K1n77qb7b-v^-hqt}hY~8BN#F~tCwROdJcB_JAti#2NUo8^-4n46)X66r^9PLat?5vW-hqm0WjQqoJBW?!2x_Y=kh7b`Y(Q zR`8+eu@#4|Qznw~6n?&B1K41c*DA3Vy_$4W71E_!&TMf1`Yz10sZQBOwR(T(^z|*! zOmIz8k6f18MwNWm%C0?(Mw@R%ts1pT418EO@pivze=cmIQ_wbwfvUPxrF^4GkjB8y z!JEH@iHCT_q}nWZ(T#~LjVsqu?X)b5sPF%1jVAYPAc?JB0ZaP>cFIUu!30K_#MUk$f&I-7;t%^Aa@x#PsnwR|Q^ufd@cK@Y{ zxi*zSwQLnNb6B-|I4dmHD$x2JHAho?#q7mVG-`4^O5~nZbwb*^gnv(fPn^lydX@SJ zmpBZARmOLfyluP6vKk|r{O3}gbNm2;;-}LaC>bFx&l)WwEf$%V6(+RcHUm_CXzcdG z`k=N|c&z!#>T+@bSv##nP3x1+YN!~60|XeL~wvi1$$1&=4>>1k&o;EM>1*oyK!dB{;%}Y7{ye8MmkR;% zRQ(gBq?NkM%g1uH!5egLORXAz>&pDH^`X9-^w=rG_}LZhOEj!dZKT*f@<>u#^=Id2 zs%+Y)>uU0|>(Y<5_P1OwJUBvVJGkN7Wj1f;4-JF0$pV4Ri}*{JNF%aRByqASkXK*n zk4Yu})+PPrO$@`>4KPS94x}JJ^aa?tx8TLT%}ZR#$hau3>>-M{+JF5rKdBzGc=A#f zE&|~(uXZ<``JZB3>|4u8rT^9?|K`t|-4(njYs2s6rSMB)a_<7u7Dj}-92@~6?Zj|AAnB}oR9#W?bbZD?Gl0BE$(|FilFf6aoqVWcs zVe{iazB3UaL-*^M!j_ps2&|7VTP$K5;}XUx)1+KGqFy-p1q1O4ZQM%71jw_tc#-I7 z;$GhdfkhS^5^+y$>&I9|u|QMQ%`pw*ZNmFZgE1Hn_C$)dT1LdMtB5cSN$_{H!htjg z5_AOD$+RFtnZq5j79lEvG}g=vU>eRsno;ITjosDtnu|R z)FTbVbBck%#g)4l`CP<^LOniB7r`x1;cgv$w0LG2D z__<6BuIVTktlx?4`_bHsysgcm@dkXvlD(EpkFZ&}$d*Ndj8B^`tG3F%Txy?l&9J&Yb|D56=EiBk zYx6^V1(%OsJj=FSC)GtUN+);P)56>}S7EsVuH$o2~U zxpXmNSOC|rlxf~5cd*WQ@S6hlgf(=ndFT9YnS>NZZz>Xp z>rfJBQsr6SAid1?%v;Y`ZN0W)b}tQ#>03O%9=LC3OM_Wkv4adt43GDRKeHUO+vbSp z$S30@1K00AN-@TxuGCz*(42crB*0GB5*r;8Jy>6TId`t3=x^n#ZEY^>?5!KUouESp z)s8DTOKy9l1Ujfyn0%m(w%tk8LW=5+f}k8GmyuHF#o;=JhA8OU?`B=Xe$LGI=jJaX zS`Xrpm51@zM^lou?Ge{x|Atdc`+rY$Q6+o6BdMul_qn5{xyI98&JNLTM2&&fsj(L7xfE8`hif=m_zl^!ZiY z9Pot`#ZHX1!uq(va*^^QS%KQ;`SRwr3iY5h747;ZGroz|^KTMJx{9r{x3aMF4Rpix z<0hN0z1dPi*K!V>ePjyj3L8-Z{#CD8Mjbxo%Jk~$QD;?~YwEnmvSVz{LZy>6VH+CS z4Q;FSYJl`w%W`cE|FAhp&auhk5ENVPZ6SUA%35oYbQALHpzgqax2?=*FkP?d#%UuK zakC3%x|L0(Heg$8Z*{u}Lm{QAp1ZiVZn9IRydCHQe-oU`cX*@s%n8?Luu!>tuoIJ4 zh<0(wNx7nQeMgJ7d2ksk zy-e5K!-jp?jiM%(YCf{=B#MH?pK4Y7&hgtOI<$G=hmg^=Q$QIC!64$ixTR{Cw_|# z*VoVXZiPhn(xqO7NCW4-0i8F(MjoFAK)YDWt_e3qwvv(bMAx*dT%JHfp6|_$9M;`V zOv!hqHoM18YPSRvHT0F9$B@-!#?H)Se9eP2ts^o|Hu<6bxCtzmZcYbk`)3?47mn!G zPM(;Do$A>PTr7t0x^^6nhxG-=db#9uTF^HPId?s%=(6hPECAFjuTS?<&cb}3p1y=I zprSv8g>@E~$<;7s(n&afx`1AmWrK$f_mUa5N+TEpDel8EqgK#QzPoXBI?2#~=G^R! zTXHu!vOw<|&KKQH^eI3va5f37Ri0B|+U=Cz1CxI^WH=WXl6CtaVQ&8hqG{cff&BQT zP{VAwY_Qa`?9N$4Q9RdrZKwTXX3ykziG=*CF>or#PO8kF-n3mL;$O@U-4VK2h~;xX ztyuP{e1%Pyu5X-q)Qb_103l0Y!AoY(VFa1<$j6uA8T`!)9Y?tliiiW=3~5p z?wv`yxa^>bfFz&ZH&kvr=XZ0^^HHpv*nJ+Z-@Gspq`#2XK85uF%9|j-=ET0ai-rJj zm})t^XDbiGu&?4yqi!$97d1pE`pTtjcTOf|%6!2=7rrRAXK&KXBq5n*CEI2hqNen7 zr@~5HUKLsfVRyRc&8fM(h6`_Mm`oql+fXF6OceJ5MqJfOp3jx( z7vPAZb9*OeHXOxM>yu%4gLE{uZ|80mmOJ;4B7%xjpLq8A>Qq)8m+mX2CCiGoFA#r5 z3=kY~`r>1!SlK^knP;16 zngL}nv&{!QrRs;ckDV}1ey?pz`$jKp_HlzZ=RmrD1k-fcM2NON|219PbOas_nqK&Y z9Eo6zJPOFmTT|fIJY)*|tXIvj0AKY4P^!0rx0`HdGO>4KKqm968|6c_8abA#G4Va& zdK{@%gy`nA@GR{C@bd8J)2?_&CCxiM*ydj|4`Q-qGsH~~8VuTwt{t$v+ zgH1QZ-T9t+u46L4AAt0G7J4R{>e*!HGtdXF!wI?up9(L~G;mJBee~`w!TkPu9-ZUl zQX=rbI()HgMgUQ#e?-ns+dAiVy0Pp9M9E8zsblMlUaBfE46{bL2v%{xvwRiYmDR*(9FokYbnC*yxz ze^H*h1^jyI9X-P?2fUNM-qri-UH1?=?lS)MZTs%k?};WIjdg>ewoim-<_TV~Dda!c zQSC^JT>wvAQA`jNAbD5bJ7UK_mc;eC%p9?lrVx}{Qp}5Zt{$;)e`~5i8>vni>s??b z=iT$l!Qd}?d!Wy2ri7ol%qR&PTQWK{gqSTURTU{$WLZ75#;7eoA8|f6<)&iJc?F_lFk;fo83T?5#zi8J<4vYPyM4T}~GD)evt~Ou$cFJ1kY3JOb>lsF~guX~FRZiOdX_ zL|uWfy$7BGXo>`Q!dr{TpzdRqNH5CFHwCgTbbW3`{W0sJ`2|nsR$U6jrceqj;aV{S zpul8WUt`o`)mr5`$wJGllp?xEgwV|*G5G~^41QF{e_bI1WTV%Mc%nBSQs!|UnHfHt zQN?KYl^ML+f)}@74O}Rg)N#Qxg&Ejsvf@T-YAIP#gN7lC*nuHA(2LlU>w*pZveW=C z=kCN_eqpOc?&Lc;Lh~|I?u*T)FS z?i}Zx#IZ?c5Tv4JgSmg)$mS@ovKw-=9}5l*-V z5!@ApE*!i(d&YEP6!!_(x*+6=>KD;{V%wI6pM!W|CJg*Oa}@AGJMn-+db1*F5eA@K zh#axXhP0s~)w%aO0U6dkIPXB$ltMa-M=`i}giMxS@rGy$oc;xx7s?P;q4ZZH)r~E; z`v_o)T#Y*65V0O53bk0nFEB4S&a(!y%HI>FNB^P_tZ(5C%}*ygSBLt{UB)e7=Yn6* z#)FUjg~h%rZJ&Ny+RKunhUO6b9k}v6TRG~nbI|*B(<2}`AC3Y;`C)bgi2N<`SzqB4 zHB*!e@)a#3Q>GQQfH)KSqA!SkaGOwpgOhXtvi1uaTTFH!gfBW{w?OknA^AkFEiS*1 z`DAkixLe@xf@01KCE}J?pL5D4DH#-`sp0f9_*J%Ewo;bpUlHwyr(^Xq`N3V6DD)D? zUYgT*f#&CUOQck{qq}wuzU9M))*QD_)m`#~qNYoZTd(|fFQyh(Bj@zzi$k~jbF63d z1iy;pe5%w|Am4Nqm{9M_7dOPm-%)v>iA72jnAHe6 zkKvxkf<6)q!X+LYhr}uqKJ-u%|InkWoTH_llD*FX)%lA&p}L}$sQQUXD{(~cIzwLXDU&jL%%asXwBZ7YJ)R_z~lRPuFez z826n(gg#JHFO20E64M8b*bh4Tf%zS%Ua-m^UVa~=CCM)o%TE~ilUC!zb>_v};#42_ zm4FeDopEJ5^NN1_V=M4WZ03#f)EkSn7nJi0C-sXbTNsNtz$_TMhusgVpCea}S&!2n z-p?G~hV9Sr1KE`?RE}ehX>X;7BcbR-C^`vzL`!=?d;xqUKb9}}faMasX!sQ>GaE6V z{QVbAo96%#mnGkVvEPw$ zcd+8^DXYA}Y*OeXIXWEsj;mYtL0Vsl3wuq?U%|+cdTIcbdQXd9f;vtT`{BO$iO=%= z@BE3LBK}RV&|R5H4HCK!&R*R>B{0Z;F-7xQL0Z`mKa&7pBbfG}ZgYXiMX^EndQ!le z4_lriVk8%4lLsw&Qn8cKz0*AX>PStzpfM4vRf_%Z9rjP`D!e(zAP$Z@AfgO(dqc4? z9TcLqS|@K`wysVq4_OzWqu6)f%FbBIrRjw9ii_Y9Jt9AI506p0E0(P)lvwTAdh&U|c+c&a(e9Fkbf_B zQ@LqbZwkaI8S(?Kuozpmb-+E8cc9oCF>m2QX+6RPox*u+o;L2mId4caUnoS^93Rzx zb3l49scd|Lj5#-@>5lPg@y!0-EzdFKk25?;zT{AUcP<>AVRT2UIYmx}c8`T}Plsk- z4fQUBZY3&DU?9Nw20?t*pt}6z7w9w5JWp;EOyB$x?;kG_@0(b$vk9cg-$R- z)P6d_VJtXLg@y78598m($TLpL9O)y&PhZA!OqWt_!}3pG6VD(ADZ0g6p&`S~wu!`w>TmyZ?(!SGbJ=oo3T5bWcKve?Vr({^8Q>|D6D143+a zx1o@I&TdrlseB3(?j-LqvZ?a4EU;mm$4+9H?@E`V966t=oJV!;-1HE(1&bNX+!Sy5 zey~Muns9f0&(Q*t_Idwd#%U;z^6kz$Xn8+4CS2frwQ%hId*_CAKj;+l;~U)T&c!ad zgs@AUq1hc$hH;J!UFERWx$RX^OJSbNg7Cgeo_v|k?WvhKle05?B)H>y&QvnTp~rZP zNBO!0`js*AEWcIH>+(EA;oPK@Ne;Y*#-2=Ni9B0k$rC5#A;`J_)0%>@B+ZA6bwO^u zpNtqwQ^2J9AmaY5tq-#7DAq2pYam&XK0gedjZ9Sni5Zlq76m4cHQU7A(6W z=Qne$y_i&JAPOzk@pFM4tq$J!hK(PHbGpe{jTezV)+hb>g#-T}FiO}9-P4)41IBY#@Jpn; z{pv^bFoOBD#gKnJdX8%S9pv3F)~jKI#gAKXS>NTvU8&|B1kTs8P3_k5$*fV&mUhlr zS+dBoq5?R4Q0BiAXVu}+WGH-8)^(qbo>Lu9P}3d@OMZD;whaq6_bktNN@h*gdxr-p z<-jd_hC`6@hs%?$2=(5j#+sQEQc8xE5w+W|dGO`tr5 z#i%eeQWcZlD~(XBt()7$WmnH3=e;3Zw{DLZCmB$xMLTRVfe+A*4hl+Wl~Gic6Tk~_ zOz7BstWZ6u-s8XIcj`}wA)L;~=v5z}R)HhxqNf2>k`<+*oe}xVkHD==g=)gNATd@p z>1Zr%(m5uU9qtN-mWi3*Sqil0L@y}P(M~y^RONc$CqwZ*{l@iL|2nkPKQpw*1=h%y zHKA5?P55d~%t&Gb!8zcSR07`wM|kckSak*f9~>!od3|U%Bk6+V{!wH1Cn^K#3$T4j zaYxpbsJ9}2^u<%ZEV&Zv%acFEst&R%((KE)`a;?6Yf+EqmFV}x9Q#ljcXVB-_ds=h z>G6)~7n^+udPm*>_MYs$gZgFdAJxzB`~tuG;pd=!8Q(n+)t<0d8`I+tM?BdG_dI?< z)*qC!{OOC34)%qwtXSO)I%+i~KDy-=plF$#TyMxB*LO_Le*@>2XjCCctqsSYWb@B` z52(^%esuF7KTacT3NDSur$N{kP`8{tBG&ud2Jo6`ZL#_ zwGnbz6~k{%Ag9`v-drkw`qH+^aMg>oF~XR$(Wl4c<7SIKw4Cnqk=i*%$2@+JGBY~>zz*YzxfvXmeJ{)7MI z`2F_&pV%W|mOpe8G^G_vST03Vg`-nN%Bk_#s`#yx{!@+P(8?Sd`9p(V5D@Fe(VjV+ zFy5T0(6bw6k#hTISgpo_`|=j@I@mIf#7NLk{Xs zed4B3rOg6+yE?rd3I{%;XBoT1LH8QLfZRQ_jy4 z!x#$FFEm@=wIbs!iz~LT(tP%1#i>(;I;-HkRyNJ4r}6~ZO}@`7j`A$!%FABSX_(~U zlb$`Jy#$M^_|qdydn~;i=@ZKpX|F*3WLqWn^x6xFup03?Y2o^yG2#jI5`n6NxEYgRO4_)yv+Dvf{umsi~HvE?hm&ULkuqjiq7 z(v?oOwpm7w@8yfg+)M2V70hoA*3`uE#s%%fS3=o!&YnNkVYYY<=F+^|G^Q~|GELJO z>s-|q>%MEX_>_pT%`h$Tehkky(w_DBDNX!=++wPR!P^T=nBZClJLHltC($(x0S>i z15~WrEF{XK(K6$>rZ^MNvf#MbB){7GK38mjA(?N6`pv#8Q4XKC)JnG2I(C}To!}-( zwh*dXe=JVcI!W45mZzN)Ha4 z*EJ7v^p(8BO$^nz$iyM9=HW&llucthkTY_Uah3^OL6@)9*w+X zfMgwNPHN(Q1IryY z`zqJ_R=a+I)ttd=K4mnHnYWbL zkMS2`gi+BoLdQ-(S&9}qoa!A{io^QmTgN-f7T#IPJXAD8$IQj#2^j2rRAlK{>;j{F zQ|f3~_^SEq=veI52=wV#_{#a)FtFV(;-z6?=da@F(Xii0V(9s-bO>h#@?gZtmHskt z2g`+VxD>gtkq=|-4zA8d|HGR!XsZ5q~*b9xil+7YsWer}ZJ3x01G<^TQO zF3a!z#9Exc`NRUu`+f7M^SfuB8HdSdrDv&k=&K(}Yt^yUlb)sRy&d%Fhi^PYDE|*lM)-d$85w&+6H}-E zHD+RytnHBnQN#8qQQM}MltPrOs%u|{X6x1v%&d$5J^-v@YFQTLLAi&rZiK=_zqS1+ zV}BH-!rk(@g_ufSb@$-lF>9jmqIhoIXNLv4{MNke$LBQ}1!D|VwXt2oO5>v7 zJQe_kgxOFgi<}oZkNxw@WTxyB(M^S+wQLvr-UB!8* z+lCWvVoK2b9D5j4s@{60lrob8TvM>4b&y%U{D}HNSo$ZK!HzgpGu2gNvSG1P{5{wm zX8T-im0R1670IyGsT-?eO*r6gj>%3^i>~2;3G?CgoyU3PJ!Ay;ZRD89sh8YgCe3#X zW}|Mt4^}Qq=#LbiIqm=OZd*HCR{W44}>a$fYohF$z3G} z3y0BTRj6W#FBoa&(WeV;+f%4x(l=JoPvGFEK?P& zJW9VXGCggBSVsSKr(Kfm)RHj%Yilp$EtJKed;{C5Toc>Kh@ey6i3<`NS?OFLxk@|( zOqt52TAgoXo!20_vk|2!B@0D)x$kRa;la?_f?`$}muGmUQK$i1x|v%#G0ZSpy!St8 z6rDuKWCsBPvH|4rqRXT-o}|;%+khG)y2}r`G4cr>jPz=va<2|FONH!J9C?c zgv4YWHaa0ymJLOKkOWZ%Xc$|-F$tC-VP+%~itrX~t!u4np{iD&lCFhl6R{vjx?11% zs{CfHx9!Ece|2;Aw(g$)Y@6HtmImtm_a@%yj(5v@&U5#LW|+D>E|;*VbCZuAYRSb3 z>$r@jEXei;?`Kl?XCZBJdaL~pmywL7b5Nmy4SuqkUgMZF{?zKEwE{erj4<)wY~W<0 zLkBfiC*wMr1|E!?Xr@yem=V(kZcNa?<)fJ>E2f(3C0oX%-f}K3lnvB%R?iSY^dF)^ zL8(=Bjwq51uH46rFgOMNaA=DNxlnMlMTN4S<2D&DnjS*r2mLt}Jv-OS3};=@FrHkg zj4%WqpW+-XcD%+shaj)|&cVSub&y1V@$%wyK1x z?AoGWoZz9@xNPXTDikpZJD6$SD$*yXU^f67Vse}Vg3JsjB^f8DY1T(IY*$vD;iDUP7f54(Wo1}O zxf*Q<=tU~ZSBT(RHI$qE9jsW<2BBlHAp4RmL-f)2q6J}*Svv*hOS}6YLjE4Ta_tN$ zlwv)^`cksn((KZM=LK8v9Xg2oExcW%2Dnn1PC!AoNu+q%1Tb0>nfOtmJeEs6u5TZw zS9faoZ7>!>n3tFF0LA{DoMCD>z5zDUZ7iJ1p!C0UW-i~=0*H&kj!_6)S*WA{Ec%%R z-9E#@<(&D&Ggq&Xg5LuAfwB>88h4kVE^gumGeqeYORFl4VgN?&ays|%y<*t|bYat+ z-sEZ1x|#$DCUyUC_Hk5UdbdUh`H%aWj% z5^}HD&{s-I^{`AbveN+&(O-2rXsJH`dV~n;Fe@~ zAxUf4`WpjKaSReUnPtF&sa;1hEnN?AcloRn{FU94#tE*8VeI(t|JMLAJ>C4$MEI!y zk0jGpS&CenNl}99d!T?|Dc(Tv3r}e^lMXWhrS01haeEu*GND%{tq{tl{rQK#F#qy7 zOpw1gpZp`JWUkO9+tB3%h&TnfKSnD02mzHc z*%A|Ja!J+iPJ#adKO@El7yS0jjFuJ_A-CwH@9cQ$}ZJN5E7Mc!C-2-d>bP}u;vjZ!&TMr#K9t0TvRP*u{^ zZM<}1(a6xg0milWI;h(S!Tc)bWidDhMc<=fPfb+LGsMdS%+0ZG&@+cVw6~lb@iK+a zQ`lEo`7?m#(Y0w2aVCz;t0Tg4b=O@<3zansbSBMIbt~c(c&ZN48#VVov3$a`WQ_CSs~z3#(ia-f?BFbrkRqM@pGQl zeMcUAN0U>la}ZoDJholE;vl@@=b$ghSI z2eJf6swyOQnvp|~Q=ce~B*EoRDK!uZ!qu>p#f+(mG6YG&_AL%KHhx)?7FdGkk1jI! zxFn;1Y<%SwVJ(p7S^leQkh4fiT8a{6rR1!5a;POyOuvW3`o6^q9{%82+xLuliR&BQ zTfx!j<0Ng03BS%%3*)3(I1F_nd65AyhozX~28H901df3LOC!#u4Q%y{?33w5sk+eE zXyZ`QqT!%Rea?cn-!suTl594Ch)l8rt>d7hCkgiA_dyz2UdUJBoTVb(%1OvD9H%>F zolYP;T7`J^j79yLGz?$C+9?mto!0H1uL{+;dAaUl>L+Mr)TSa4UQd8yWzL+*g6mVI zyJ_kME~Kri9XofEZ2v8##)HOUnc})42{h+{&7?|_&5)5vyRM>%ybG?tJQpi%hnN2&y0~<9kg?b?QFRkB zV7FqW`1j-iEu!gNs?$rSiv?8JwGVrhoiS}^FmLiInAa+HLVYngG=*OEo$4oKaP0{{ z28JdIOty@xI4~^1Wj#vRmD7iKujFYKe2=JHH>VMO67WYiotlvYn}u$((+Ihl1jXi%21Z&uTP)9Xw!C5 z$Ho~($*}d(vceBBgZhcm>Ybb&3j}Zzc(4e>mjq;`DrHL~u6zWn@0i4o^MV$CK)0~g zNo`YNoU%+Fn!)q;lbvdMZxp8!(o)PHi>8)&?C)WmAi={H8epdIR2?^cezHoQURq+2 z!wY>j_J30{1}9j;-HBF*VSB!y{guz`vA%5mSS4#Nhjn$!FoJc=AiBJb(z9{mLr*Mm zGC9An`|F%?h@SH-CYmvZowf~a3{F?f#B}$a0u@1ZeQ*< zGj;EKGw-X}-CfnEcGc=X_U?VUe!W(&_v-Jjh;w}aO7wL-DS`K#o{aA zyyqiHx3PsEEhyRPGl}^XfAGcuYB$VRi{d9pl;wn)Cf-#{y5b~x&G+F=RbL)2)Ro`Y zG>&4dqYajclC`8ELRoxfN8dOQIXO5vxNLRmj26nnG`Mv(0$0IVrhj$@;mmyVZPk8soW2d;)(llCo2<7-S2Guo-M0c&@B%hf8M!M;+o`Bhqq|7 z0j_X;ey2ZRE*|D%m}KLEFakb-aBiXyX6wac(h8$T!qUDJv#L{M0`ls&r(>*gk97Vew3|<)Y^#*)8)%4sPBw%~*U_TKt2G&{ zt@N!`0qp%I=?+>?oMn`v^vTd(8bRWS=L+nf^Tp{CWj-kg)YL0F{b)`uj3(H|!im(- zuVnA1t-}u+HNM&HjBbHZ&7ClIsXLPesXBcEp9Y#_GlG!n3~VEO&gXS3N@wwzxo$Bq za;z&RbQ+|r+n3Q&hoyQ#@z;o)Xu?3BK|}}_NXci+sXyyA+DLiK_=;c)kXiYr#YsAk z08m0ZW@S`nU1>1r)bKMGma zWm~oZqIzW2>H_U;l3%+sf}V1EO1=B3(5|)F$##kwew#*oE^MIJYR%&q&^)H&;s90~ ztVDSh!Rz1%s_Bzy?%<=>t0x0Xzmeyvrs=5F^Q&ZVqb|KLjO(-Le&3 zDRo(MV%InMLO2kF7c#j=u>_ey7pz-5dmaNd%Y~;>HZFZI-_GNuKyZ9`e3s-?Kvhfk z-tuHf6ofSW_8P>iqN*rw*v6If{JHXKsMI?}%I=Lz zj04hjU^UNS_I!91D|3AA#x{NZ)Ag8stZmEYMx(YiSWP(@7k=#_?Wp56#1ofI>z6G@ zdWGTE<}KVfKN={8!Q3@&I#v$iJbT7*ouPZ|fVdw}`UDmT@4So~NYfqbrkwFhRG-3# zZb&3)bv1rRTclGNfh}+@)_#_dbRdTaWaxGom(h?xO?zRq`n}%f)o?p*(kH?m1!aUS zk~zOl!Hr+rqaeUimB?kAmDv!h6MKe(hD1d;lCr$;1BJ=;O63b5;bHe&6s0jkGId%A zmpH>{U*a)^vf`;=3E^Z3LH-vx^~QyH+u6>-!I7J>E8qs4U&t<&W)2^skB0Mx=TN)C39T$UfCo5#mWm-Plfa3er{Rc^Mw zmvqlj?5=&?r4%E4K{( zjyGXodvbZOa9J{v(lIX!$E&i->YAO6m9|UK_@nMG9-e&tb=2YZJqbb<#t8-Cf85PDd0aWLTv*s9t zi|-Sd7UfB7+fbzdFVK5%#f+Tvp(EuBualkn5FFRjYC)uYJDXUx0*pL9cll^zLjiN` zePW?SEPkVa>YLFX9%XiVS(%U22SmOzZR)$=(1NAd$zXsD4zX$Vn1F>NmUv1)50K`N^1r3$L!1d`31vW-{I}+d8ai2JbRfb%T{4yNi1QaEziy!8YfJ zcl&{b51qH6acpC4{fIc%#Ahfs)!qe`K;5Lk=({{a!yKOAboTX~7O&V;{+pVYYyr(& zC0lC}Mzn#-G{3*hH*-h_QOi@vUcJqT{YtH2wvvLe=b$i;qTYq?J=dpeIKbx6Cd4{+ zar%=BYxw@AFy4YEZ4g9FlTyVJP#i{n7Js`rX#Ur?pM+ay;5MH7zz>P8y;@>Nh3;}I z4E=3Xw=cDL8w4m07iV~{nqbn%d+*|5$LaoA2@3h^8w)^;8JlcL-S1#A>HIpFB6aGz zkmie_c5av^Uli$2kYMx8BMe68`A?;Z&P0lxCB=CeNA%jDuhJ{|wK(FM2p~?|bFsRz zGM#Ix>9Lz&VL&)6@)Pr;M7-s}-|$`sYxcbwlhMZ5I2wGw=QMqQ93UNiT}vxF&~sti z1$NGajAIY($3FL%OKWzS!`ln=<|*O?Bdh~&+Vm%@&#;e0z4&=N_J3Wn*| z$O^8q(W4ojw8^pla5H6awjZqBLRKe-k1A>l@EN^kHLA+6qz&t!`DNxRqrFRpW^9H;K@H)} zAWq&Hi-aAXluGmW!QM(+lOx4x;m=Q)5%YikzE0J)Nw}tzju48EAQiq!!!Pg=!0DMA zPIKvg0l<4y=$_QKxb!X+j>>6rdu!XU%e=`KP6S`S7yIDoHYvn8u$_)D6W;VDM#wpKRBlI$#7Y=}*vL|;hfKb+N zLsjLbyD6|i6#~N4j3$|D6qoIi3X8!!?e4+&KJB(3ulEhaY*U&zHSkfRHJQGPit4eL0NCn^@y$~F|B=~nHtm`V#251Cd9O2ev)+^Pc zmfg%dp(nIm!(FMCA6xDs^K+D4?dHR8HvqiLe|ReKiCq^<$9m#&Py|_*l8x~tk5~K8 zqOP#ltG$1c^oe`SdgO~DR(5+Aof)Y=tQ7Hs*Oe|h(H^y%E7!=lI8IJ4om5pD8^@+p zzq^ALnmV=->6p1ywwXv z-wR(d!g|tyw)JaU10%*-uA`G&vciCbq#L9s`X}Y51zSEx$nB$mwNsai5>3Bz5WV4I zWO^&-oa!mfBJt0i&PtQ1M$qXFdp1GqfIOulVqyPT&u^ZcO))dw zFv~FO0Z#t5CFOS&_u&{ase>vPHS7H+KrQRx>*!UBne-|MvP7_M zC^%mn){3@u`By5fxgyXWDR2Hj19DlGqV!y2L0$Il$VK{8AK|6qh=7j?+&_~#6%^zM z82SsK3o^Ndx|`Lsg6)vwJ>md~Z-_rj zx0@F_lGd|%1-JC5hAoushO#IxTyHUNpGgY$ix?rJ; z@Q&XsgB>@D8Mbn;^{FTFuvdIQ<~&TZyHe(m`pbx_n+dU(-VMt1X}M17l$%MhZf3fA zminoG2&K?weeuGvz#9er@f@jS=5wiOjW)lJB6k0&BwEGS1*VzP;|BZ6ulU4`RpYF* zy*EU1C#r>Wew!NxyQc*z&-A~?gIxvq#So8-#}8Z@alIIB#dYN4MD~e-v-lO3ghZ+f zg5xtJk2Jhxq^J3o6Y6s6Cnl*Gjy)teyS65jm}0s-XIER-41hrA zDS^=%e{KBLOk*<#e(jFR{eHPr&N=;4Mt6P@8$i_B9H1dEeV=N#$>u9-Xc-Bpoe7SgH8oE zX)vLU0UQD+qrxD+xIE<;^QJMq+GMFzvb4H&nWJj)Ol-}r4>6!*bK=VUZjK*zm~2Dy z+zA|1LnX?KNvR2t5kSNhj-hnMR`0u&3wko>Uug_-DGf#jO?u4-w-fRuxx3VTtH(%O z6&gSorKffRLQFH1Sb%HEk73ysFXzZ=KlLf}`OuMBW_H9`Wx)UT*_9Eu<05Qoyd>Oh zx$LA40+l2VJo~2;Z0FZ_>-4ve9t(hY6~B9@MQx5t-p%w`Oa{#b@CiLJVC?onE=V5M zB`W@`m}vyt*>EssMec#Q(lTO=mAHYzpTe|;&;(1R%@|!^&$O`uSA-TwEDI``M#w3? zOp9%aD^cD)*`}brfWjv=z@EKgCt-h9yh1LT#);JI=SsHHC-0=_v?Eo8GP@S|rifm1 z?ZfW+Q{lzshZEX|x%8COu5<(!N<^^A#g1!j)iIo3OA$s-52@VS5u{rCxbMMyE5Iy% z#?a*&ESZ|VJgM6ci-3(Zh$PVKK-j0aFJWPeAAe5EL{tr)Nz50VBm*_#n3U5^lPT1Bme0dnJu zU=6@-TYlzMuyIa^hP!TCOy)M+G<}G1M)}^;w(YJt=IGUu7!*=x6Bt*4lDP(j@ybdz zBa5~K-UKSUE#%-f^v?~yx)~~}!96)-re(l0o|yq_7Gj_amuv#4;FDq+ z_TYV^%NwKX&zCt;I5Rx$tA{47YC#>y;3x0oZHvgYqF^tqfrLTw(X&Xgq`qB97UU8q zm+OPPWtl!S{k&-P=k<4<2)(j2l|Rbvuo2B_(JgzBIT7t0BhI{8$q^;AJ>!%-hbWdz zp*=M0v(R%Mq}VCzti@nBlet&OJ1rZen^SaFF>If`Su+iqe8(QKTZpnY6gRq07W}=d zT90x4au2EBw5Gw!WrE&uuy1KUS3Xm`zk_x|!4_|fSRN`Kc({!*Z=~T0BOSo@xgc(I ztq3+`4sNKdi7tXW+T*aV-@0GH0XrRkc(lt}alu8e0({|MmOF6G%HW;;d9~}5{#M2v z@~^{#JgL8*yv_nGViX*($)&FZ4|7wN9*3{-t7sNgS|Tl29=JQ!G~Bk~-u$&GiaR{2 zapA+2Rf}Ry3k1`@I^xD}d4P@Q+MsaIbzP;>MnXQ1hLdm>NBk~Gd&i6*L!D)Q$B5aY zBK1}jo*;B?FO&^DFPmC0D- zaB0&*V>&_@`tGP8g(iB92=+RWH~!qvkigUO1(f*v;v&S}7uM!aVI_gzhBqC;kJbcu zl9#EKRw-ZQ<&IZl?c%m#!2>n?oh7$RG$gwkrX5lU_eNr)wEDL^xQHz^SJ5W{;M0{; zpDBone=ewBF4$IbG@xX?i@Fixq}Ad$852A}Jk?+pEidFMfqx*8j!%(#JM@}=psHUo zZB++|17AP425vx{cLD1deF1vx;@BLTH=q55RCRQKSG&fPfb#a(Fy&TST%J3nII0}^C0)u1y!cP@ z>kyit*3D!-{;!FJTA#Z{wGOUnO27~=F%o>x+TDEldc_se%4u%6GlrLt2^=z3DBNLm zV7F2^`T)Cp(zGoLL39a*)|HA*)P+X7W&t0$gp$_xMsWz1>_1U(ZFsCjIuUK?)gAVZr8cRuF2aso0HaryCNdG7puS6~ zj`Py9g2!59;PA+_3O=IMODRFMaHplwfS@Fc>$m#MJviW=4^K|a$X0VHwCI6KSz37D zGXy@}u{Ff{o(Z^o?*n^#NkO|ZeQ#@3Pi(4;GJxk~0A&%lGTA!O(|QEg`cuWR35T*} z<4NJc2Y(ozjTckbKQodcT4kl4Hk84ksJ19HRogX1yVy$$#${$GTJ^Nv3RWr`WxEfi zVT@thF8T?Pejk?A%FiWv{<++&D!eEI1Y!KkQJbT&P3KAENn5MRb(IuuLOR0^-xkN! zCd1?v0t-zRyfl>YLeikZn|B))y(i22=Lm1!2yf4&{`Brx&*hq0^aDYm?f_P!ik>xF z2aJLWpgM4WQZ-|&an__ujKWBmQP~=5PEon2$ah=g z>7+$%O$x?vl+ipyiie~SWVFhQZDOgSqj%v1)QZ`CqX3-6q8W+v2?1xTq!AYZQd_jY z(t(!ERs-8Kbzl{NS=n)03+0cEs5c#Gw8`{_xsqm25qzCOaF^m~(U9>~l1ILUOrI2| zS+8&zaN-gT2^#k*Aohp!eldFBCc9rrLfI5{zVGV&!Uk(s38XNP*Znov0HYr=l54AT zsIGlz_m#}M9sKZWKu8hNKlQeb7|ng#p*uvWa|)yP&mte{l{$ZtxX3Sv(IliT;z+DpWirPj&pn`ZkO$>EY#Rj%9GC8({RyaFLMsPpuD>E}#YT)el1xLThBf$yM}mcSE@PpFatX4wmnEwg?;2&|< z$Z|eUFM22YAo}Wsmc>5cir&c{h=65IC{#B+XJ^%BZ6RXuaS~Jsxmz)j>}vj;WBN*i zHkll5s25j{>6d4OfMIiTRIUWqD^u%RD0imc5N>0x4OzX*S%fbox0;J2O*wR#LE3&w;vpt0s?)! z!pjE?>vZ1O7OMk(ilgmPUZH9Q1$>$HPUVbxuuAkKhPj61-a@smc#LR@R)yxEZduY;0X3F-kYrf@_B21hEUWW4%LBAweI_+ zLAOVd5vOU@=atR*t=YQZYkqd0Pa}(OCT9xqEakPS4h8Z2=+l@EbnZOhaTP@VUdI`; zFFOL%fCh;7}jr+ z8^Qq8H`N2XDA!dFFm#{1t^^iKRvNPvBT(l@tRmv*!jnW(ow^>;Y&1m2YX`SeEXas1 zwq;k;9V_&Fp?0A?IfH6o-|?$9ALjjoz4pE6lK17KZV3V3xAzP+oLzTNIT+pb1Ut{N_=bRQOnXEI08oF!> z{w>+3nsOIA4j%|}8yNE%j1j%-I+T$ZKA7nz zux2a9We9m_A?U@Y`_|D^>5Bu0ekN~rO$%S9LAi>KHg-0ExXcYquGNyN19SOXW_dD07<=aMWc*y2Qj1Mq6>!`$vQmV59+ z-Zd$J`VjX+&Hqhg^Gde(%e%bb$369jv;kg5ZiL=^5b)j{?1ntqI=ocHCyLT0+&bl+ zonb!5l|XCB=4tfSvs8gjsTsIE+D_a@kdLL)*S@U7j3YIOePxJBxZh>E@@W5!l$K;p zA&``6<^mC=jBz>&jElw!^Q$C=^Ld>TeiUQ$nUB7v&cUO)llC{WfMq>5T*xDw>H?8) z3nEMd#PotMsWj^2T`@E5Q8Vo*XKyH2HXG0w8&2b$qZxjTF^e*%fr~8p#&9sXfLQWk4#Q~aty*I15XpGvnIBb;T{ z?nha^+CbKrUvjayK?&Ar?bKr$VP)m5hDaR9abpMd zNQxb=_0^H-mbVdo{mj-G&jXh_9fKvrSOrNbq#`NJr2Wf&yLThO-cIL1bCS9BcS2QX z`q1o?93hl#2-d|F5y$$XWdrJR6?%PERTXNa(^1}uM|$Q-^_%6AHl}LS%8H_mb&uC1 zU|CU{m7}b}PKAprspg9VHF7`MljBb4)nYR;o_>CZvIr20xe#qpNx+8%ey+*3sWJ}N zfjO$)$NLC!PTg&$zk2PJZrY{GmzzsSH1Q{!6r)@Sd_0XscyLOdoE+p@?BGY*I+Fv>K)`Y1d%Zl&Y=}Jk@Bql((jl6d!B)3P}`kn z+T~#ww|F3!+WM8zLC=1ADEAS>B+%yLJ`+8jUHNg&WNBE`AU)XyW)p<17(G zsIi_6;sZJx73?Eq(A;}Ow3 z)ukU_f+sV}x-S&*pca1~3nT1YdQN%9$47bb%ih@*dN($ z3$UPLHKjc&2?hR09;@6cQ%SFC=$lZ8-3K=-t*zz~ek{u=7+J_HQxsEjB4UAq7+bvU z4YNckt{dZ<)JgaAbwR=1gF1;K8P+ntY2y>C%%n_#*mV4$v%k(N#WXl+LtnL2h_Wb@ ze!91QS>-yvU^3hYp&upuN-M&iar0c#em{_E_oQ`zpoe`DcEHT_Cw))NhfO(c&xw?c zIPd=ZV}M9GMQe&6b^t#@?_u3}MvAL%?>4a=XBN(w;}A01K1(c87ZvwskrZO*)ZrI$ z&8Dv&t*&LiA=id7_%8fBQj))Z;?6j)U=IC)COMa89A+AWq{Tt*32sos`_b3$lCADM&hfLTg%_LxWNpzDNRaI$)k$?@Pc)Z0R`=c~2yIA3jc-2%xjdSa*o z5fivc$nE(crh0-fP1>8Tm1Pnrd@NpbH^xsVA2xEkzDbe?Rn{a%G4j6cg zuSocC&ByqPjkHt|f=RDuf=QDOp&BIs3M$JaaWuf5C%w4Q$u>{-bd?Xy| z***mRz@4+-8Q=Wa^P_Lw<)3+M)f^)$H7NVA86gJ&%IFrA70Z-vQ5`9!9wUl3b`F*Z z2i!o3(bk`wtP9@(pRv~*TRFd;#9jsc;0k@gIw7`TU+w=KrZ&lrSLQ9aw#m0Kb;EFh zSw57giSwhX9Xaeo!fofj)k#jDy@j{`^h_4y|I{;xe|knlPRvk4Tt!t{QGsoqZHW!~ zzfFMn0*NeO|74;697?j_5TC&Q_96PGLC{}5ME@H9$(88eQU0l)`pZn{?+E&H`afF> z{X5n_)h~Z3uKteFKZEW+Vf|nFtAB_6=P3P4I`emMa{h1F|EG}V-*NtV1OGL6{|+Iu zf5G|x9mD^Afqz!}uQ~8{U|9Ui1^(ky_;WT9dHO2Yox2>%SI Kus>-qu>S(PSAA^& literal 0 HcmV?d00001 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties new file mode 100644 index 0000000..8375666 --- /dev/null +++ b/lib/bld/bld-wrapper.properties @@ -0,0 +1,10 @@ +bld.downloadExtensionJavadoc=false +bld.downloadExtensionSources=true +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3-SNAPSHOT +bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0-SNAPSHOT +bld.extensions-pmd=com.uwyn.rife2:bld-testng:0.9.2 +bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES +bld.downloadLocation= +bld.sourceDirectories= +bld.version=1.7.5 diff --git a/release_info.txt b/release_info.txt new file mode 100644 index 0000000..f5efde2 --- /dev/null +++ b/release_info.txt @@ -0,0 +1,27 @@ +/* + * This file is automatically generated + * Do not modify! -- ALL CHANGES WILL BE ERASED! + */ + +package {{v packageName/}} + +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId + +/** + * Provides release information. + */ +object {{v className/}} { + const val PROJECT = "{{v project/}}" + const val VERSION = "{{v version/}}" + + @JvmField + val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli({{v epoch/}}L), ZoneId.systemDefault() + ) + + const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val AUTHOR = "Erik C. Thauvin" + const val AUTHOR_URL = "https://erik.thauvin.net/" +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 53bc4db..0000000 --- a/settings.gradle +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id "com.gradle.enterprise" version "3.6.3" -} - -gradleEnterprise { - buildScan { - link("GitHub", "https://github.com/ethauvin/pinboard-poster/tree/master") - if (System.getenv("CI")) { - uploadInBackground = false - publishOnFailure() - tag "CI" - } - termsOfServiceUrl = "https://gradle.com/terms-of-service" - termsOfServiceAgree = "yes" - } -} - -rootProject.name = 'mobibot' diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..85d8fce --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.organization=ethauvin-github +sonar.projectKey=ethauvin_mobibot +sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml +sonar.sources=src/main/kotlin/ +sonar.tests=src/test/kotlin/ +sonar.java.binaries=build/main,build/test +sonar.java.libraries=lib/compile/*.jar diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java new file mode 100644 index 0000000..9b6b32a --- /dev/null +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -0,0 +1,170 @@ +/* + * MobibotBuild.java + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik; + +import rife.bld.BuildCommand; +import rife.bld.Project; +import rife.bld.dependencies.Repository; +import rife.bld.extension.CompileKotlinOperation; +import rife.bld.extension.CompileKotlinOptions; +import rife.bld.extension.GeneratedVersionOperation; +import rife.bld.extension.JacocoReportOperation; +import rife.tools.FileUtils; +import rife.tools.exceptions.FileUtilsErrorException; + +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; + +import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; +import static rife.bld.dependencies.Repository.MAVEN_LOCAL; +import static rife.bld.dependencies.Scope.compile; +import static rife.bld.dependencies.Scope.test; + +public class MobibotBuild extends Project { + public MobibotBuild() { + pkg = "net.thauvin.erik.mobibot"; + name = "mobibot"; + version = version(0, 8, 0, "rc+" + + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); + + mainClass = pkg + ".Mobibot"; + + javaRelease = 17; + downloadSources = true; + autoDownloadPurge = true; + repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io")); + + var log4j = version(2, 21, 1); + scope(compile) + // PircBotX + .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) + // Commons (mostly for PircBotX) + .include(dependency("org.apache.commons", "commons-lang3", "3.13.0")) + .include(dependency("org.apache.commons", "commons-text", "1.11.0")) + .include(dependency("commons-codec", "commons-codec", "1.16.0")) + .include(dependency("commons-net", "commons-net", "3.10.0")) + // Google + .include(dependency("com.google.code.gson", "gson", "2.10.1")) + .include(dependency("com.google.guava", "guava", "32.1.3-jre")) + // Kotlin + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", version(1, 9, 20))) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) + // Logging + .include(dependency("org.slf4j", "slf4j-api", "2.0.9")) + .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) + .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) + .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) + .include(dependency("com.rometools", "rome", "2.1.0")) + .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) + .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) + .include(dependency("net.objecthunter", "exp4j", "0.4.8")) + .include(dependency("org.json", "json", "20231013")) + .include(dependency("org.jsoup", "jsoup", "1.16.2")) + // Thauvin + .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.1")) + .include(dependency("net.thauvin.erik", "jokeapi", "0.9.0")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.0")) + .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); + scope(test) + .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 27, 0))) + .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 20))) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); + + List jars = new ArrayList<>(); + compileClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); + jarOperation() + .manifestAttribute(Attributes.Name.MAIN_CLASS, mainClass()) + .manifestAttribute(Attributes.Name.CLASS_PATH, ". " + String.join(" ", jars)); + + jarSourcesOperation().sourceDirectories(new File(srcMainDirectory(), "kotlin")); + } + + public static void main(String[] args) { + new MobibotBuild().start(args); + } + + @Override + public void clean() throws Exception { + FileUtils.deleteDirectory(new File("deploy")); + super.clean(); + } + + @BuildCommand(summary = "Compiles the Kotlin project") + @Override + public void compile() throws Exception { + releaseInfo(); + new CompileKotlinOperation() + .fromProject(this) + .compileOptions( + new CompileKotlinOptions() + .jdkRelease(javaRelease) + .verbose(true) + ) + .execute(); + } + + @BuildCommand(summary = "Copies all needed files to the deploy directory") + public void deploy() throws FileUtilsErrorException { + var deploy = new File("deploy"); + var lib = new File(deploy, "lib"); + var ignore = lib.mkdirs(); + FileUtils.copyDirectory(new File("properties"), deploy); + FileUtils.copyDirectory(libCompileDirectory(), lib); + FileUtils.copy(new File(buildDistDirectory(), jarFileName()), new File(deploy, "mobibot.jar")); + } + + @BuildCommand(summary = "Generates JaCoCo Reports") + public void jacoco() throws IOException { + new JacocoReportOperation() + .fromProject(this) + .execute(); + } + + @BuildCommand(value = "release-info", summary = "Generates the ReleaseInfo class") + public void releaseInfo() { + new GeneratedVersionOperation() + .fromProject(this) + .classTemplate(new File(workDirectory(), "release-info.txt")) + .className("ReleaseInfo") + .packageName(pkg) + .directory(new File(srcMainDirectory(), "kotlin")) + .extension(".kt") + .execute(); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java deleted file mode 100644 index 4bbbd9b..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * War.java - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules; - -import net.thauvin.erik.mobibot.Utils; -import org.jetbrains.annotations.NotNull; -import org.pircbotx.hooks.types.GenericMessageEvent; - -import java.security.SecureRandom; - -import static net.thauvin.erik.mobibot.Utils.bold; - -/** - * The War module. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public final class War extends AbstractModule { - private static final String[] CLUBS = - {"🃑", "🃞", "🃝", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒"}; - private static final String[] DIAMONDS = - {"🃁", "🃎", "🃍", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂"}; - private static final String[] HEARTS = - {"🂱", "🂾", "🂽", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲"}; - // Random - private static final SecureRandom RANDOM = new SecureRandom(); - private static final String[] SPADES = - {"🂡", "🂮", "🂭", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢"}; - private static final String[][] DECK = {HEARTS, SPADES, DIAMONDS, CLUBS}; - // War command - private static final String WAR_CMD = "war"; - - /** - * The default constructor. - */ - public War() { - super(); - - commands.add(WAR_CMD); - - help.add("To play war:"); - help.add(Utils.helpFormat("%c " + WAR_CMD)); - } - - @NotNull - @Override - public String getName() { - return "War"; - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(@NotNull final String channel, @NotNull final String cmd, @NotNull final String args, - @NotNull final GenericMessageEvent event) { - int i; - int y; - - do { - i = RANDOM.nextInt(HEARTS.length); - y = RANDOM.nextInt(HEARTS.length); - - final String result; - if (i < y) { - result = bold("win"); - } else if (i > y) { - result = bold("lose"); - } else { - result = bold("tie") + ". This means " + bold("WAR"); - } - - event.respond(DECK[RANDOM.nextInt(DECK.length)][i] + " " + DECK[RANDOM.nextInt(DECK.length)][y] + - " » You " + result + '!'); - - } while (i == y); - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 2c5f05d..c6f16cb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 98ef74a..ea89f4c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index d82f011..371b523 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index f91c457..47dd7c2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -51,7 +51,6 @@ import net.thauvin.erik.mobibot.commands.links.* import net.thauvin.erik.mobibot.commands.seen.Seen import net.thauvin.erik.mobibot.commands.tell.Tell import net.thauvin.erik.mobibot.modules.* -import net.thauvin.erik.semver.Version import org.pircbotx.Configuration import org.pircbotx.PircBotX import org.pircbotx.hooks.ListenerAdapter @@ -66,7 +65,6 @@ import java.util.* import java.util.regex.Pattern import kotlin.system.exitProcess -@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { // The bot configuration. private val config: Configuration @@ -257,7 +255,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Output the version println( "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + " (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})" ) println(ReleaseInfo.WEBSITE) } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 7cb5aed..ac01b0a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt new file mode 100644 index 0000000..88634ae --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -0,0 +1,27 @@ +/* + * This file is automatically generated + * Do not modify! -- ALL CHANGES WILL BE ERASED! + */ + +package net.thauvin.erik.mobibot + +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId + +/** + * Provides release information. + */ +object ReleaseInfo { + const val PROJECT = "mobibot" + const val VERSION = "0.8.0-rc+20231110234054" + + @JvmField + val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli(1699688454937L), ZoneId.systemDefault() + ) + + const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val AUTHOR = "Erik C. Thauvin" + const val AUTHOR_URL = "https://erik.thauvin.net/" +} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index e4760d2..d69c0c5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 5f79472..e0b091a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 038e378..9084660 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 9608ca8..18a8b1d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index f271bfa..c1708a9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index d083c10..8d2b154 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index ed0b6ef..8ee8c4f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index ec7823b..f2a13ba 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index b2293b0..1aaefd7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 20a6635..0f492c9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 85a03ab..41b2c4b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 77154c7..0159017 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 7f76d35..1f89982 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 33d6fef..4a881a7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 896c569..a8300f0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -40,7 +40,7 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Versions : AbstractCommand() { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})", "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + ", JVM ${System.getProperty("java.runtime.version")}", "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 1443d44..90d8bb3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fba6b99..2d7add6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index ff4278d..dca99cf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1662857..c59cad9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 825e374..1626a58 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index cfd2c27..2a3856b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index c9ee0f3..83083fd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 7924977..1445f9f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 061ca6a..e9d18ae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index b65a4da..229bc5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index d17fbb5..cc48d59 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index e8676ec..15506e0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 9c09626..6e58fd9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index e18d692..c0cc2a8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 4a69446..8098bbf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index f786cb2..e7017ab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 8c8e736..8dcf6d8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index b7aae28..9003783 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index bd92332..09791a8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,7 +1,7 @@ /* * ChatGpt.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index d14056e..4464732 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -120,7 +120,7 @@ class CryptoPrices : AbstractModule() { } /** - * Loads the Fiat currencies.. + * Loads the Fiat currencies. */ @JvmStatic @Throws(ModuleException::class) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index da0efd8..954f4d1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 8420fb1..84f2280 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index f426d1e..a9b15a5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 2760fa7..91b9ad2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 9ab2ead..eecc55e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 3be3a5f..1141c0e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index a7416c2..9a0e641 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 944dbc1..86b311a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index a299d8d..d224568 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index dcae5e7..6b591cd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt new file mode 100644 index 0000000..0c64465 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -0,0 +1,89 @@ +/* + * War.kt + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent +import java.security.SecureRandom + +/** + * The War module. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @since 1.0 + */ +class War : AbstractModule() { + override val name = "War" + + override fun commandResponse( + channel: String, cmd: String, args: String, + event: GenericMessageEvent + ) { + var i: Int + var y: Int + do { + i = RANDOM.nextInt(HEARTS.size) + y = RANDOM.nextInt(HEARTS.size) + val result: String = if (i < y) { + "win".bold() + } else if (i > y) { + "lose".bold() + } else { + "tie".bold() + ". This means " + "WAR".bold() + } + event.respond( + DECK[RANDOM.nextInt(DECK.size)][i] + " " + DECK[RANDOM.nextInt(DECK.size)][y] + + " » You " + result + '!' + ) + } while (i == y) + } + + companion object { + private val CLUBS = arrayOf("🃑", "🃞", "🃝", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒") + private val DIAMONDS = arrayOf("🃁", "🃎", "🃍", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂") + private val HEARTS = arrayOf("🂱", "🂾", "🂽", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲") + + // Random + private val RANDOM = SecureRandom() + private val SPADES = arrayOf("🂡", "🂮", "🂭", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢") + private val DECK = arrayOf(HEARTS, SPADES, DIAMONDS, CLUBS) + + // War command + private const val WAR_CMD = "war" + } + + init { + commands.add(WAR_CMD) + help.add("To play war:") + help.add(helpFormat("%c $WAR_CMD")) + } +} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 80a06fa..83eba95 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index a72efab..8d9c4e6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 18072bc..0c379b3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 0607936..b9f5212 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 23a33b9..84e3a1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -30,8 +30,6 @@ */ package net.thauvin.erik.mobibot.msg -import net.thauvin.erik.semver.Constants - /** * The `Message` class. */ @@ -43,7 +41,7 @@ open class Message @JvmOverloads constructor( var isPrivate: Boolean = false ) { companion object { - var DEFAULT_COLOR = Constants.EMPTY + var DEFAULT_COLOR = "" } init { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 037d504..2be52aa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index b424fdf..8d34d28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 9c5e088..9e67ecb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index 91f2dd9..3fb3874 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index b594670..47a33d9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 3fd315e..e779a3c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..16644cc --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 3241bf0..7f65548 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -42,8 +42,8 @@ import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.modules.* -import org.testng.annotations.Test import java.util.* +import kotlin.test.Test class AddonsTest { private val p = Properties().apply { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt new file mode 100644 index 0000000..01d98ca --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -0,0 +1,44 @@ +/* + * DisableOnCi.kt + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import org.junit.jupiter.api.extension.ExtendWith + +/** + * Disables tests on CI annotation. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @since 1.0 + */ +@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@ExtendWith(DisableOnCiCondition::class) +annotation class DisabledOnCi diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt new file mode 100644 index 0000000..24f1ca0 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -0,0 +1,51 @@ +/* + * DisableOnCiCondition.kt + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot + +import org.junit.jupiter.api.extension.ConditionEvaluationResult +import org.junit.jupiter.api.extension.ExecutionCondition +import org.junit.jupiter.api.extension.ExtensionContext + +/** + * Disables tests on CI condition. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @since 1.0 + */ +class DisableOnCiCondition : ExecutionCondition { + override fun evaluateExecutionCondition(context: ExtensionContext): ConditionEvaluationResult { + return if (System.getenv("CI") != null) { + ConditionEvaluationResult.disabled("Test disabled on CI") + } else { + ConditionEvaluationResult.enabled("Test enabled") + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index a3994ec..b02188e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 78f5b18..cfc3d4e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,10 +37,10 @@ import assertk.assertions.* import com.rometools.rome.io.FeedException import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed import net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test import java.io.IOException import java.net.MalformedURLException import java.net.UnknownHostException +import kotlin.test.Test /** * The `FeedReader Test` class. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 1384a72..95b6c08 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -30,7 +30,6 @@ */ package net.thauvin.erik.mobibot -import org.testng.annotations.BeforeSuite import java.io.IOException import java.net.InetAddress import java.net.UnknownHostException @@ -42,8 +41,7 @@ import java.util.* * Access to `local.properties`. */ open class LocalProperties { - @BeforeSuite(alwaysRun = true) - fun loadProperties() { + init { val localPath = Paths.get("local.properties") if (Files.exists(localPath)) { try { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 87617e8..cf9d59a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.reader import net.thauvin.erik.mobibot.entries.EntryLink -import org.testng.Assert.assertFalse -import org.testng.Assert.assertTrue -import org.testng.annotations.Test import java.net.URL +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue class PinboardTest : LocalProperties() { private val pinboard = Pinboard() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 7a3f5d2..a024cff 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -59,14 +59,14 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.underline import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import org.junit.jupiter.api.BeforeEach import org.pircbotx.Colors -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test import java.io.File import java.io.IOException import java.net.URL import java.time.LocalDateTime import java.util.* +import kotlin.test.Test /** * The `Utils Test` class. @@ -78,7 +78,7 @@ class UtilsTest { private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) private val test = "This is a test." - @BeforeClass + @BeforeEach fun setUp() { cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 265009b..33a411f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.commands import assertk.assertThat import assertk.assertions.isEqualTo import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime -import org.testng.annotations.Test +import kotlin.test.Test class InfoTest { - @Test(groups = ["commands"]) + @Test fun testToUptime() { assertThat( 547800300076L.toUptime(), diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index f1fbe11..34dce0e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,10 +37,10 @@ import assertk.assertions.isEqualTo import assertk.assertions.matches import assertk.assertions.prop import assertk.assertions.size -import org.testng.annotations.Test +import kotlin.test.Test class RecapTest { - @Test(groups = ["commands"]) + @Test fun storeRecapTest() { for (i in 1..20) { Recap.storeRecap("sender$i", "test $i", false) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 8e49b5e..eea09db 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,12 +38,12 @@ import assertk.assertions.isEqualTo import assertk.assertions.isTrue import assertk.assertions.size import net.thauvin.erik.mobibot.Constants -import org.testng.annotations.Test +import kotlin.test.Test class LinksManagerTest { private val linksManager = LinksManager() - @Test(groups = ["commands", "links"]) + @Test fun fetchTitle() { assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") assertThat( @@ -52,13 +52,13 @@ class LinksManagerTest { ).isEqualTo(Constants.NO_TITLE) } - @Test(groups = ["commands", "links"]) + @Test fun testMatches() { assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue() assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue() } - @Test(groups = ["commands", "links"]) + @Test fun matchTagKeywordsTest() { linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3") val tags = mutableListOf() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index c28090d..b5028e3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,10 +36,10 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.prop import net.thauvin.erik.mobibot.entries.EntryLink -import org.testng.annotations.Test +import kotlin.test.Test class ViewTest { - @Test(groups = ["commands", "links"]) + @Test fun testParseArgs() { val view = View() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 52a21cc..331f97c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,29 +34,16 @@ package net.thauvin.erik.mobibot.commands.seen import assertk.all import assertk.assertThat import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.jupiter.api.Order import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize +import kotlin.test.Test class SeenTest { - private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") - private val seen = Seen(tmpFile.toAbsolutePath().toString()) - private val nick = "ErikT" - - @BeforeClass - fun saveTest() { - seen.add("ErikT") - assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) - } - - @AfterClass(alwaysRun = true) - fun afterClass() { - tmpFile.deleteIfExists() - } - - @Test(priority = 1, groups = ["commands"]) + @Test + @Order(1) fun loadTest() { seen.clear() assertThat(seen::seenNicks).isEmpty() @@ -64,7 +51,8 @@ class SeenTest { assertThat(seen::seenNicks).key(nick).isNotNull() } - @Test(groups = ["commands"]) + @Test + @Order(2) fun addTest() { val last = seen.seenNicks[nick]?.lastSeen seen.add(nick.lowercase()) @@ -75,11 +63,31 @@ class SeenTest { } } - @Test(priority = 10, groups = ["commands"]) + @Test + @Order(3) fun clearTest() { seen.clear() seen.save() seen.load() assertThat(seen::seenNicks).size().isEqualTo(0) } + + companion object { + private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") + private val seen = Seen(tmpFile.toAbsolutePath().toString()) + private const val nick = "ErikT" + + @JvmStatic + @BeforeClass + fun beforeClass() { + seen.add(nick) + assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) + } + + @JvmStatic + @AfterClass + fun afterClass() { + tmpFile.deleteIfExists() + } + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index f7239e0..f2c6f35 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,10 +36,10 @@ import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.assertions.prop -import org.testng.annotations.Test import java.time.Duration import java.time.LocalDateTime import java.time.temporal.Temporal +import kotlin.test.Test /** * The `TellMessageTest` class. @@ -49,7 +49,7 @@ class TellMessageTest { return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 } - @Test(groups = ["commands", "tell"]) + @Test fun testTellMessage() { val message = "Test message." val recipient = "recipient" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index 115e9fb..9ab1bce 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,16 +34,14 @@ package net.thauvin.erik.mobibot.commands.tell import assertk.all import assertk.assertThat import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test +import org.junit.AfterClass import java.time.LocalDateTime import kotlin.io.path.createTempFile import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize +import kotlin.test.Test class TellMessagesMgrTest { - private val testFile = createTempFile(suffix = ".ser") private val maxDays = 10L private val testMessages = mutableListOf().apply { for (i in 0..5) { @@ -51,18 +49,12 @@ class TellMessagesMgrTest { } } - @BeforeClass - fun saveTest() { + init { TellManager.save(testFile.toAbsolutePath().toString(), testMessages) assertThat(testFile.fileSize()).isGreaterThan(0) } - @AfterClass - fun afterClass() { - testFile.deleteIfExists() - } - - @Test(groups = ["commands", "tell"]) + @Test fun cleanTest() { testMessages.add(TellMessage("sender", "recipient", "message").apply { queued = LocalDateTime.now().minusDays(maxDays) @@ -73,7 +65,7 @@ class TellMessagesMgrTest { assertThat(testMessages, "testMessages").size().isEqualTo(size - 1) } - @Test(groups = ["commands", "tell"]) + @Test fun loadTest() { val messages = TellManager.load(testFile.toAbsolutePath().toString()) for (i in messages.indices) { @@ -84,4 +76,14 @@ class TellMessagesMgrTest { } } } + + companion object { + private val testFile = createTempFile(suffix = ".ser") + + @JvmStatic + @AfterClass + fun afterClass() { + testFile.deleteIfExists() + } + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 6eef16e..8e9bd6f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,7 +39,7 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import org.testng.annotations.Test +import kotlin.test.Test class EntriesUtilsTest { private val comment = EntryComment("comment", "nick") @@ -58,12 +58,12 @@ class EntriesUtilsTest { } } - @Test(groups = ["entries"]) + @Test fun printCommentTest() { assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") } - @Test(groups = ["entries"]) + @Test fun printLinkTest() { for (i in links.indices) { assertThat( @@ -75,7 +75,7 @@ class EntriesUtilsTest { assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]") } - @Test(groups = ["entries"]) + @Test fun printTagsTest() { for (i in links.indices) { assertThat( @@ -84,7 +84,7 @@ class EntriesUtilsTest { } } - @Test(groups = ["entries"]) + @Test fun toLinkLabelTest() { assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index ab3feee..495ed15 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,9 +35,9 @@ import assertk.assertThat import assertk.assertions.* import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl -import org.testng.annotations.Test import java.security.SecureRandom import java.util.* +import kotlin.test.Test /** * The `EntryUtilsTest` class. @@ -52,7 +52,7 @@ class EntryLinkTest { listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") ) - @Test(groups = ["entries"]) + @Test fun testAddDeleteComment() { var i = 0 while (i < 5) { @@ -85,7 +85,7 @@ class EntryLinkTest { assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse() } - @Test(groups = ["entries"]) + @Test fun testConstructor() { val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" }) val link = EntryLink("link", "title", "nick", "channel", Date(), tags) @@ -95,7 +95,7 @@ class EntryLinkTest { } } - @Test(groups = ["entries"]) + @Test fun testMatches() { assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue() assertThat(entryLink.matches("skynx"), "match(nick)").isTrue() @@ -106,7 +106,7 @@ class EntryLinkTest { } - @Test(groups = ["entries"]) + @Test fun testTags() { val tags: List = entryLink.tags for ((i, tag) in tags.withIndex()) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index 4223d9d..de6075d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,27 +35,25 @@ import assertk.all import assertk.assertThat import assertk.assertions.* import net.thauvin.erik.mobibot.Utils.today -import org.testng.annotations.BeforeSuite -import org.testng.annotations.Test import java.nio.file.Paths import java.util.* import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize import kotlin.io.path.name +import kotlin.test.Test class FeedMgrTest { private val entries = Entries() private val channel = "mobibot" - @BeforeSuite(alwaysRun = true) - fun beforeSuite() { + init { entries.logsDir = "src/test/resources/" entries.ircServer = "irc.example.com" entries.channel = channel entries.backlogs = "https://www.mobitopia.org/mobibot/logs" } - @Test(groups = ["entries"]) + @Test fun testFeedMgr() { // Load the feed assertThat(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 2b1d3f9..6df5616 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,13 +37,13 @@ import assertk.assertions.isInstanceOf import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate -import org.testng.annotations.Test +import kotlin.test.Test /** * The `CalcTest` class. */ class CalcTest { - @Test(groups = ["modules"]) + @Test fun testCalculate() { assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 66fb98d..00fa43f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,7 +1,7 @@ /* * ChatGptTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,18 +35,20 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.DisabledOnCi import net.thauvin.erik.mobibot.LocalProperties -import org.testng.annotations.Test +import kotlin.test.Test class ChatGptTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testApiKey() { assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } - @Test(groups = ["modules", "no-ci"]) + @Test + @DisabledOnCi fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 0547c95..9847080 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,20 +39,17 @@ import net.thauvin.erik.crypto.CryptoPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test +import kotlin.test.Test /** * The `CryptoPricesTest` class. */ class CryptoPricesTest { - @BeforeClass - @Throws(ModuleException::class) - fun before() { + init { loadCurrencies() } - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testMarketPrice() { var price = currentPrice(listOf("BTC")) @@ -70,7 +67,7 @@ class CryptoPricesTest { } } - @Test(groups = ["modules"]) + @Test fun testGetCurrencyName() { assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 5375784..57affe5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -42,23 +42,19 @@ import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test +import kotlin.test.Test /** * The `CurrencyConvertTest` class. */ class CurrencyConverterTest : LocalProperties() { - - @BeforeClass - @Throws(ModuleException::class) - fun before() { + init { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) loadSymbols(apiKey) } - @Test(groups = ["modules"]) + @Test fun testConvertCurrency() { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) assertThat( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index cdc04f0..0aaacb9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,10 +35,10 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.matches -import org.testng.annotations.Test +import kotlin.test.Test class DiceTest { - @Test(groups = ["modules"]) + @Test fun testRoll() { assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") assertThat(Dice.roll(2, 1), "roll(2d1)") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index fa50f8c..b0c154a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,18 +34,19 @@ import assertk.all import assertk.assertFailure import assertk.assertThat import assertk.assertions.* +import net.thauvin.erik.mobibot.DisabledOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test +import kotlin.test.Test /** * The `GoogleSearchTest` class. */ class GoogleSearchTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testAPIKeys() { assertThat( searchGoogle("", "apikey", "cssKey").first(), @@ -63,7 +64,8 @@ class GoogleSearchTest : LocalProperties() { .hasMessage("API key not valid. Please pass a valid API key.") } - @Test(groups = ["no-ci", "modules"]) + @Test + @DisabledOnCi @Throws(ModuleException::class) fun testSearchGoogle() { val apiKey = getProperty(GoogleSearch.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 55a7b8f..9b8dcb3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,13 +36,13 @@ import assertk.assertions.* import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage -import org.testng.annotations.Test +import kotlin.test.Test /** * The `JokeTest` class. */ class JokeTest { - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testRandomJoke() { val joke = randomJoke() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 9c21f7c..e67c8ad 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,13 +35,13 @@ import assertk.assertions.any import assertk.assertions.contains import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois -import org.testng.annotations.Test +import kotlin.test.Test /** * The `Lookup Test` class. */ class LookupTest { - @Test(groups = ["modules"]) + @Test @Throws(Exception::class) fun testLookup() { var result = nslookup("apple.com") @@ -51,7 +51,7 @@ class LookupTest { assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") } - @Test(groups = ["modules"]) + @Test @Throws(Exception::class) fun testWhois() { val result = whois("17.178.96.59", Lookup.WHOIS_HOST) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 34f778a..d189d75 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ import assertk.assertThat import assertk.assertions.contains import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot -import org.testng.annotations.Test +import kotlin.test.Test class MastodonTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index db68280..9dda5b3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ import assertk.all import assertk.assertThat import assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import org.testng.annotations.DataProvider -import org.testng.annotations.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource import java.io.IOException -import java.lang.reflect.Method +import kotlin.test.Test /** * The `ModuleExceptionTest` class. @@ -46,28 +46,30 @@ class ModuleExceptionTest { companion object { const val DEBUG_MESSAGE = "debugMessage" const val MESSAGE = "message" + + @JvmStatic + fun dataProviders(): List { + return listOf( + ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com")), + ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?")), + ModuleException(DEBUG_MESSAGE, MESSAGE) + ) + } } - @DataProvider(name = "dp") - fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array> { - return arrayOf( - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) - ) - } - - @Test(dataProvider = "dp") + @ParameterizedTest + @MethodSource("dataProviders") fun testGetDebugMessage(e: ModuleException) { assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) } - @Test(dataProvider = "dp") + @ParameterizedTest + @MethodSource("dataProviders") fun testGetMessage(e: ModuleException) { assertThat(e).hasMessage(MESSAGE) } - @Test(groups = ["modules"]) + @Test fun testSanitizeMessage() { val apiKey = "1234567890" var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index e1e79f3..a6ff707 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,18 +34,18 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isNotEmpty import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing -import org.testng.annotations.Test +import kotlin.test.Test /** * The `PingTest` class. */ class PingTest { - @Test(groups = ["modules"]) + @Test fun testPingsArray() { assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty() } - @Test(groups = ["modules"]) + @Test fun testRandomPing() { for (i in 0..9) { assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing()) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 8dc90ba..fb8865b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.isEqualTo import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw -import org.testng.annotations.Test +import kotlin.test.Test class RockPaperScissorsTest { - @Test(groups = ["modules"]) + @Test fun testWinLoseOrDraw() { assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win") assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index aff4188..ae8cff2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,7 +39,7 @@ import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test +import kotlin.test.Test /** * The `StockQuoteTest` class. @@ -49,7 +49,7 @@ class StockQuoteTest : LocalProperties() { return "${label}:[ ]+[0-9.]+".prependIndent() } - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testGetQuote() { val apiKey = getProperty(StockQuote.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index d7d65de..e135866 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -43,19 +43,19 @@ import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh import net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test +import kotlin.test.Test /** * The `Weather2Test` class. */ class Weather2Test : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testFtoC() { val t = ftoC(32.0) assertThat(t.second, "32 °F is 0 °C").isEqualTo(0) } - @Test(groups = ["modules"]) + @Test fun testGetCountry() { assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) @@ -67,13 +67,13 @@ class Weather2Test : LocalProperties() { } } - @Test(groups = ["modules"]) + @Test fun testMphToKmh() { val w = mphToKmh(0.62) assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1) } - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testWeather() { var query = "98204" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index ae1722d..417f9fc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,13 +36,14 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.DisabledOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram -import org.testng.annotations.Test +import kotlin.test.Test class WolframAlphaTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testAppId() { assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } .isInstanceOf(ModuleException::class.java) @@ -52,7 +53,8 @@ class WolframAlphaTest : LocalProperties() { .isInstanceOf(ModuleException::class.java) } - @Test(groups = ["modules", "no-ci"]) + @Test + @DisabledOnCi @Throws(ModuleException::class) fun queryWolframTest() { val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 3602a27..5c8cc84 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,14 +39,14 @@ import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time import org.pircbotx.Colors -import org.testng.annotations.Test import java.time.ZoneId +import kotlin.test.Test /** * The `WordTimeTest` class. */ class WordTimeTest { - @Test(groups = ["modules"]) + @Test fun testTime() { assertThat(time(), "time()").matches( ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + @@ -61,7 +61,7 @@ class WordTimeTest { assertThat(time("BEAT"), "time($BEATS_KEYWORD)").matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) } - @Test(groups = ["modules"]) + @Test fun testZones() { COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { assertThat(ZoneId.of(it.value)) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index e856112..477c1a4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,7 +36,7 @@ import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.assertions.prop -import org.testng.annotations.Test +import kotlin.test.Test class MessageTest { @Test diff --git a/src/test/resources/current.xml b/src/test/resources/current.xml index 535a400..8552a9a 100644 --- a/src/test/resources/current.xml +++ b/src/test/resources/current.xml @@ -1,35 +1,4 @@ - - #mobibot IRC Links diff --git a/version.mustache b/version.mustache deleted file mode 100644 index b2672e4..0000000 --- a/version.mustache +++ /dev/null @@ -1,32 +0,0 @@ -/* -* This file is automatically generated. -* Do not modify! -- ALL CHANGES WILL BE ERASED! -*/ -package {{packageName}} - -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.Instant - -/** -* Provides semantic version information. -* -* @author [Semantic Version Annotation Processor](https://github.com/ethauvin/semver) -*/ -object ReleaseInfo{ - const val PROJECT = "{{project}}" - const val VERSION = "{{version}}" - - @JvmField - val BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()) - - const val MAJOR = {{major}} - const val MINOR = {{minor}} - const val PATCH = {{patch}} - const val BUILDMETA = "{{buildMeta}}" - const val PRERELEASE = "{{preRelease}}" - - const val WEBSITE = "https://www.mobitopia.org/mobibot/" - const val AUTHOR = "Erik C. Thauvin" - const val AUTHOR_URL = "https://erik.thauvin.net/" -} diff --git a/version.properties b/version.properties deleted file mode 100644 index 9c446af..0000000 --- a/version.properties +++ /dev/null @@ -1,9 +0,0 @@ -#Generated by the Semver Plugin for Gradle -#Wed Nov 01 22:09:32 PDT 2023 -version.buildmeta=20231101220932 -version.major=0 -version.minor=8 -version.patch=0 -version.prerelease=rc -version.project=mobibot -version.semver=0.8.0-rc+20231101220932 From f8de100dde2e02996f1b0e10c9d6183c6f213b63 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 11 Nov 2023 00:34:54 -0800 Subject: [PATCH 738/842] Added missing env variables --- .github/workflows/gradle.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ae6c66f..19081a4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -35,6 +35,16 @@ jobs: run: ./bld compile - name: Run tests with bld + env: + CI_NAME: "GitHub CI" + ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} + CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} + OWM_API_KEY: ${{ secrets.OWM_API_KEY }} + PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} + MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} + MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} + MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} + EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} run: ./bld jacoco - name: SonarCloud Scan From 208f3a82d99f1c056c424948f7ad107d482ea481 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 11 Nov 2023 13:13:35 -0800 Subject: [PATCH 739/842] Ignored bin directory --- .gitignore | 5 +- bin/main/log4j2.xml | 69 --- bin/main/net/thauvin/erik/mobibot/Addons.kt | 190 -------- .../net/thauvin/erik/mobibot/Constants.kt | 102 ---- .../net/thauvin/erik/mobibot/FeedReader.kt | 92 ---- bin/main/net/thauvin/erik/mobibot/Mobibot.kt | 421 ----------------- bin/main/net/thauvin/erik/mobibot/Pinboard.kt | 113 ----- bin/main/net/thauvin/erik/mobibot/Utils.kt | 439 ------------------ .../erik/mobibot/commands/AbstractCommand.kt | 79 ---- .../erik/mobibot/commands/ChannelFeed.kt | 62 --- .../thauvin/erik/mobibot/commands/Cycle.kt | 66 --- .../net/thauvin/erik/mobibot/commands/Die.kt | 62 --- .../thauvin/erik/mobibot/commands/Ignore.kt | 147 ------ .../net/thauvin/erik/mobibot/commands/Info.kt | 124 ----- .../net/thauvin/erik/mobibot/commands/Me.kt | 51 -- .../thauvin/erik/mobibot/commands/Modules.kt | 63 --- .../net/thauvin/erik/mobibot/commands/Msg.kt | 60 --- .../net/thauvin/erik/mobibot/commands/Nick.kt | 51 -- .../thauvin/erik/mobibot/commands/Recap.kt | 81 ---- .../net/thauvin/erik/mobibot/commands/Say.kt | 51 -- .../thauvin/erik/mobibot/commands/Users.kt | 50 -- .../thauvin/erik/mobibot/commands/Versions.kt | 59 --- .../erik/mobibot/commands/links/Comment.kt | 151 ------ .../mobibot/commands/links/LinksManager.kt | 207 --------- .../erik/mobibot/commands/links/Posting.kt | 164 ------- .../erik/mobibot/commands/links/Tags.kt | 87 ---- .../erik/mobibot/commands/links/View.kt | 120 ----- .../mobibot/commands/seen/NickComparator.kt | 45 -- .../erik/mobibot/commands/seen/Seen.kt | 150 ------ .../erik/mobibot/commands/seen/SeenNick.kt | 41 -- .../erik/mobibot/commands/tell/Tell.kt | 306 ------------ .../erik/mobibot/commands/tell/TellManager.kt | 74 --- .../erik/mobibot/commands/tell/TellMessage.kt | 104 ----- .../thauvin/erik/mobibot/entries/Entries.kt | 54 --- .../erik/mobibot/entries/EntriesUtils.kt | 83 ---- .../erik/mobibot/entries/EntryComment.kt | 52 --- .../thauvin/erik/mobibot/entries/EntryLink.kt | 213 --------- .../erik/mobibot/entries/FeedsManager.kt | 187 -------- .../erik/mobibot/modules/AbstractModule.kt | 131 ------ .../net/thauvin/erik/mobibot/modules/Calc.kt | 87 ---- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 176 ------- .../erik/mobibot/modules/CryptoPrices.kt | 159 ------- .../erik/mobibot/modules/CurrencyConverter.kt | 222 --------- .../net/thauvin/erik/mobibot/modules/Dice.kt | 87 ---- .../erik/mobibot/modules/GoogleSearch.kt | 162 ------- .../net/thauvin/erik/mobibot/modules/Joke.kt | 105 ----- .../thauvin/erik/mobibot/modules/Lookup.kt | 171 ------- .../thauvin/erik/mobibot/modules/Mastodon.kt | 149 ------ .../erik/mobibot/modules/ModuleException.kt | 45 -- .../net/thauvin/erik/mobibot/modules/Ping.kt | 83 ---- .../erik/mobibot/modules/RockPaperScissors.kt | 114 ----- .../erik/mobibot/modules/StockQuote.kt | 236 ---------- .../thauvin/erik/mobibot/modules/War.class | Bin 2452 -> 0 bytes .../thauvin/erik/mobibot/modules/Weather2.kt | 250 ---------- .../erik/mobibot/modules/WolframAlpha.kt | 142 ------ .../thauvin/erik/mobibot/modules/WorldTime.kt | 390 ---------------- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 37 -- .../net/thauvin/erik/mobibot/msg/Message.kt | 65 --- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 38 -- .../erik/mobibot/msg/PrivateMessage.kt | 37 -- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 36 -- .../erik/mobibot/social/SocialManager.kt | 116 ----- .../erik/mobibot/social/SocialModule.kt | 96 ---- .../erik/mobibot/social/SocialTimer.kt | 40 -- bin/test/current.xml | 67 --- .../net/thauvin/erik/mobibot/AddonsTest.kt | 86 ---- .../erik/mobibot/ExceptionSanitizer.kt | 60 --- .../thauvin/erik/mobibot/FeedReaderTest.kt | 78 ---- .../thauvin/erik/mobibot/LocalProperties.kt | 85 ---- .../net/thauvin/erik/mobibot/PinboardTest.kt | 81 ---- .../net/thauvin/erik/mobibot/UtilsTest.kt | 278 ----------- .../thauvin/erik/mobibot/commands/InfoTest.kt | 58 --- .../erik/mobibot/commands/RecapTest.kt | 60 --- .../commands/links/LinksManagerTest.kt | 77 --- .../erik/mobibot/commands/links/ViewTest.kt | 111 ----- .../erik/mobibot/commands/seen/SeenTest.kt | 85 ---- .../mobibot/commands/tell/TellMessageTest.kt | 72 --- .../commands/tell/TellMessagesMgrTest.kt | 87 ---- .../erik/mobibot/entries/EntriesUtilsTest.kt | 91 ---- .../erik/mobibot/entries/EntryLinkTest.kt | 133 ------ .../erik/mobibot/entries/FeedMgrTest.kt | 115 ----- .../thauvin/erik/mobibot/modules/CalcTest.kt | 53 --- .../erik/mobibot/modules/ChatGptTest.kt | 62 --- .../erik/mobibot/modules/CryptoPricesTest.kt | 78 ---- .../mobibot/modules/CurrencyConverterTest.kt | 85 ---- .../thauvin/erik/mobibot/modules/DiceTest.kt | 53 --- .../erik/mobibot/modules/GoogleSearchTest.kt | 95 ---- .../thauvin/erik/mobibot/modules/JokeTest.kt | 57 --- .../erik/mobibot/modules/LookupTest.kt | 60 --- .../erik/mobibot/modules/MastodonTest.kt | 54 --- .../mobibot/modules/ModuleExceptionTest.kt | 105 ----- .../thauvin/erik/mobibot/modules/PingTest.kt | 54 --- .../mobibot/modules/RockPaperScissorsTest.kt | 50 -- .../erik/mobibot/modules/StockQuoteTest.kt | 85 ---- .../erik/mobibot/modules/Weather2Test.kt | 117 ----- .../erik/mobibot/modules/WolframAlphaTest.kt | 77 --- .../erik/mobibot/modules/WordTimeTest.kt | 70 --- .../thauvin/erik/mobibot/msg/MessageTest.kt | 109 ----- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +- src/main/resources/log4j2.xml | 38 -- 100 files changed, 5 insertions(+), 10574 deletions(-) delete mode 100644 bin/main/log4j2.xml delete mode 100644 bin/main/net/thauvin/erik/mobibot/Addons.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Constants.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/FeedReader.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Mobibot.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Pinboard.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Utils.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Die.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Info.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Me.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Modules.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Msg.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Nick.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Recap.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Say.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Users.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Versions.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/View.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/Entries.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Calc.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Dice.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Joke.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Ping.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/War.class delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/Message.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt delete mode 100644 bin/test/current.xml delete mode 100644 bin/test/net/thauvin/erik/mobibot/AddonsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/LocalProperties.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/PinboardTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/UtilsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt delete mode 100644 src/main/resources/log4j2.xml diff --git a/.gitignore b/.gitignore index fa550e5..6c5fc87 100644 --- a/.gitignore +++ b/.gitignore @@ -54,8 +54,9 @@ atlassian-ide-plugin.xml # Editor-based Rest Client .idea/httpRequests -local.properties - +bin deploy +local.properties logs mobibot.properties +out diff --git a/bin/main/log4j2.xml b/bin/main/log4j2.xml deleted file mode 100644 index 265c88f..0000000 --- a/bin/main/log4j2.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bin/main/net/thauvin/erik/mobibot/Addons.kt b/bin/main/net/thauvin/erik/mobibot/Addons.kt deleted file mode 100644 index 2c5f05d..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Addons.kt +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Addons.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -import net.thauvin.erik.mobibot.Utils.notContains -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.LinksManager -import net.thauvin.erik.mobibot.modules.AbstractModule -import org.pircbotx.hooks.events.PrivateMessageEvent -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.util.* - -/** - * Modules and Commands addons. - */ -class Addons(private val props: Properties) { - private val logger: Logger = LoggerFactory.getLogger(Addons::class.java) - private val disabledModules = props.getProperty("disabled-modules", "").split(LinksManager.TAG_MATCH) - private val disableCommands = props.getProperty("disabled-commands", "").split(LinksManager.TAG_MATCH) - - val commands: MutableList = mutableListOf() - val modules: MutableList = mutableListOf() - val names = Names - - /** - * Add a module with properties. - */ - fun add(module: AbstractModule): Boolean { - var enabled = false - with(module) { - if (disabledModules.notContains(name, true)) { - if (hasProperties()) { - propertyKeys.forEach { - setProperty(it, props.getProperty(it, "")) - } - } - - if (isEnabled) { - modules.add(this) - names.modules.add(name) - names.commands.addAll(commands) - enabled = true - } else { - if (logger.isDebugEnabled) { - logger.debug("Module $name is disabled.") - } - names.disabledModules.add(name) - } - } else { - names.disabledModules.add(name) - } - } - return enabled - } - - /** - * Add a command with properties. - */ - fun add(command: AbstractCommand): Boolean { - var enabled = false - with(command) { - if (disableCommands.notContains(name, true)) { - if (properties.isNotEmpty()) { - properties.keys.forEach { - setProperty(it, props.getProperty(it, "")) - } - } - if (isEnabled()) { - commands.add(this) - if (isVisible) { - if (isOpOnly) { - names.ops.add(name) - } else { - names.commands.add(name) - } - } - enabled = true - } else { - if (logger.isDebugEnabled) { - logger.debug("Command $name is disabled.") - } - names.disabledCommands.add(name) - } - } else { - names.disabledCommands.add(name) - } - } - return enabled - } - - /** - * Execute a command or module. - */ - fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean { - val cmds = if (event is PrivateMessageEvent) commands else commands.filter { it.isPublic } - for (command in cmds) { - if (command.name.startsWith(cmd)) { - command.commandResponse(channel, args, event) - return true - } - } - val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules - for (module in mods) { - if (module.commands.contains(cmd)) { - module.commandResponse(channel, cmd, args, event) - return true - } - } - return false - } - - /** - * Match a command. - */ - fun match(channel: String, event: GenericMessageEvent): Boolean { - for (command in commands) { - if (command.matches(event.message)) { - command.commandResponse(channel, event.message, event) - return true - } - } - return false - } - - /** - * Commands and Modules help. - */ - fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean { - for (command in commands) { - if (command.isVisible && command.name.startsWith(topic)) { - return command.helpResponse(channel, topic, event) - } - } - for (module in modules) { - if (module.commands.contains(topic)) { - return module.helpResponse(event) - } - } - return false - } - - /** - * Holds commands and modules names. - */ - object Names { - val modules: MutableList = mutableListOf() - val disabledModules: MutableList = mutableListOf() - val commands: MutableList = mutableListOf() - val disabledCommands: MutableList = mutableListOf() - val ops: MutableList = mutableListOf() - - fun sort() { - modules.sort() - disabledModules.sort() - commands.sort() - disabledCommands.sort() - ops.sort() - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/Constants.kt b/bin/main/net/thauvin/erik/mobibot/Constants.kt deleted file mode 100644 index 98ef74a..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Constants.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Constants.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -/** - * The `Constants`. - */ -object Constants { - /** - * The connect/read timeout in ms. - */ - const val CONNECT_TIMEOUT = 5000 - - /** - * Debug command line argument. - */ - const val DEBUG_ARG = "debug" - - /** - * Default IRC Port. - */ - const val DEFAULT_PORT = 6667 - - /** - * Default IRC Server. - */ - const val DEFAULT_SERVER = "irc.libera.chat" - - /** - * CLI command for usage. - */ - const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar" - - /** - * User-Agent - */ - const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" - - /** - * The help command. - */ - const val HELP_CMD = "help" - - /** - * The link command. - */ - const val LINK_CMD = "L" - - /** - * The empty title string. - */ - const val NO_TITLE = "No Title" - - /** - * Properties command line argument. - */ - const val PROPS_ARG = "properties" - - /** - * The tag command - */ - const val TAG_CMD = "T" - - /** - * The timer delay in minutes. - */ - const val TIMER_DELAY = 10L - - /** - * Properties version line argument. - */ - const val VERSION_ARG = "version" -} diff --git a/bin/main/net/thauvin/erik/mobibot/FeedReader.kt b/bin/main/net/thauvin/erik/mobibot/FeedReader.kt deleted file mode 100644 index d82f011..0000000 --- a/bin/main/net/thauvin/erik/mobibot/FeedReader.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * FeedReader.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -import com.rometools.rome.io.FeedException -import com.rometools.rome.io.SyndFeedInput -import com.rometools.rome.io.XmlReader -import net.thauvin.erik.mobibot.Utils.green -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.entries.FeedsManager -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URL - -/** - * Reads an RSS feed. - */ -class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable { - private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) - - /** - * Fetches the Feed's items. - */ - override fun run() { - try { - readFeed(url).forEach { - event.sendMessage("", it) - } - } catch (e: FeedException) { - if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e) - event.sendMessage("An error has occurred while parsing the feed: ${e.message}") - } catch (e: IOException) { - if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e) - event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}") - } - } - - companion object { - @JvmStatic - @Throws(FeedException::class, IOException::class) - fun readFeed(url: String, maxItems: Int = 5): List { - val messages = mutableListOf() - val input = SyndFeedInput() - XmlReader(URL(url).openStream()).use { reader -> - val feed = input.build(reader) - val items = feed.entries - if (items.isEmpty()) { - messages.add(NoticeMessage("There is currently nothing to view.")) - } else { - items.take(maxItems).forEach { - messages.add(NoticeMessage(it.title)) - messages.add(NoticeMessage(helpFormat(it.link.green(), false))) - } - } - } - return messages - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/Mobibot.kt b/bin/main/net/thauvin/erik/mobibot/Mobibot.kt deleted file mode 100644 index f91c457..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Mobibot.kt +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Mobibot.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot - -import kotlinx.cli.ArgParser -import kotlinx.cli.ArgType -import kotlinx.cli.default -import net.thauvin.erik.mobibot.Utils.appendIfMissing -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.getIntProperty -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.lastOrEmpty -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import net.thauvin.erik.mobibot.commands.* -import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap -import net.thauvin.erik.mobibot.commands.links.* -import net.thauvin.erik.mobibot.commands.seen.Seen -import net.thauvin.erik.mobibot.commands.tell.Tell -import net.thauvin.erik.mobibot.modules.* -import net.thauvin.erik.semver.Version -import org.pircbotx.Configuration -import org.pircbotx.PircBotX -import org.pircbotx.hooks.ListenerAdapter -import org.pircbotx.hooks.events.* -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.* -import java.nio.file.Files -import java.nio.file.Paths -import java.util.* -import java.util.regex.Pattern -import kotlin.system.exitProcess - -@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") -class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { - // The bot configuration. - private val config: Configuration - - // Commands and Modules - private val addons: Addons - - // Seen command - private val seen: Seen - - // Tell command - private val tell: Tell - - /** Logger. */ - val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java) - - /** - * Connects to the server and joins the channel. - */ - fun connect() { - PircBotX(config).startBot() - } - - /** - * Responds with the default help. - */ - private fun helpDefault(event: GenericMessageEvent) { - event.sendMessage("Type a URL on $channel to post it.") - event.sendMessage("For more information on a specific command, type:") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c ${Constants.HELP_CMD} ", event.bot().nick, event is PrivateMessageEvent) - ) - ) - event.sendMessage("The commands are:") - event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) - if (event.isChannelOp(channel)) { - if (addons.names.disabledCommands.isNotEmpty()) { - event.sendMessage("The disabled commands are:") - event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true) - } - event.sendMessage("The op commands are:") - event.sendList(addons.names.ops, 8, isBold = true, isIndent = true) - } - } - - /** - * Responds with the default, commands or modules help. - */ - private fun helpResponse(event: GenericMessageEvent, topic: String) { - if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) { - helpDefault(event) - } - } - - override fun onAction(event: ActionEvent?) { - event?.channel?.let { - if (channel == it.name) { - event.user?.let { user -> - storeRecap(user.nick, event.action, true) - } - } - } - } - - override fun onDisconnect(event: DisconnectEvent?) { - event?.let { - with(event.getBot()) { - LinksManager.socialManager.notification("$nick disconnected from $serverHostname") - seen.add(userChannelDao.getChannel(channel).users) - } - } - LinksManager.socialManager.shutdown() - } - - override fun onPrivateMessage(event: PrivateMessageEvent?) { - event?.user?.let { user -> - if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}") - val cmds = event.message.trim().split(" ".toRegex(), 2) - val cmd = cmds[0].lowercase() - val args = cmds.lastOrEmpty().trim() - if (cmd.startsWith(Constants.HELP_CMD)) { // help - helpResponse(event, args) - } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module - helpDefault(event) - } - } - } - - override fun onJoin(event: JoinEvent?) { - event?.user?.let { user -> - with(event.getBot()) { - if (user.nick == nick) { - LinksManager.socialManager.notification( - "$nick has joined ${event.channel.name} on $serverHostname" - ) - seen.add(userChannelDao.getChannel(channel).users) - } else { - tell.send(event) - seen.add(user.nick) - } - } - } - } - - override fun onMessage(event: MessageEvent?) { - event?.user?.let { user -> - tell.send(event) - if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: - if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") - val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2) - val cmd = cmds[0].lowercase() - val args = cmds.lastOrEmpty().trim() - if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help - helpResponse(event, args) - } else { - // Execute module or command - addons.exec(channel, cmd, args, event) - } - } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. - if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") - } - storeRecap(user.nick, event.message, false) - seen.add(user.nick) - } - } - - override fun onNickChange(event: NickChangeEvent?) { - event?.let { - tell.send(event) - if (!it.oldNick.equals(it.newNick, true)) { - seen.add(it.oldNick) - } - seen.add(it.newNick) - } - } - - override fun onPart(event: PartEvent?) { - event?.user?.let { user -> - with(event.getBot()) { - if (user.nick == nick) { - LinksManager.socialManager.notification( - "$nick has left ${event.channel.name} on $serverHostname" - ) - seen.add(userChannelDao.getChannel(channel).users) - } else { - seen.add(user.nick) - } - } - } - } - - override fun onQuit(event: QuitEvent?) { - event?.user?.let { user -> - seen.add(user.nick) - } - } - - companion object { - @JvmStatic - @Throws(Exception::class) - fun main(args: Array) { - // Set up the command line options - val parser = ArgParser(Constants.CLI_CMD) - val debug by parser.option( - ArgType.Boolean, - Constants.DEBUG_ARG, - Constants.DEBUG_ARG.substring(0, 1), - "Print debug & logging data directly to the console" - ).default(false) - val property by parser.option( - ArgType.String, - Constants.PROPS_ARG, - Constants.PROPS_ARG.substring(0, 1), - "Use alternate properties file" - ).default("./${ReleaseInfo.PROJECT}.properties") - val version by parser.option( - ArgType.Boolean, - Constants.VERSION_ARG, - Constants.VERSION_ARG.substring(0, 1), - "Print version info" - ).default(false) - - // Parse the command line - parser.parse(args) - - if (version) { - // Output the version - println( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" - ) - println(ReleaseInfo.WEBSITE) - } else { - // Load the properties - val p = Properties() - try { - Files.newInputStream( - Paths.get(property) - ).use { fis -> - p.load(fis) - } - } catch (ignore: FileNotFoundException) { - System.err.println("Unable to find properties file.") - exitProcess(1) - } catch (ignore: IOException) { - System.err.println("Unable to open properties file.") - exitProcess(1) - } - val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) - val channel = p.getProperty("channel") - val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) - - // Redirect stdout and stderr - if (!debug) { - try { - val stdout = PrintStream( - BufferedOutputStream( - FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true - ) - ), true - ) - System.setOut(stdout) - } catch (ignore: IOException) { - System.err.println("Unable to open output (stdout) log file.") - exitProcess(1) - } - try { - val stderr = PrintStream( - BufferedOutputStream( - FileOutputStream("$logsDir$nickname.err", true) - ), true - ) - System.setErr(stderr) - } catch (ignore: IOException) { - System.err.println("Unable to open error (stderr) log file.") - exitProcess(1) - } - } - - // Start the bot - Mobibot(nickname, channel, logsDir, p).connect() - } - } - } - - /** - * Initialize the bot. - */ - init { - val ircServer = p.getProperty("server", Constants.DEFAULT_SERVER) - config = Configuration.Builder().apply { - name = nickname - login = p.getProperty("login", nickname) - realName = p.getProperty("realname", nickname) - addServer( - ircServer, - p.getIntProperty("port", Constants.DEFAULT_PORT) - ) - addAutoJoinChannel(channel) - addListener(this@Mobibot) - version = "${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}" - isAutoNickChange = true - val identPwd = p.getProperty("ident") - if (!identPwd.isNullOrBlank()) { - nickservPassword = identPwd - } - val identNick = p.getProperty("ident-nick") - if (!identNick.isNullOrBlank()) { - nickservNick = identNick - } - val identMsg = p.getProperty("ident-msg") - if (!identMsg.isNullOrBlank()) { - nickservCustomMessage = identMsg - } - isAutoReconnect = true - - //socketConnectTimeout = Constants.CONNECT_TIMEOUT - //socketTimeout = Constants.CONNECT_TIMEOUT - //messageDelay = StaticDelay(500) - }.buildConfiguration() - - // Load the current entries - with(LinksManager) { - entries.channel = channel - entries.ircServer = ircServer - entries.logsDir = logsDirPath - entries.backlogs = p.getProperty("backlogs", "") - entries.load() - - // Set up pinboard - pinboard.setApiToken(p.getProperty("pinboard-api-token", "")) - } - - addons = Addons(p) - - // Load the commands - addons.add(ChannelFeed(channel.removePrefix("#"))) - addons.add(Comment()) - addons.add(Cycle()) - addons.add(Die()) - addons.add(Ignore()) - addons.add(LinksManager()) - addons.add(Me()) - addons.add(Modules(addons.names.modules, addons.names.disabledModules)) - addons.add(Msg()) - addons.add(Nick()) - addons.add(Posting()) - addons.add(Recap()) - addons.add(Say()) - - // Seen command - seen = Seen("${logsDirPath}${nickname}-seen.ser") - addons.add(seen) - - addons.add(Tags()) - - // Tell command - tell = Tell("${logsDirPath}${nickname}.ser") - addons.add(tell) - - addons.add(Users()) - addons.add(Versions()) - addons.add(View()) - - // Load social modules - LinksManager.socialManager.add(addons, Mastodon()) - - // Load the modules - addons.add(Calc()) - addons.add(ChatGpt()) - addons.add(CryptoPrices()) - addons.add(CurrencyConverter()) - addons.add(Dice()) - addons.add(GoogleSearch()) - addons.add(Info(tell, seen)) - addons.add(Joke()) - addons.add(Lookup()) - addons.add(Ping()) - addons.add(RockPaperScissors()) - addons.add(StockQuote()) - addons.add(War()) - addons.add(Weather2()) - addons.add(WolframAlpha()) - addons.add(WorldTime()) - - // Sort the addons - addons.names.sort() - } -} - diff --git a/bin/main/net/thauvin/erik/mobibot/Pinboard.kt b/bin/main/net/thauvin/erik/mobibot/Pinboard.kt deleted file mode 100644 index 7cb5aed..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Pinboard.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Pinboard.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot - -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.pinboard.PinboardPoster -import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.time.temporal.ChronoUnit -import java.util.* - -/** - * Handles posts to pinboard.in. - */ -class Pinboard { - private val poster = PinboardPoster() - - /** - * Adds a pin. - */ - fun addPin(ircServer: String, entry: EntryLink) { - if (poster.apiToken.isNotBlank()) { - with(entry) { - poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) - } - } - } - - /** - * Sets the pinboard API token. - */ - fun setApiToken(apiToken: String) { - poster.apiToken = apiToken - } - - /** - * Deletes a pin. - */ - fun deletePin(entry: EntryLink) { - if (poster.apiToken.isNotBlank()) { - poster.deletePin(entry.link) - } - - } - - /** - * Updates a pin. - */ - fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) { - if (poster.apiToken.isNotBlank()) { - with(entry) { - if (oldUrl != link) { - poster.deletePin(oldUrl) - } - poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) - } - } - } - - /** - * Formats a date to a UTC timestamp. - */ - private fun Date.toTimestamp(): String { - return ZonedDateTime.ofInstant( - toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() - ).format(DateTimeFormatter.ISO_INSTANT) - } - - /** - * Formats the tags for pinboard. - */ - private fun EntryLink.formatTags(): String { - return nick + formatTags(",", ",") - } - - /** - * Returns the pinboard.in extended attribution line. - */ - private fun EntryLink.postedBy(ircServer: String): String { - return "Posted by $nick on $channel ( $ircServer )" - } -} - diff --git a/bin/main/net/thauvin/erik/mobibot/Utils.kt b/bin/main/net/thauvin/erik/mobibot/Utils.kt deleted file mode 100644 index e4760d2..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Utils.kt +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Utils.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import net.thauvin.erik.urlencoder.UrlEncoderUtil -import org.jsoup.Jsoup -import org.pircbotx.Colors -import org.pircbotx.PircBotX -import org.pircbotx.hooks.events.PrivateMessageEvent -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import java.io.* -import java.net.HttpURLConnection -import java.net.URL -import java.nio.file.Files -import java.nio.file.Paths -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.util.* -import kotlin.io.path.exists -import kotlin.io.path.fileSize - -/** - * Miscellaneous utilities. - */ -@Suppress("TooManyFunctions") -object Utils { - private val searchFlags = arrayOf("%c", "%n") - - /** - * Prepends a prefix if not present. - */ - @JvmStatic - fun String.prefixIfMissing(prefix: Char): String { - return if (first() != prefix) { - "$prefix${this}" - } else { - this - } - } - - /** - * Appends a suffix to the end of the String if not present. - */ - @JvmStatic - fun String.appendIfMissing(suffix: Char): String { - return if (last() != suffix) { - "$this${suffix}" - } else { - this - } - } - - /** - * Makes the given int bold. - */ - @JvmStatic - fun Int.bold(): String = toString().bold() - - /** - * Makes the given long bold. - */ - @JvmStatic - fun Long.bold(): String = toString().bold() - - /** - * Makes the given string bold. - */ - @JvmStatic - fun String?.bold(): String = colorize(Colors.BOLD) - - /** - * Returns the [PircBotX] instance. - */ - fun GenericMessageEvent.bot(): PircBotX { - return getBot() as PircBotX - } - - /** - * Capitalize a string. - */ - @JvmStatic - fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() } - - /** - * Capitalize words - */ - @JvmStatic - fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() } - - /** - * Colorize a string. - */ - @JvmStatic - fun String?.colorize(color: String): String { - return when { - isNullOrEmpty() -> { - "" - } - - color == DEFAULT_COLOR -> { - this - } - - Colors.BOLD == color || Colors.REVERSE == color -> { - color + this + color - } - - else -> { - color + this + Colors.NORMAL - } - } - } - - /** - * Makes the given string cyan. - */ - @JvmStatic - fun String?.cyan(): String = colorize(Colors.CYAN) - - /** - * URL encodes the given string. - */ - @JvmStatic - fun String.encodeUrl(): String = UrlEncoderUtil.encode(this) - - /** - * Returns a property as an int. - */ - @JvmStatic - fun Properties.getIntProperty(key: String, defaultValue: Int): Int { - return getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue - } - - /** - * Makes the given string green. - */ - @JvmStatic - fun String?.green(): String = colorize(Colors.DARK_GREEN) - - /** - * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's - * nick. - */ - @JvmStatic - fun helpCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { - val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) - return text.replaceEach(searchFlags, replace) - } - - /** - * Returns a formatted help string. - */ - @JvmStatic - @JvmOverloads - fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { - val s = if (isBold) help.bold() else help - return if (isIndent) s.prependIndent() else s - } - - /** - * Returns `true` if the specified user is an operator on the [channel]. - */ - @JvmStatic - fun GenericMessageEvent.isChannelOp(channel: String): Boolean { - return this.bot().userChannelDao.getChannel(channel).isOp(this.user) - } - - /** - * Returns `true` if a HTTP status code indicates a successful response. - */ - @JvmStatic - fun Int.isHttpSuccess() = this in 200..399 - - /** - * Returns the last item of a list of strings or empty if none. - */ - @JvmStatic - fun List.lastOrEmpty(): String { - return if (this.size >= 2) { - this.last() - } else - "" - } - - /** - * Load serial data from file. - */ - @JvmStatic - fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any { - val serialFile = Paths.get(file) - if (serialFile.exists() && serialFile.fileSize() > 0) { - try { - ObjectInputStream( - BufferedInputStream(Files.newInputStream(serialFile)) - ).use { input -> - if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") - return input.readObject() - } - } catch (e: IOException) { - logger.error("An IO error occurred loading the ${description}.", e) - } catch (e: ClassNotFoundException) { - logger.error("An error occurred loading the ${description}.", e) - } - } - return default - } - - /** - * Returns `true` if the list does not contain the given string. - */ - @JvmStatic - fun List.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } - - /** - * Obfuscates the given string. - */ - @JvmStatic - fun String.obfuscate(): String { - return if (isNotBlank()) { - "x".repeat(length) - } else this - } - - /** - * Returns the plural form of a word, if count > 1. - */ - @JvmStatic - fun String.plural(count: Long): String { - return if (count > 1) "${this}s" else this - } - - /** - * Makes the given string red. - */ - @JvmStatic - fun String?.red(): String = colorize(Colors.RED) - - /** - * Replaces all occurrences of Strings within another String. - */ - @JvmStatic - fun String.replaceEach(search: Array, replace: Array): String { - var result = this - if (search.size == replace.size) { - search.forEachIndexed { i, s -> - result = result.replace(s, replace[i]) - } - } - return result - } - - /** - * Makes the given string reverse color. - */ - @JvmStatic - fun String?.reverseColor(): String = colorize(Colors.REVERSE) - - /** - * Save data - */ - @JvmStatic - fun saveSerialData(file: String, data: Any, logger: Logger, description: String) { - try { - BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> - ObjectOutputStream(bos).use { output -> - if (logger.isDebugEnabled) logger.debug("Saving the ${description}.") - output.writeObject(data) - } - } - } catch (e: IOException) { - logger.error("Unable to save the ${description}.", e) - } - } - - /** - * Send a formatted commands/modules, etc. list. - */ - @JvmStatic - @JvmOverloads - fun GenericMessageEvent.sendList( - list: List, - maxPerLine: Int, - separator: String = " ", - isBold: Boolean = false, - isIndent: Boolean = false - ) { - var i = 0 - while (i < list.size) { - sendMessage( - helpFormat( - list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), - isBold, - isIndent - ), - ) - i += maxPerLine - } - } - - /** - * Sends a [message]. - */ - @JvmStatic - fun GenericMessageEvent.sendMessage(channel: String, message: Message) { - if (message.isNotice) { - bot().sendIRC().notice(user.nick, message.msg.colorize(message.color)) - } else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) { - respondPrivateMessage(message.msg.colorize(message.color)) - } else { - bot().sendIRC().message(channel, message.msg.colorize(message.color)) - } - } - - /** - * Sends a response as a private message or notice. - */ - @JvmStatic - fun GenericMessageEvent.sendMessage(message: String) { - if (this is PrivateMessageEvent) { - respondPrivateMessage(message) - } else { - bot().sendIRC().notice(user.nick, message) - } - } - - /** - * Returns today's date. - */ - @JvmStatic - fun today(): String = LocalDateTime.now().toIsoLocalDate() - - /** - * Converts a string to an int. - */ - @JvmStatic - fun String.toIntOrDefault(defaultValue: Int): Int { - return try { - toInt() - } catch (e: NumberFormatException) { - defaultValue - } - } - - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun Date.toIsoLocalDate(): String { - return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toIsoLocalDate() - } - - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun LocalDateTime.toIsoLocalDate(): String = format(DateTimeFormatter.ISO_LOCAL_DATE) - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun Date.toUtcDateTime(): String { - return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toUtcDateTime() - } - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) - - /** - * Makes the given string bold. - */ - @JvmStatic - fun String?.underline(): String = colorize(Colors.UNDERLINE) - - - /** - * Converts XML/XHTML entities to plain text. - */ - @JvmStatic - fun String.unescapeXml(): String = Jsoup.parse(this).text() - - /** - * Reads contents of a URL. - */ - @JvmStatic - @Throws(IOException::class) - fun URL.reader(): UrlReaderResponse { - val connection = this.openConnection() as HttpURLConnection - connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" - ) - return if (connection.responseCode.isHttpSuccess()) { - UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) - } else { - UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) - } - } - - /** - * Holds the [URL.reader] response code and body text. - */ - data class UrlReaderResponse(val responseCode: Int, val body: String) -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt deleted file mode 100644 index 5f79472..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * AbstractCommand.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.pircbotx.hooks.events.PrivateMessageEvent -import org.pircbotx.hooks.types.GenericMessageEvent - -abstract class AbstractCommand { - abstract val name: String - abstract val help: List - abstract val isOpOnly: Boolean - abstract val isPublic: Boolean - abstract val isVisible: Boolean - - val properties: MutableMap = mutableMapOf() - - abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent) - - open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - if (!isOpOnly || isOpOnly == event.isChannelOp(channel)) { - for (h in help) { - event.sendMessage(helpCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic)) - } - return true - } - return false - } - - open fun initProperties(vararg keys: String) { - keys.forEach { - properties[it] = "" - } - } - - open fun isEnabled(): Boolean { - return true - } - - open fun matches(message: String): Boolean { - return false - } - - open fun setProperty(key: String, value: String) { - properties[key] = value - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt deleted file mode 100644 index 038e378..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * ChannelFeed.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.FeedReader -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - -class ChannelFeed(channel: String) : AbstractCommand() { - override val name = channel - override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel")) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val FEED_PROP = "feed" - } - - init { - initProperties(FEED_PROP) - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (isEnabled()) { - properties[FEED_PROP]?.let { FeedReader(it, event).run() } - } - } - - override fun isEnabled(): Boolean { - return !properties[FEED_PROP].isNullOrBlank() - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt b/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt deleted file mode 100644 index 9608ca8..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Cycle.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Cycle : AbstractCommand() { - private val wait = 10 - override val name = "cycle" - override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - with(event.bot()) { - if (event.isChannelOp(channel)) { - runBlocking { - launch { - sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!") - userChannelDao.getChannel(channel).send().part() - delay(wait * 1000L) - sendIRC().joinChannel(channel) - } - } - } else { - helpResponse(channel, args, event) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Die.kt b/bin/main/net/thauvin/erik/mobibot/commands/Die.kt deleted file mode 100644 index f271bfa..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Die.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Die.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Die : AbstractCommand() { - override val name = "die" - override val help = emptyList() - override val isOpOnly = true - override val isPublic = false - override val isVisible = false - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - with(event.bot()) { - if (event.isChannelOp(channel) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) { - sendIRC().message(channel, "${event.user?.nick} has just signed my death sentence.") - stopBotReconnect() - sendIRC().quitServer("The Bot is Out There!") - } - } - } - - companion object { - const val DIE_PROP = "die" - } - - init { - initProperties(DIE_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt b/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt deleted file mode 100644 index d083c10..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Ignore.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.links.LinksManager -import org.pircbotx.hooks.types.GenericMessageEvent - -class Ignore : AbstractCommand() { - private val me = "me" - - init { - initProperties(IGNORE_PROP) - } - - override val name = IGNORE_CMD - override val help = listOf( - "To ignore a link posted to the channel:", - helpFormat("https://www.foo.bar %n"), - "To check your ignore status:", - helpFormat("%c $name"), - "To toggle your ignore status:", - helpFormat("%c $name $me") - ) - private val helpOp = help.plus( - arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name [ ...]")) - ) - - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val IGNORE_CMD = "ignore" - const val IGNORE_PROP = IGNORE_CMD - private val ignored = mutableSetOf() - - @JvmStatic - fun isNotIgnored(nick: String): Boolean { - return !ignored.contains(nick.lowercase()) - } - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val isMe = args.trim().equals(me, true) - if (isMe || !event.isChannelOp(channel)) { - val nick = event.user.nick.lowercase() - ignoreNick(nick, isMe, event) - } else { - ignoreOp(args, event) - } - } - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - return if (event.isChannelOp(channel)) { - for (h in helpOp) { - event.sendMessage(helpCmdSyntax(h, event.bot().nick, true)) - } - true - } else { - super.helpResponse(channel, topic, event) - } - } - - private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) { - if (isMe) { - if (ignored.remove(sender)) { - event.sendMessage("You are no longer ignored.") - } else { - ignored.add(sender) - event.sendMessage("You are now ignored.") - } - } else { - if (ignored.contains(sender)) { - event.sendMessage("You are currently ignored.") - } else { - event.sendMessage("You are not currently ignored.") - } - } - } - - private fun ignoreOp(args: String, event: GenericMessageEvent) { - if (args.isNotEmpty()) { - val nicks = args.lowercase().split(" ") - for (nick in nicks) { - val ignore = if (me == nick) { - nick.lowercase() - } else { - nick - } - if (!ignored.remove(ignore)) { - ignored.add(ignore) - } - } - } - - if (ignored.isNotEmpty()) { - event.sendMessage("The following nicks are ignored:") - event.sendList(ignored.sorted(), 8, isIndent = true) - } else { - event.sendMessage("No one is currently ${"ignored".bold()}.") - } - } - - override fun setProperty(key: String, value: String) { - super.setProperty(key, value) - if (IGNORE_PROP == key) { - ignored.addAll(value.split(LinksManager.TAG_MATCH)) - } - } - -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Info.kt b/bin/main/net/thauvin/erik/mobibot/commands/Info.kt deleted file mode 100644 index ed0b6ef..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Info.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Info.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.ReleaseInfo -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.green -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.plural -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.links.LinksManager -import net.thauvin.erik.mobibot.commands.seen.Seen -import net.thauvin.erik.mobibot.commands.tell.Tell -import org.pircbotx.hooks.types.GenericMessageEvent -import java.lang.management.ManagementFactory -import kotlin.time.DurationUnit -import kotlin.time.toDuration - -class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { - private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", - "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" - ) - override val name = "info" - override val help = listOf("To view information about the bot:", helpFormat("%c $name")) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - /** - * Converts milliseconds to year month week day hour and minutes. - */ - @JvmStatic - fun Long.toUptime(): String { - this.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, seconds, _ -> - val years = wholeDays / 365 - var days = wholeDays % 365 - val months = days / 30 - days %= 30 - val weeks = days / 7 - days %= 7 - - with(StringBuffer()) { - if (years > 0) { - append(years).append(" year".plural(years)).append(' ') - } - if (months > 0) { - append(months).append(" month".plural(months)).append(' ') - } - if (weeks > 0) { - append(weeks).append(" week".plural(weeks)).append(' ') - } - if (days > 0) { - append(days).append(" day".plural(days)).append(' ') - } - if (hours > 0) { - append(hours).append(" hour".plural(hours.toLong())).append(' ') - } - - if (minutes > 0) { - append(minutes).append(" minute".plural(minutes.toLong())) - } else { - append(seconds).append(" second".plural(seconds.toLong())) - } - - return toString() - } - } - } - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - event.sendList(allVersions, 1) - val info = StringBuilder() - info.append("Uptime: ") - .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) - .append(" [Entries: ") - .append(LinksManager.entries.links.size) - if (seen.isEnabled()) { - info.append(", Seen: ").append(seen.count()) - } - if (event.isChannelOp(channel)) { - if (tell.isEnabled()) { - info.append(", Messages: ").append(tell.size()) - } - if (LinksManager.socialManager.entriesCount() > 0) { - info.append(", Social: ").append(LinksManager.socialManager.entriesCount()) - } - } - info.append(", Recap: ").append(Recap.recaps.size).append(']') - event.sendMessage(info.toString()) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Me.kt b/bin/main/net/thauvin/erik/mobibot/commands/Me.kt deleted file mode 100644 index ec7823b..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Me.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Me.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Me : AbstractCommand() { - override val name = "me" - override val help = listOf("To have the bot perform an action:", helpFormat("%c $name ")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.bot().sendIRC().action(channel, args) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt b/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt deleted file mode 100644 index b2293b0..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Modules.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendList -import org.pircbotx.hooks.types.GenericMessageEvent - -class Modules(private val modules: List, private val disabledModules: List) : AbstractCommand() { - override val name = "modules" - override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - if (modules.isEmpty()) { - event.respondPrivateMessage("There are no enabled modules.") - } else { - event.respondPrivateMessage("The enabled modules are: ") - event.sendList(modules, 7, isIndent = true) - } - if (disabledModules.isNotEmpty()) { - event.respondPrivateMessage("The disabled modules are: ") - event.sendList(disabledModules, 7, isIndent = true) - } - } else { - helpResponse(channel, args, event) - } - } -} - diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt b/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt deleted file mode 100644 index 20a6635..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Msg.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Msg : AbstractCommand() { - override val name = "msg" - override val help = listOf( - "To have the bot send a private message to someone:", - helpFormat("%c $name ") - ) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - val msg = args.split(" ", limit = 2) - if (args.length > 2) { - event.bot().sendIRC().message(msg[0], msg[1]) - event.respondPrivateMessage("A message was sent to ${msg[0]}") - } else { - helpResponse(channel, args, event) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt b/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt deleted file mode 100644 index 85a03ab..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Nick.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Nick : AbstractCommand() { - override val name = "nick" - override val help = listOf("To change the bot's nickname:", helpFormat("%c $name ")) - override val isOpOnly = true - override val isPublic = true - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.bot().sendIRC().changeNick(args) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt b/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt deleted file mode 100644 index 77154c7..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Recap.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.toUtcDateTime -import org.pircbotx.hooks.types.GenericMessageEvent -import java.time.Clock -import java.time.LocalDateTime - -class Recap : AbstractCommand() { - override val name = "recap" - override val help = listOf( - "To list the last 10 public channel messages:", - helpFormat("%c $name") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val MAX_RECAPS = 10 - - @JvmField - val recaps = mutableListOf() - - /** - * Stores the last 10 public messages and actions. - */ - @JvmStatic - fun storeRecap(sender: String, message: String, isAction: Boolean) { - recaps.add( - LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() - + " - $sender" + (if (isAction) " " else ": ") + message - ) - if (recaps.size > MAX_RECAPS) { - recaps.removeFirst() - } - } - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (recaps.isNotEmpty()) { - for (r in recaps) { - event.sendMessage(r) - } - } else { - event.sendMessage("Sorry, nothing to recap.") - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Say.kt b/bin/main/net/thauvin/erik/mobibot/commands/Say.kt deleted file mode 100644 index 7f76d35..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Say.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Say.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Say : AbstractCommand() { - override val name = "say" - override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name ")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.bot().sendIRC().message(channel, args) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Users.kt b/bin/main/net/thauvin/erik/mobibot/commands/Users.kt deleted file mode 100644 index 33d6fef..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Users.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Users.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendList -import org.pircbotx.hooks.types.GenericMessageEvent - -class Users : AbstractCommand() { - override val name = "users" - override val help = listOf("To list the users present on the channel:", helpFormat("%c $name")) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val ch = event.bot().userChannelDao.getChannel(channel) - event.sendList(ch.users.map { if (it.channelsOpIn.contains(ch)) "@${it.nick}" else it.nick }, 8) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt b/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt deleted file mode 100644 index 896c569..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Versions.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.commands - -import net.thauvin.erik.mobibot.ReleaseInfo -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import org.pircbotx.PircBotX -import org.pircbotx.hooks.types.GenericMessageEvent - -class Versions : AbstractCommand() { - private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", - "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + - ", JVM ${System.getProperty("java.runtime.version")}", - "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" - ) - override val name = "versions" - override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.sendList(allVersions, 1) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt deleted file mode 100644 index 1443d44..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Comment.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.links - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import org.pircbotx.hooks.types.GenericMessageEvent - -class Comment : AbstractCommand() { - override val name = COMMAND - override val help = listOf( - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, use its label: ", - helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), - "To delete a comment, use its label and a minus sign: ", - helpFormat("${Constants.LINK_CMD}1.1:-") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val COMMAND = "comment" - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val cmds = args.substring(1).split("[.:]".toRegex(), 3) - val entryIndex = cmds[0].toInt() - 1 - - if (entryIndex < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { - val entry: EntryLink = LinksManager.entries.links[entryIndex] - val commentIndex = cmds[1].toInt() - 1 - if (commentIndex < entry.comments.size) { - when (val cmd = cmds[2].trim()) { - "" -> showComment(entry, entryIndex, commentIndex, event) // L1.1: - "-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:- - else -> { - if (cmd.startsWith('?')) { // L1.1:? - changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event) - } else { // L1.1: - setComment(cmd, entry, entryIndex, commentIndex, event) - } - } - } - } - } - } - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - if (super.helpResponse(channel, topic, event)) { - if (event.isChannelOp(channel)) { - event.sendMessage("To change a comment's author:") - event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?")) - } - return true - } - return false - } - - override fun matches(message: String): Boolean { - return message.matches("^${Constants.LINK_CMD}\\d+\\.\\d+:.*".toRegex()) - } - - private fun changeAuthor( - channel: String, - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent - ) { - if (event.isChannelOp(channel) && cmd.length > 1) { - val comment = entry.getComment(commentIndex) - comment.nick = cmd.substring(1) - event.sendMessage(printComment(entryIndex, commentIndex, comment)) - LinksManager.entries.save() - } else { - event.sendMessage("Please ask a channel op to change the author of this comment for you.") - } - } - - private fun deleteComment( - channel: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent - ) { - if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { - entry.deleteComment(commentIndex) - event.sendMessage("Comment ${entryIndex.toLinkLabel()}.${commentIndex + 1} removed.") - LinksManager.entries.save() - } else { - event.sendMessage("Please ask a channel op to delete this comment for you.") - } - } - - private fun setComment( - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent - ) { - entry.setComment(commentIndex, cmd, event.user.nick) - event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) - LinksManager.entries.save() - } - - private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) { - event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt deleted file mode 100644 index fba6b99..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ /dev/null @@ -1,207 +0,0 @@ -/* - * LinksManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.links - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Pinboard -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.today -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored -import net.thauvin.erik.mobibot.entries.Entries -import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.social.SocialManager -import org.jsoup.Jsoup -import org.pircbotx.hooks.types.GenericMessageEvent -import java.io.IOException - -class LinksManager : AbstractCommand() { - private val defaultTags: MutableList = mutableListOf() - private val keywords: MutableList = mutableListOf() - - override val name = Constants.LINK_CMD - override val help = emptyList() - override val isOpOnly = false - override val isPublic = false - override val isVisible = false - - init { - initProperties(TAGS_PROP, KEYWORDS_PROP) - } - - companion object { - val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex() - const val KEYWORDS_PROP = "tags-keywords" - const val TAGS_PROP = "tags" - val TAG_MATCH = ", *| +".toRegex() - - /** - * Entries array - */ - @JvmField - val entries = Entries() - - /** - * Pinboard handler. - */ - @JvmField - val pinboard = Pinboard() - - /** - * Social Manager handler. - */ - @JvmField - val socialManager = SocialManager() - - /** - * Let the user know if the entries are too old to be modified. - */ - @JvmStatic - fun isUpToDate(event: GenericMessageEvent): Boolean { - if (entries.lastPubDate != today()) { - event.sendMessage("The links are too old to be updated.") - return false - } - return true - } - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val cmds = args.split(" ".toRegex(), 2) - val sender = event.user.nick - val botNick = event.bot().nick - val login = event.user.login - - if (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) { - val link = cmds[0].trim() - if (!isDupEntry(link, event)) { - var title = "" - val tags = ArrayList(defaultTags) - if (cmds.size == 2) { - val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) - title = data[0].trim() - if (data.size > 1) { - tags.addAll(data[1].split(TAG_MATCH)) - } - } - - if (title.isBlank()) { - title = fetchTitle(link) - } - - if (title != Constants.NO_TITLE) { - matchTagKeywords(title, tags) - } - - // Links are old, clear them - if (entries.lastPubDate != today()) { - entries.links.clear() - } - - val entry = EntryLink(link, title, sender, login, channel, tags) - entries.links.add(entry) - val index = entries.links.lastIndexOf(entry) - event.sendMessage(printLink(index, entry)) - - pinboard.addPin(event.bot().serverHostname, entry) - - // Queue link for posting to social media. - socialManager.queueEntry(index) - - entries.save() - - if (Constants.NO_TITLE == entry.title) { - event.sendMessage("Please specify a title, by typing:") - event.sendMessage(helpFormat("${index.toLinkLabel()}:|This is the title")) - } - } - } - } - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false - - override fun matches(message: String): Boolean { - return message.matches(LINK_MATCH) - } - - internal fun fetchTitle(link: String): String { - try { - val html = Jsoup.connect(link) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") - .get() - val title = html.title() - if (title.isNotBlank()) { - return title - } - } catch (ignore: IOException) { - // Do nothing - } - return Constants.NO_TITLE - } - - private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean { - synchronized(entries) { - return try { - val match = entries.links.single { it.link == link } - event.sendMessage( - "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) - ) - true - } catch (ignore: NoSuchElementException) { - false - } - } - } - - internal fun matchTagKeywords(title: String, tags: MutableList) { - for (match in keywords) { - val m = Regex.escape(match) - if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { - tags.add(match) - } - } - } - - override fun setProperty(key: String, value: String) { - super.setProperty(key, value) - if (KEYWORDS_PROP == key) { - keywords.addAll(value.split(TAG_MATCH)) - } else if (TAGS_PROP == key) { - defaultTags.addAll(value.split(TAG_MATCH)) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt deleted file mode 100644 index ff4278d..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Posting.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.links - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.LinksManager.Companion.entries -import net.thauvin.erik.mobibot.entries.EntriesUtils -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import org.pircbotx.hooks.types.GenericMessageEvent - -class Posting : AbstractCommand() { - override val name = "posting" - override val help = listOf( - "Post a URL, by saying it on a line on its own:", - helpFormat(" [] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", - "To add a title, use its label and a pipe:", - helpFormat("${Constants.LINK_CMD}1:|This is the title"), - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, see: ", - helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val cmds = args.substring(1).split(":", limit = 2) - val entryIndex = cmds[0].toInt() - 1 - - if (entryIndex < entries.links.size) { - val cmd = cmds[1].trim() - if (cmd.isBlank()) { - showEntry(entryIndex, event) // L1: - } else if (LinksManager.isUpToDate(event)) { - if (cmd == "-") { - removeEntry(channel, entryIndex, event) // L1:- - } else { - when (cmd[0]) { - '|' -> changeTitle(cmd, entryIndex, event) // L1:|<title> - '=' -> changeUrl(channel, cmd, entryIndex, event) // L1:=<url> - '?' -> changeAuthor(channel, cmd, entryIndex, event) // L1:?<author> - else -> addComment(cmd, entryIndex, event) // L1:<comment> - } - } - } - } - } - - override fun matches(message: String): Boolean { - return message.matches("${Constants.LINK_CMD}\\d+:.*".toRegex()) - } - - private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) { - val entry: EntryLink = entries.links[entryIndex] - val commentIndex = entry.addComment(cmd, event.user.nick) - val comment = entry.getComment(commentIndex) - event.sendMessage(EntriesUtils.printComment(entryIndex, commentIndex, comment)) - entries.save() - } - - private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) { - if (cmd.length > 1) { - val entry: EntryLink = entries.links[entryIndex] - entry.title = cmd.substring(1).trim() - LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) - entries.save() - } - } - - private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) { - val entry: EntryLink = entries.links[entryIndex] - if (entry.login == event.user.login || event.isChannelOp(channel)) { - val link = cmd.substring(1) - if (link.matches(LinksManager.LINK_MATCH)) { - val oldLink = entry.link - entry.link = link - LinksManager.pinboard.updatePin(event.bot().serverHostname, oldLink, entry) - event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) - entries.save() - } - } - } - - private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - if (cmd.length > 1) { - val entry: EntryLink = entries.links[index] - entry.nick = cmd.substring(1) - LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.printLink(index, entry)) - entries.save() - } - } else { - event.sendMessage("Please ask a channel op to change the author of this link for you.") - } - } - - private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) { - val entry: EntryLink = entries.links[index] - if (entry.login == event.user.login || event.isChannelOp(channel)) { - LinksManager.pinboard.deletePin(entry) - LinksManager.socialManager.removeEntry(index) - entries.links.removeAt(index) - event.sendMessage("Entry ${index.toLinkLabel()} removed.") - entries.save() - } else { - event.sendMessage("Please ask a channel op to remove this entry for you.") - } - } - - private fun showEntry(index: Int, event: GenericMessageEvent) { - val entry: EntryLink = entries.links[index] - event.sendMessage(EntriesUtils.printLink(index, entry)) - if (entry.tags.isNotEmpty()) { - event.sendMessage(EntriesUtils.printTags(index, entry)) - } - if (entry.comments.isNotEmpty()) { - val comments = entry.comments - for (i in comments.indices) { - event.sendMessage(EntriesUtils.printComment(index, i, comments[i])) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt deleted file mode 100644 index 1662857..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Tags.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.links - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.entries.EntriesUtils -import net.thauvin.erik.mobibot.entries.EntryLink -import org.pircbotx.hooks.types.GenericMessageEvent - -class Tags : AbstractCommand() { - override val name = COMMAND - override val help = listOf( - "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", - helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val COMMAND = "tags" - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2) - val index = cmds[0].toInt() - 1 - - if (index < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { - val cmd = cmds[1].trim() - val entry: EntryLink = LinksManager.entries.links[index] - if (cmd.isNotEmpty()) { - if (entry.login == event.user.login || event.isChannelOp(channel)) { - entry.setTags(cmd) - LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.printTags(index, entry)) - LinksManager.entries.save() - } else { - event.sendMessage("Please ask a channel op to change the tags for you.") - } - } else { - if (entry.tags.isNotEmpty()) { - event.sendMessage(EntriesUtils.printTags(index, entry)) - } else { - event.sendMessage("The entry has no tags. Why don't add some?") - } - } - } - } - - override fun matches(message: String): Boolean { - return message.matches("^${Constants.LINK_CMD}\\d+${Constants.TAG_CMD}:.*".toRegex()) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt deleted file mode 100644 index 825e374..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * View.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.links - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.lastOrEmpty -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.LinksManager.Companion.entries -import net.thauvin.erik.mobibot.entries.EntriesUtils -import net.thauvin.erik.mobibot.entries.EntryLink -import org.pircbotx.hooks.events.PrivateMessageEvent -import org.pircbotx.hooks.types.GenericMessageEvent - -class View : AbstractCommand() { - override val name = VIEW_CMD - override val help = listOf( - "To list or search the current URL posts:", - helpFormat("%c $name [<start>] [<query>]") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val MAX_ENTRIES = 6 - const val VIEW_CMD = "view" - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (entries.links.isNotEmpty()) { - val p = parseArgs(args) - showPosts(p.first, p.second, event) - } else { - event.sendMessage("There is currently nothing to view. Why don't you post something?") - } - } - - internal fun parseArgs(args: String): Pair<Int, String> { - var query = args.lowercase().trim() - var start = 0 - if (query.isEmpty() && entries.links.size > MAX_ENTRIES) { - start = entries.links.size - MAX_ENTRIES - } - if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>] - val split = query.split(" ", limit = 2) - try { - start = split[0].toInt() - 1 - query = split.lastOrEmpty().trim() - if (start > entries.links.size) { - start = 0 - } - } catch (ignore: NumberFormatException) { - // Do nothing - } - } - return Pair(start, query) - } - - private fun showPosts(start: Int, query: String, event: GenericMessageEvent) { - var index = start - var entry: EntryLink - var sent = 0 - while (index < entries.links.size && sent < MAX_ENTRIES) { - entry = entries.links[index] - if (query.isNotBlank()) { - if (entry.matches(query)) { - event.sendMessage(EntriesUtils.printLink(index, entry, true)) - sent++ - } - } else { - event.sendMessage(EntriesUtils.printLink(index, entry, true)) - sent++ - } - index++ - if (sent == MAX_ENTRIES && index < entries.links.size) { - event.sendMessage("To view more, try: ") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) - ) - ) - } - } - if (sent == 0) { - event.sendMessage("No matches. Please try again.") - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt deleted file mode 100644 index cfd2c27..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * NickComparator.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.seen - -import java.io.Serializable - -class NickComparator : Comparator<String>, Serializable { - override fun compare(a: String, b: String): Int { - return a.lowercase().compareTo(b.lowercase()) - } - - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt deleted file mode 100644 index c9ee0f3..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Seen.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.seen - -import com.google.common.collect.ImmutableSortedSet -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.loadSerialData -import net.thauvin.erik.mobibot.Utils.saveSerialData -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime -import org.pircbotx.User -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.util.* - - -class Seen(private val serialObject: String) : AbstractCommand() { - private val logger: Logger = LoggerFactory.getLogger(Seen::class.java) - private val allKeyword = "all" - val seenNicks = TreeMap<String, SeenNick>(NickComparator()) - - override val name = "seen" - override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) - private val helpOp = help.plus( - arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (isEnabled()) { - if (args.isNotBlank() && !args.contains(' ')) { - val ch = event.bot().userChannelDao.getChannel(channel) - if (args == allKeyword && ch.isOp(event.user) && seenNicks.isNotEmpty()) { - event.sendMessage("The ${"seen".bold()} nicks are:") - event.sendList(seenNicks.keys.toList(), 7, separator = ", ", isIndent = true) - return - } - ch.users.forEach { - if (args.equals(it.nick, true)) { - event.sendMessage("${it.nick} is on ${channel}.") - return - } - } - if (seenNicks.containsKey(args)) { - val seenNick = seenNicks.getValue(args) - val lastSeen = System.currentTimeMillis() - seenNick.lastSeen - event.sendMessage("${seenNick.nick} was last seen on $channel ${lastSeen.toUptime()} ago.") - return - } - event.sendMessage("I haven't seen $args on $channel lately.") - } else { - helpResponse(channel, args, event) - } - } - } - - fun add(nick: String) { - if (isEnabled()) { - seenNicks[nick] = SeenNick(nick, System.currentTimeMillis()) - save() - } - } - - fun add(users: ImmutableSortedSet<User>) { - if (isEnabled()) { - users.forEach { - seenNicks[it.nick] = SeenNick(it.nick, System.currentTimeMillis()) - } - save() - } - } - - fun clear() { - seenNicks.clear() - } - - fun count(): Int = seenNicks.size - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - return if (event.isChannelOp(channel)) { - for (h in helpOp) { - event.sendMessage(Utils.helpCmdSyntax(h, event.bot().nick, true)) - } - true - } else { - super.helpResponse(channel, topic, event) - } - } - - fun load() { - if (isEnabled()) { - @Suppress("UNCHECKED_CAST") - seenNicks.putAll( - loadSerialData( - serialObject, - TreeMap<String, SeenNick>(), - logger, - "seen nicknames" - ) as TreeMap<String, SeenNick> - ) - } - } - - fun save() { - saveSerialData(serialObject, seenNicks, logger, "seen nicknames") - } - - init { - load() - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt deleted file mode 100644 index 7924977..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SeenNick.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.seen - -import java.io.Serializable - -data class SeenNick(val nick: String, val lastSeen: Long) : Serializable { - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt deleted file mode 100644 index 061ca6a..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Tell.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.commands.tell - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.plural -import net.thauvin.erik.mobibot.Utils.reverseColor -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.toIntOrDefault -import net.thauvin.erik.mobibot.Utils.toUtcDateTime -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.View -import org.pircbotx.PircBotX -import org.pircbotx.hooks.events.MessageEvent -import org.pircbotx.hooks.types.GenericMessageEvent -import org.pircbotx.hooks.types.GenericUserEvent - -/** - * The `Tell` command. - */ -class Tell(private val serialObject: String) : AbstractCommand() { - // Messages queue - private val messages: MutableList<TellMessage> = mutableListOf() - - // Maximum number of days to keep messages - private var maxDays = 7 - - // Message maximum queue size - private var maxSize = 50 - - /** - * The tell command. - */ - override val name = "tell" - - override val help = listOf( - "To send a message to someone when they join the channel:", - helpFormat("%c $name <nick> <message>"), - "To view queued and sent messages:", - helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' - ) - override val isOpOnly: Boolean = false - override val isPublic: Boolean = isEnabled() - override val isVisible: Boolean = isEnabled() - - /** - * Cleans the messages queue. - */ - private fun clean(): Boolean { - return TellManager.clean(messages, maxDays.toLong()) - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (isEnabled()) { - when { - args.isBlank() -> { - helpResponse(channel, args, event) - } - - args.startsWith(View.VIEW_CMD) -> { - if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { - viewAll(event) - } else { - viewMessages(event) - } - } - - args.startsWith("$TELL_DEL_KEYWORD ") -> { - deleteMessage(channel, args, event) - } - - else -> { - newMessage(channel, args, event) - } - } - if (clean()) { - save() - } - } - } - - // Delete message. - private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) { - val split = args.split(" ") - if (split.size == 2) { - val id = split[1] - if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { - if (messages.removeIf { it.sender.equals(event.user.nick, true) && it.isReceived }) { - save() - event.sendMessage("Delivered messages have been deleted.") - } else { - event.sendMessage("No delivered messages were found.") - } - } else { - if (messages.removeIf { - it.id == id && - (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) - }) { - save() - event.sendMessage("The message was deleted from the queue.") - } else { - event.sendMessage("The specified message [ID $id] could not be found.") - } - } - } else { - helpResponse(channel, args, event) - } - } - - override fun isEnabled(): Boolean { - return maxSize > 0 && maxDays > 0 - } - - override fun setProperty(key: String, value: String) { - super.setProperty(key, value) - if (MAX_DAYS_PROP == key) { - maxDays = value.toIntOrDefault(maxDays) - } else if (MAX_SIZE_PROP == key) { - maxSize = value.toIntOrDefault(maxSize) - } - } - - // New message. - private fun newMessage(channel: String, args: String, event: GenericMessageEvent) { - val split = args.split(" ".toRegex(), 2) - if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) { - if (messages.size < maxSize) { - val message = TellMessage(event.user.nick, split[0], split[1].trim()) - messages.add(message) - save() - event.sendMessage("Message [ID ${message.id}] was queued for ${message.recipient.bold()}") - } else { - event.sendMessage("Sorry, the messages queue is currently full.") - } - } else { - helpResponse(channel, args, event) - } - } - - /** - * Saves the messages queue. - */ - private fun save() { - TellManager.save(serialObject, messages) - } - - /** - * Checks and sends messages. - */ - fun send(event: GenericUserEvent) { - val nickname = event.user.nick - if (isEnabled() && nickname != event.getBot<PircBotX>().nick) { - messages.filter { it.isMatch(nickname) }.forEach { message -> - if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { - if (message.sender == nickname) { - if (event !is MessageEvent) { - event.user.send().message( - "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" - ) - message.isReceived = true - message.isNotified = true - save() - } - } else { - event.user.send().message( - "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" - ) - message.isReceived = true - save() - } - } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified - ) { - event.user.send().message( - "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " - + "${message.recipient.bold()} on ${message.receptionDate}" - ) - message.isNotified = true - save() - } - } - } - } - - /** - * Returns the messages queue size. - * - * @return The size. - */ - fun size(): Int = messages.size - - // View all messages. - private fun viewAll(event: GenericMessageEvent) { - if (messages.isNotEmpty()) { - for (message in messages) { - event.sendMessage( - "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + - (if (message.isReceived) "DELIVERED]" else "QUEUED]") - ) - } - } else { - event.sendMessage("There are no messages in the queue.") - } - } - - // View messages. - private fun viewMessages(event: GenericMessageEvent) { - var hasMessage = false - for (message in messages.filter { it.isMatch(event.user.nick) }) { - if (!hasMessage) { - hasMessage = true - event.sendMessage("Here are your messages: ") - } - if (message.isReceived) { - event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" - ) - } else { - event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" - ) - } - event.sendMessage(helpFormat(message.message)) - } - if (!hasMessage) { - event.sendMessage("You have no messages in the queue.") - } else { - event.sendMessage("To delete one or all delivered messages:") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) - ) - ) - event.sendMessage(help.last()) - } - } - - companion object { - /** - * Max days property. - */ - const val MAX_DAYS_PROP = "tell-max-days" - - /** - * Max size property. - */ - const val MAX_SIZE_PROP = "tell-max-size" - - // Arrow - private const val ARROW = " --> " - - // All keyword - private const val TELL_ALL_KEYWORD = "all" - - //T he delete command. - private const val TELL_DEL_KEYWORD = "del" - } - - /** - * Creates a new instance. - */ - init { - initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP) - - // Load the message queue - messages.addAll(TellManager.load(serialObject)) - if (clean()) { - save() - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt deleted file mode 100644 index b65a4da..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * TellManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.commands.tell - -import net.thauvin.erik.mobibot.Utils.loadSerialData -import net.thauvin.erik.mobibot.Utils.saveSerialData -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.time.Clock -import java.time.LocalDateTime - -/** - * The Tell Messages Manager. - */ -object TellManager { - private val logger: Logger = LoggerFactory.getLogger(TellManager::class.java) - - /** - * Cleans the messages queue. - */ - @JvmStatic - fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean { - if (logger.isDebugEnabled) logger.debug("Cleaning the messages.") - val today = LocalDateTime.now(Clock.systemUTC()) - return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) } - } - - /** - * Loads the messages. - */ - @JvmStatic - fun load(file: String): List<TellMessage> { - @Suppress("UNCHECKED_CAST") - return loadSerialData(file, emptyList<TellMessage>(), logger, "message queue") as List<TellMessage> - } - - /** - * Saves the messages. - */ - @JvmStatic - fun save(file: String, messages: List<TellMessage?>?) { - if (messages != null) { - saveSerialData(file, messages, logger, "messages") - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt deleted file mode 100644 index d17fbb5..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * TellMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.commands.tell - -import java.io.Serializable -import java.time.Clock -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter - -/** - * Tell Message. - */ -class TellMessage( - /** - * Returns the message's sender. - */ - val sender: String, - - /** - * Returns the message's recipient. - */ - val recipient: String, - - /** - * Returns the message text. - */ - val message: String -) : Serializable { - /** - * Returns the queued date/time. - */ - var queued: LocalDateTime = LocalDateTime.now(Clock.systemUTC()) - - /** - * Returns the message id. - */ - var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) - - /** - * Returns `true` if a notification was sent. - */ - var isNotified = false - - /** - * Returns `true` if the message was received. - */ - var isReceived = false - set(value) { - if (value) { - receptionDate = LocalDateTime.now(Clock.systemUTC()) - } - field = value - } - - /** - * Returns the message creating date. - */ - var receptionDate: LocalDateTime = LocalDateTime.MIN - - /** - * Matches the message sender or recipient. - */ - fun isMatch(nick: String?): Boolean { - return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true) - } - - override fun toString(): String { - return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " + - "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") - } - - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 2L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt b/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt deleted file mode 100644 index e8676ec..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Entries.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries - -import net.thauvin.erik.mobibot.Utils.today - -class Entries( - var channel: String = "", - var ircServer: String = "", - var logsDir: String = "", - var backlogs: String = "" -) { - val links = mutableListOf<EntryLink>() - - var lastPubDate = today() - - fun load() { - lastPubDate = FeedsManager.loadFeed(this) - } - - fun save() { - lastPubDate = today() - FeedsManager.saveFeed(this) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt deleted file mode 100644 index 9c09626..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * EntriesUtils.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.entries - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.green - -/** - * Entries utilities. - */ -object EntriesUtils { - /** - * Prints an entry's comment for display on the channel. - */ - @JvmStatic - fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") - - /** - * Prints an entry's link for display on the channel. - */ - @JvmStatic - @JvmOverloads - fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { - val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") - .append('[').append(entry.nick).append(']') - if (isView && entry.comments.isNotEmpty()) { - buff.append("[+").append(entry.comments.size).append(']') - } - buff.append(' ') - with(entry) { - if (Constants.NO_TITLE == title) { - buff.append(title) - } else { - buff.append(title.bold()) - } - buff.append(" ( ").append(link.green()).append(" )") - } - return buff.toString() - } - - /** - * Prints an entry's tags/categories for display on the channel. e.g. L1T: tag1, tag2 - */ - @JvmStatic - fun printTags(entryIndex: Int, entry: EntryLink): String = - entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") - - /** - * Builds link label based on its index. e.g: L1 - */ - @JvmStatic - fun Int.toLinkLabel(): String = Constants.LINK_CMD + (this + 1) -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt deleted file mode 100644 index e18d692..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * EntryComment.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.entries - -import java.io.Serializable -import java.time.LocalDateTime - -/** - * Entry comments data class. - */ -data class EntryComment(var comment: String, var nick: String) : Serializable { - /** - * Creation date. - */ - val date: LocalDateTime = LocalDateTime.now() - - override fun toString(): String = "EntryComment{comment='$comment', date=$date, nick='$nick'}" - - companion object { - // Serial version UID - @Suppress("ConstPropertyName") - private const val serialVersionUID: Long = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt deleted file mode 100644 index 4a69446..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ /dev/null @@ -1,213 +0,0 @@ -/* - * EntryLink.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.entries - -import com.rometools.rome.feed.synd.SyndCategory -import com.rometools.rome.feed.synd.SyndCategoryImpl -import net.thauvin.erik.mobibot.commands.links.LinksManager -import java.io.Serializable -import java.util.* - -/** - * The class used to store link entries. - */ -class EntryLink( - // Link's comments - val comments: MutableList<EntryComment> = mutableListOf(), - - // Tags/categories - val tags: MutableList<SyndCategory> = mutableListOf(), - - // Channel - var channel: String, - - // Creation date - var date: Date = Calendar.getInstance().time, - - // Link's URL - var link: String, - - // Author's login - var login: String = "", - - // Author's nickname - var nick: String, - - // Link's title - var title: String -) : Serializable { - /** - * Creates a new entry. - */ - constructor( - link: String, - title: String, - nick: String, - login: String, - channel: String, - tags: List<String?> - ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { - setTags(tags) - } - - /** - * Creates a new entry. - */ - constructor( - link: String, - title: String, - nick: String, - channel: String, - date: Date, - tags: List<SyndCategory> - ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { - this.tags.addAll(tags) - } - - /** - * Adds a new comment - */ - fun addComment(comment: EntryComment): Int { - comments.add(comment) - return comments.lastIndex - } - - /** - * Adds a new comment. - */ - fun addComment(comment: String, nick: String): Int { - return addComment(EntryComment(comment, nick)) - } - - /** - * Deletes a specific comment. - */ - fun deleteComment(index: Int): Boolean { - if (index < comments.size) { - comments.removeAt(index) - return true - } - return false - } - - /** - * Deletes a comment. - */ - fun deleteComment(entryComment: EntryComment): Boolean { - return comments.remove(entryComment) - } - - /** - * Formats the tags. - */ - fun formatTags(sep: String, prefix: String = ""): String { - return tags.joinToString(separator = sep, prefix = prefix) { it.name } - } - - /** - * Returns a comment. - */ - fun getComment(index: Int): EntryComment = comments[index] - - /** - * Returns true if a string is contained in the link, title, or nick. - */ - fun matches(match: String?): Boolean { - return if (match.isNullOrEmpty()) { - false - } else { - link.contains(match, true) || title.contains(match, true) || nick.contains(match, true) - } - } - - /** - * Sets a comment. - */ - fun setComment(index: Int, comment: String?, nick: String?) { - if (index < comments.size && !comment.isNullOrBlank() && !nick.isNullOrBlank()) { - comments[index] = EntryComment(comment, nick) - } - } - - /** - * Sets the tags. - */ - fun setTags(tags: String) { - setTags(tags.split(LinksManager.TAG_MATCH)) - } - - /** - * Sets the tags. - */ - private fun setTags(tags: List<String?>) { - if (tags.isNotEmpty()) { - var category: SyndCategoryImpl - for (tag in tags) { - if (!tag.isNullOrBlank()) { - val t = tag.lowercase() - val mod = t[0] - if (mod == '-') { - // Don't remove the channel tag - if (channel.substring(1) != t.substring(1)) { - category = SyndCategoryImpl() - category.name = t.substring(1) - this.tags.remove(category) - } - } else { - category = SyndCategoryImpl() - if (mod == '+') { - category.name = t.substring(1) - } else { - category.name = t - } - if (!this.tags.contains(category)) { - this.tags.add(category) - } - } - } - } - } - } - - /** - * Returns a string representation of the object. - */ - override fun toString(): String { - return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," + - "nick='$nick', tags=$tags, title='$title'}") - } - - companion object { - // Serial version UID - @Suppress("ConstPropertyName") - private const val serialVersionUID: Long = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt deleted file mode 100644 index f786cb2..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ /dev/null @@ -1,187 +0,0 @@ -/* - * FeedsManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.entries - -import com.rometools.rome.feed.synd.* -import com.rometools.rome.io.FeedException -import com.rometools.rome.io.SyndFeedInput -import com.rometools.rome.io.SyndFeedOutput -import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import net.thauvin.erik.mobibot.Utils.today -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.io.InputStreamReader -import java.io.OutputStreamWriter -import java.nio.charset.StandardCharsets -import java.nio.file.Files -import java.nio.file.Paths -import java.util.* -import kotlin.io.path.exists - -/** - * Manages the RSS feeds. - */ -class FeedsManager private constructor() { - companion object { - private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) - - // The file containing the current entries. - private const val CURRENT_XML = "current.xml" - - // The .xml extension. - private const val DOT_XML = ".xml" - - /** - * Loads the current feed. - */ - @JvmStatic - @Throws(IOException::class, FeedException::class) - fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String { - entries.links.clear() - val xml = Paths.get("${entries.logsDir}${currentFile}") - var pubDate = today() - if (xml.exists()) { - val input = SyndFeedInput() - InputStreamReader( - Files.newInputStream(xml), StandardCharsets.UTF_8 - ).use { reader -> - val feed = input.build(reader) - pubDate = feed.publishedDate.toIsoLocalDate() - val items = feed.entries - var entry: EntryLink - for (i in items.indices.reversed()) { - with(items[i]) { - entry = EntryLink( - link, - title, - author.substring(author.lastIndexOf('(') + 1, author.length - 1), - entries.channel, - publishedDate, - categories - ) - var split: List<String> - for (comment in description.value.split("<br/>")) { - split = comment.split(": ".toRegex(), 2) - if (split.size == 2) { - entry.addComment(comment = split[1].trim(), nick = split[0].trim()) - } - } - } - entries.links.add(entry) - } - } - } else { - // Create an empty feed. - saveFeed(entries) - } - return pubDate - } - - /** - * Saves the feeds. - */ - @JvmStatic - fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) { - if (logger.isDebugEnabled) logger.debug("Saving the feeds...") - if (entries.logsDir.isNotBlank()) { - try { - val output = SyndFeedOutput() - val rss: SyndFeed = SyndFeedImpl() - val items: MutableList<SyndEntry> = mutableListOf() - var item: SyndEntry - OutputStreamWriter( - Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 - ).use { fw -> - with(rss) { - feedType = "rss_2.0" - title = "${entries.channel} IRC Links" - description = "Links from ${entries.ircServer} on ${entries.channel}" - if (entries.backlogs.isNotBlank()) link = entries.backlogs - publishedDate = Calendar.getInstance().time - language = "en" - } - val buff: StringBuilder = StringBuilder() - for (i in entries.links.indices.reversed()) { - with(entries.links[i]) { - buff.setLength(0) - buff.append("Posted by <b>") - .append(nick) - .append("</b> on <a href=\"irc://") - .append(entries.ircServer).append('/') - .append(channel) - .append("\"><b>") - .append(channel) - .append("</b></a>") - if (comments.isNotEmpty()) { - buff.append(" <br/><br/>") - for (j in comments.indices) { - if (j > 0) { - buff.append(" <br/>") - } - buff.append(comments[j].nick).append(": ").append(comments[j].comment) - } - } - item = SyndEntryImpl() - item.link = link - item.description = SyndContentImpl().apply { value = buff.toString() } - item.title = title - item.publishedDate = date - item.author = "${channel.removePrefix("#")}@${entries.ircServer} ($nick)" - item.categories = tags - items.add(item) - } - } - rss.entries = items - if (logger.isDebugEnabled) logger.debug("Writing the entries feed.") - output.output(rss, fw) - } - OutputStreamWriter( - Files.newOutputStream( - Paths.get( - entries.logsDir + today() + DOT_XML - ) - ), StandardCharsets.UTF_8 - ).use { fw -> output.output(rss, fw) } - } catch (e: FeedException) { - if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e) - } catch (e: IOException) { - if (logger.isWarnEnabled) - logger.warn("An IO error occurred while generating the entries feed.", e) - } - } else { - if (logger.isWarnEnabled) { - logger.warn("Unable to generate the entries feed. A required property is missing.") - } - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt deleted file mode 100644 index 8c8e736..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * AbstractModule.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.pircbotx.hooks.events.PrivateMessageEvent -import org.pircbotx.hooks.types.GenericMessageEvent - -/** - * The `Module` abstract class. - */ -abstract class AbstractModule { - /** - * The module name. - */ - abstract val name: String - - /** - * The module's commands, if any. - */ - @JvmField - val commands: MutableList<String> = mutableListOf() - - @JvmField - val help: MutableList<String> = mutableListOf() - val properties: MutableMap<String, String> = mutableMapOf() - - /** - * Responds to a command. - */ - abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) - - /** - * Returns the module's property keys. - */ - val propertyKeys: Set<String> - get() = properties.keys - - /** - * Returns `true` if the module has properties. - */ - fun hasProperties(): Boolean { - return properties.isNotEmpty() - } - - /** - * Responds with the module's help. - */ - open fun helpResponse(event: GenericMessageEvent): Boolean { - for (h in help) { - event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent)) - } - return true - } - - /** - * Initializes the properties. - */ - fun initProperties(vararg keys: String) { - for (key in keys) { - properties[key] = "" - } - } - - /** - * Returns `true` if the module is enabled. - */ - val isEnabled: Boolean - get() = if (hasProperties()) { - isValidProperties - } else { - true - } - - /** - * Returns `true` if the module responds to private messages. - */ - open val isPrivateMsgEnabled: Boolean = false - - /** - * Ensures that all properties have values. - */ - open val isValidProperties: Boolean - get() { - for (s in properties.keys) { - if (properties[s].isNullOrBlank()) { - return false - } - } - return true - } - - /** - * Sets a property key and value. - */ - fun setProperty(key: String, value: String) { - if (key.isNotBlank()) { - properties[key] = value - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt b/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt deleted file mode 100644 index b7aae28..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Calc.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.objecthunter.exp4j.ExpressionBuilder -import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.text.DecimalFormat - -/** - * The Calc module. - */ -class Calc : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Calc::class.java) - - override val name = "Calc" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - event.respond(calculate(args)) - } catch (e: IllegalArgumentException) { - if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e) - event.respond("No idea. This is the kind of math I don't get.") - } catch (e: UnknownFunctionOrVariableException) { - if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e) - event.respond("No idea. I must've some form of Dyscalculia.") - } - } else { - helpResponse(event) - } - } - - companion object { - // Calc command - private const val CALC_CMD = "calc" - - /** - * Performs a calculation. e.g.: 1 + 1 * 2 - */ - @JvmStatic - @Throws(IllegalArgumentException::class) - fun calculate(query: String): String { - val decimalFormat = DecimalFormat("#.##") - val calc = ExpressionBuilder(query).build() - return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()).bold() - } - } - - init { - commands.add(CALC_CMD) - help.add("To solve a mathematical calculation:") - help.add(helpFormat("%c $CALC_CMD <calculation>")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt deleted file mode 100644 index bd92332..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ /dev/null @@ -1,176 +0,0 @@ -/* - * ChatGpt.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.apache.commons.text.WordUtils -import org.json.JSONException -import org.json.JSONObject -import org.json.JSONWriter -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse - -class ChatGpt : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) - - override val name = CHATGPT_NAME - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val answer = chat( - args.trim(), properties[API_KEY_PROP], - properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() - ) - if (answer.isNotBlank()) { - event.sendMessage(WordUtils.wrap(answer, 400)) - } else { - event.respond("$name is stumped.") - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } catch (e: NumberFormatException) { - if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e) - event.respond("The $name module is misconfigured.") - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The service name. - */ - const val CHATGPT_NAME = "ChatGPT" - - /** - * The API Key property. - */ - const val API_KEY_PROP = "chatgpt-api-key" - - /** - * The max tokens property. - */ - const val MAX_TOKENS_PROP = "chatgpt-max-tokens" - - // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/completions" - - // ChatGPT command - private const val CHATGPT_CMD = "chatgpt" - - - @JvmStatic - @Throws(ModuleException::class) - fun chat(query: String, apiKey: String?, maxTokens: Int): String { - if (!apiKey.isNullOrEmpty()) { - val prompt = JSONWriter.valueToString("Q:$query\nA:") - val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST( - HttpRequest.BodyPublishers.ofString( - """{ - "model": "text-davinci-003", - "prompt": $prompt, - "temperature": 0, - "max_tokens": $maxTokens, - "top_p": 1, - "frequency_penalty": 0, - "presence_penalty": 0 - }""".trimIndent() - ) - ) - .build() - try { - val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) - if (response.statusCode() == 200) { - try { - val jsonResponse = JSONObject(response.body()) - val choices = jsonResponse.getJSONArray("choices") - return choices.getJSONObject(0).getString("text").trim() - } catch (e: JSONException) { - throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e - ) - } - } else { - if (response.statusCode() == 429) { - throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." - ) - } else { - throw IOException("HTTP Status Code: " + response.statusCode()) - } - } - } catch (e: IOException) { - throw ModuleException( - "$CHATGPT_CMD($query): IO", - "An IO error has occurred while conversing with $CHATGPT_NAME.", - e - ) - } - } else { - throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.") - } - } - } - - init { - commands.add(CHATGPT_CMD) - with(help) { - add("To get answers from $name:") - add(Utils.helpFormat("%c $CHATGPT_CMD <query>")) - add("For example:") - add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) - add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) - } - initProperties(API_KEY_PROP, MAX_TOKENS_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt deleted file mode 100644 index d14056e..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ /dev/null @@ -1,159 +0,0 @@ -/* - * CryptoPrices.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.crypto.CryptoException -import net.thauvin.erik.crypto.CryptoPrice -import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.json.JSONObject -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException - -/** - * The Cryptocurrency Prices module. - */ -class CryptoPrices : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) - - override val name = "CryptoPrices" - - /** - * Returns the cryptocurrency market price from - * [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price). - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (CURRENCIES.isEmpty()) { - try { - loadCurrencies() - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - } - } - - val debugMessage = "crypto($cmd $args)" - if (args == CODES_KEYWORD) { - event.sendMessage("The supported currencies are:") - event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true) - } else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { - try { - val price = currentPrice(args.split(' ')) - val amount = try { - price.toCurrency() - } catch (ignore: IllegalArgumentException) { - price.amount - } - event.respond("${price.base} current price is $amount [${CURRENCIES[price.currency]}]") - } catch (e: CryptoException) { - if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) - e.message?.let { - event.respond(it) - } - } catch (e: IOException) { - if (logger.isErrorEnabled) logger.error(debugMessage, e) - event.respond("An IO error has occurred while retrieving the cryptocurrency market price.") - } - } else { - helpResponse(event) - } - - } - - companion object { - // Crypto command - private const val CRYPTO_CMD = "crypto" - - // Fiat Currencies - private val CURRENCIES: MutableMap<String, String> = mutableMapOf() - - // Currency codes keyword - private const val CODES_KEYWORD = "codes" - - /** - * Get current market price. - */ - @JvmStatic - fun currentPrice(args: List<String>): CryptoPrice { - return if (args.size == 2) - spotPrice(args[0], args[1]) - else - spotPrice(args[0]) - } - - /** - * For testing purposes. - */ - fun getCurrencyName(code: String): String? { - return CURRENCIES[code] - } - - /** - * Loads the Fiat currencies.. - */ - @JvmStatic - @Throws(ModuleException::class) - fun loadCurrencies() { - try { - val json = JSONObject(CryptoPrice.apiCall(listOf("currencies"))) - val data = json.getJSONArray("data") - for (i in 0 until data.length()) { - val d = data.getJSONObject(i) - CURRENCIES[d.getString("id")] = d.getString("name") - } - } catch (e: CryptoException) { - throw ModuleException( - "loadCurrencies(): CE", - "An error has occurred while retrieving the currencies table.", - e - ) - } - } - } - - init { - commands.add(CRYPTO_CMD) - with(help) { - add("To retrieve a cryptocurrency's market price:") - add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) - add("For example:") - add(helpFormat("%c $CRYPTO_CMD BTC")) - add(helpFormat("%c $CRYPTO_CMD ETH EUR")) - add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) - add("To list the supported currencies:") - add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD")) - } - loadCurrencies() - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt deleted file mode 100644 index da0efd8..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ /dev/null @@ -1,222 +0,0 @@ -/* - * CurrencyConverter.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage -import org.json.JSONObject -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URL -import java.text.DecimalFormat -import java.util.* - - -/** - * The CurrencyConverter module. - */ -class CurrencyConverter : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) - - override val name = "CurrencyConverter" - - // Reload currency codes - private fun reload(apiKey: String?) { - if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) { - try { - loadSymbols(apiKey) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - } - } - } - - /** - * Converts the specified currencies. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - reload(properties[API_KEY_PROP]) - - when { - SYMBOLS.isEmpty() -> { - event.respond(EMPTY_SYMBOLS_TABLE) - } - - args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { - val msg = convertCurrency(properties[API_KEY_PROP], args) - event.respond(msg.msg) - if (msg.isError) { - helpResponse(event) - } - } - - args.contains(CODES_KEYWORD) -> { - event.sendMessage("The supported currency codes are:") - event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) - } - - else -> { - helpResponse(event) - } - } - } - - override fun helpResponse(event: GenericMessageEvent): Boolean { - reload(properties[API_KEY_PROP]) - - if (SYMBOLS.isEmpty()) { - event.sendMessage(EMPTY_SYMBOLS_TABLE) - } else { - val nick = event.bot().nick - event.sendMessage("To convert from one currency to another:") - event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled) - ) - ) - event.sendMessage("To list the supported currency codes:") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) - ) - ) - } - return true - } - - companion object { - /** - * The API Key property. - */ - const val API_KEY_PROP = "exchangerate-api-key" - - // Currency command - private const val CURRENCY_CMD = "currency" - - // Currency codes keyword - private const val CODES_KEYWORD = "codes" - - // Empty symbols table. - private const val EMPTY_SYMBOLS_TABLE = "Sorry, but the currency table is empty." - - // Currency symbols - private val SYMBOLS: TreeMap<String, String> = TreeMap() - - // Decimal format - private val DECIMAL_FORMAT = DecimalFormat("0.00#") - - /** - * Converts from a currency to another. - */ - @JvmStatic - fun convertCurrency(apiKey: String?, query: String): Message { - if (apiKey.isNullOrEmpty()) { - throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.") - } - - val cmds = query.split(" ") - return if (cmds.size == 4) { - if (cmds[3] == cmds[1] || "0" == cmds[0]) { - PublicMessage("You're kidding, right?") - } else { - val to = cmds[1].uppercase() - val from = cmds[3].uppercase() - if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) { - try { - val amt = cmds[0].replace(",", "") - val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt") - val body = url.reader().body - val json = JSONObject(body) - - if (json.getString("result") == "success") { - val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result")) - PublicMessage( - "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" - ) - } else { - ErrorMessage("Sorry, an error occurred while converting the currencies.") - } - } catch (ignore: IOException) { - ErrorMessage("Sorry, an IO error occurred while converting the currencies.") - } - } else { - ErrorMessage("Sounds like monopoly money to me!") - } - } - } else { - ErrorMessage("Invalid query. Let's try again.") - } - } - - /** - * Loads the currency ISO symbols. - */ - @JvmStatic - @Throws(ModuleException::class) - fun loadSymbols(apiKey: String?) { - if (!apiKey.isNullOrEmpty()) { - try { - val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes") - val json = JSONObject(url.reader().body) - if (json.getString("result") == "success") { - val codes = json.getJSONArray("supported_codes") - for (i in 0 until codes.length()) { - val code = codes.getJSONArray(i) - SYMBOLS[code.getString(0)] = code.getString(1) - } - } - } catch (e: IOException) { - throw ModuleException( - "loadCodes(): IOE", - "An IO error has occurred while retrieving the currencies.", - e - ) - } - } - } - } - - init { - commands.add(CURRENCY_CMD) - initProperties(API_KEY_PROP) - loadSymbols(properties[ChatGpt.API_KEY_PROP]) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt b/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt deleted file mode 100644 index 8420fb1..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Dice.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - -/** - * The Dice module. - */ -class Dice : AbstractModule() { - override val name = "Dice" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - val arg = if (args.isBlank()) "2d6" else args.trim() - val match = Regex("^([1-9]|[12]\\d|3[0-2])[dD]([1-9]|[12]\\d|3[0-2])$").find(arg) - if (match != null) { - val (dice, sides) = match.destructured - event.respond("you rolled " + roll(dice.toInt(), sides.toInt())) - } else { - helpResponse(event) - } - } - - companion object { - // Dice command - private const val DICE_CMD = "dice" - - @JvmStatic - fun roll(dice: Int, sides: Int): String { - val result = StringBuilder() - var total = 0 - - repeat(dice) { - val roll = (1..sides).random() - total += roll - - if (result.isNotEmpty()) { - result.append(" + ") - } - - result.append(roll.bold()) - } - - if (dice != 1) { - result.append(" = ${total.bold()}") - } - - return result.toString() - } - } - - init { - commands.add(DICE_CMD) - help.add("To roll 2 dice with 6 sides:") - help.add(helpFormat("%c $DICE_CMD [2d6]")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt deleted file mode 100644 index f426d1e..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ /dev/null @@ -1,162 +0,0 @@ -/* - * GoogleSearch.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.ReleaseInfo -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.colorize -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage -import org.json.JSONException -import org.json.JSONObject -import org.pircbotx.Colors -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URL - -/** - * The GoogleSearch module. - */ -class GoogleSearch : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) - - override val name = "GoogleSearch" - - /** - * Searches Google. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val results = searchGoogle( - args, - properties[API_KEY_PROP], - properties[CSE_KEY_PROP], - event.user.nick - ) - for (msg in results) { - if (msg.isError) { - event.respond(msg.msg.colorize(msg.color)) - } else { - event.sendMessage(channel, msg) - } - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - // Google API Key property - const val API_KEY_PROP = "google-api-key" - - // Google Custom Search Engine ID property - const val CSE_KEY_PROP = "google-cse-cx" - - // Google command - private const val GOOGLE_CMD = "google" - - /** - * Performs a search on Google. - */ - @JvmStatic - @Throws(ModuleException::class) - fun searchGoogle( - query: String, - apiKey: String?, - cseKey: String?, - quotaUser: String = ReleaseInfo.PROJECT - ): List<Message> { - if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { - throw ModuleException( - "${GoogleSearch::class.java.name} is disabled.", - "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." - ) - } - val results = mutableListOf<Message>() - if (query.isNotBlank()) { - try { - val url = URL( - "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" - ) - val json = JSONObject(url.reader().body) - if (json.has("items")) { - val ja = json.getJSONArray("items") - for (i in 0 until ja.length()) { - val j = ja.getJSONObject(i) - results.add(NoticeMessage(j.getString("title").unescapeXml())) - results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) - } - } else if (json.has("error")) { - val error = json.getJSONObject("error") - val message = error.getString("message") - throw ModuleException("searchGoogle($query): ${error.getInt("code")} : $message", message) - } else { - results.add(ErrorMessage("No results found.", Colors.RED)) - } - } catch (e: IOException) { - throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) - } catch (e: JSONException) { - throw ModuleException( - "searchGoogle($query): JSON", - "A JSON error has occurred searching Google.", - e - ) - } - } else { - results.add(ErrorMessage("Invalid query. Please try again.")) - } - return results - } - } - - init { - commands.add(GOOGLE_CMD) - help.add("To search Google:") - help.add(helpFormat("%c $GOOGLE_CMD <query>")) - initProperties(API_KEY_PROP, CSE_KEY_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt b/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt deleted file mode 100644 index 2760fa7..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Joke.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.jokeapi.exceptions.HttpErrorException -import net.thauvin.erik.jokeapi.exceptions.JokeException -import net.thauvin.erik.jokeapi.joke -import net.thauvin.erik.jokeapi.models.Type -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.colorize -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage -import org.json.JSONException -import org.pircbotx.Colors -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException - -/** - * The Joke module. - */ -class Joke : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) - - override val name = "Joke" - - /** - * Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/). - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - with(event.bot()) { - try { - randomJoke().forEach { - sendIRC().notice(channel, it.msg.colorize(it.color)) - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } - } - - companion object { - // Joke command - private const val JOKE_CMD = "joke" - - /** - * Retrieves a random joke. - */ - @JvmStatic - @Throws(ModuleException::class) - fun randomJoke(): List<Message> { - return try { - val joke = joke(safe = true, type = Type.SINGLE, splitNewLine = true) - joke.joke.map { PublicMessage(it, Colors.CYAN) } - } catch (e: JokeException) { - throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e) - } catch (e: HttpErrorException) { - throw ModuleException("randomJoke(): HTTP: ${e.statusCode}", e.message, e) - } catch (e: IOException) { - throw ModuleException("randomJoke(): IOE", "An IO error has occurred retrieving a random joke.", e) - } catch (e: JSONException) { - throw ModuleException("randomJoke(): JSON", "A parsing error has occurred retrieving a random joke.", e) - } - } - } - - init { - commands.add(JOKE_CMD) - help.add("To display a random joke:") - help.add(helpFormat("%c $JOKE_CMD")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt b/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt deleted file mode 100644 index 9ab2ead..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Lookup.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.apache.commons.net.whois.WhoisClient -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.InetAddress -import java.net.UnknownHostException - -/** - * The Lookup module. - */ -class Lookup : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java) - - override val name = "Lookup" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.matches("(\\S.)+(\\S)+".toRegex())) { - try { - event.respondWith(nslookup(args).prependIndent()) - } catch (ignore: UnknownHostException) { - if (args.matches( - ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") - .toRegex() - ) - ) { - try { - val lines = whois(args) - if (lines.isNotEmpty()) { - var line: String - var hasData = false - for (rawLine in lines) { - line = rawLine.trim() - if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) { - if (!hasData) { - event.respondWith(line) - hasData = true - } else { - event.bot().sendIRC().notice(event.user.nick, line) - } - } - } - } else { - event.respond("Unknown host.") - } - } catch (ioe: IOException) { - if (logger.isWarnEnabled) { - logger.warn("Unable to perform whois IP lookup: $args", ioe) - } - event.respond("Unable to perform whois IP lookup: ${ioe.message}") - } - } else { - event.respond("Unknown host.") - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The whois default host. - */ - const val WHOIS_HOST = "whois.arin.net" - - // Lookup command - private const val LOOKUP_CMD = "lookup" - - /** - * Performs a DNS lookup on the specified query. - */ - @JvmStatic - @Throws(UnknownHostException::class) - fun nslookup(query: String): String { - val buffer = StringBuilder() - val results = InetAddress.getAllByName(query) - var hostInfo: String - for (result in results) { - if (result.hostAddress == query) { - hostInfo = result.hostName - if (hostInfo == query) { - throw UnknownHostException() - } - } else { - hostInfo = result.hostAddress - } - if (buffer.isNotEmpty()) { - buffer.append(", ") - } - buffer.append(hostInfo) - } - return buffer.toString() - } - - /** - * Performs a whois IP query. - */ - @Throws(IOException::class) - private fun whois(query: String): List<String> { - return whois(query, WHOIS_HOST) - } - - /** - * Performs a whois IP query. - */ - @JvmStatic - @Throws(IOException::class) - fun whois(query: String, host: String): List<String> { - val whoisClient = WhoisClient() - val lines: List<String> - with(whoisClient) { - try { - defaultTimeout = Constants.CONNECT_TIMEOUT - connect(host) - soTimeout = Constants.CONNECT_TIMEOUT - setSoLinger(false, 0) - lines = if (WHOIS_HOST == host) { - query("n - $query").split("\n") - } else { - query(query).split("\n") - } - } finally { - disconnect() - } - } - return lines - } - } - - init { - commands.add(LOOKUP_CMD) - help.add("To perform a DNS lookup query:") - help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt b/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt deleted file mode 100644 index 3be3a5f..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Mastodon.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.prefixIfMissing -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.social.SocialModule -import org.json.JSONException -import org.json.JSONObject -import org.json.JSONWriter -import java.io.IOException -import java.net.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse - -class Mastodon : SocialModule() { - override val name = "Mastodon" - - override val handle: String? - get() = properties[HANDLE_PROP] - - override val isAutoPost: Boolean - get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() - - override val isValidProperties: Boolean - get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank()) - - /** - * Formats the entry for posting. - */ - override fun formatEntry(entry: EntryLink): String { - return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}" - } - - private fun formatTags(entry: EntryLink): String { - return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } - .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } - } - - /** - * Posts on Mastodon. - */ - @Throws(ModuleException::class) - override fun post(message: String, isDm: Boolean): String { - return toot( - apiKey = properties[ACCESS_TOKEN_PROP], - instance = properties[INSTANCE_PROP], - handle = handle, - message = message, - isDm = isDm - ) - } - - companion object { - // Property keys - const val ACCESS_TOKEN_PROP = "mastodon-access-token" - const val AUTO_POST_PROP = "mastodon-auto-post" - const val HANDLE_PROP = "mastodon-handle" - const val INSTANCE_PROP = "mastodon-instance" - - private const val MASTODON_CMD = "mastodon" - private const val TOOT_CMD = "toot" - - /** - * Post on Mastodon. - */ - @JvmStatic - @Throws(ModuleException::class) - fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { - val request = HttpRequest.newBuilder() - .uri(URI.create("https://$instance/api/v1/statuses")) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .POST( - HttpRequest.BodyPublishers.ofString( - JSONWriter.valueToString( - if (isDm) { - mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") - } else { - mapOf("status" to message) - } - ) - ) - ) - .build() - try { - val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) - if (response.statusCode() == 200) { - return try { - val jsonResponse = JSONObject(response.body()) - if (isDm) { - jsonResponse.getString("content") - } else { - "Your message was posted to ${jsonResponse.getString("url")}" - } - } catch (e: JSONException) { - throw ModuleException("mastodonPost($message)", "A JSON error has occurred: ${e.message}", e) - } - } else { - throw IOException("Status Code: " + response.statusCode()) - } - } catch (e: IOException) { - throw ModuleException("mastodonPost($message)", "An IO error has occurred: ${e.message}", e) - } catch (e: InterruptedException) { - throw ModuleException("mastodonPost($message)", "An error has occurred: ${e.message}", e) - } - } - } - - init { - commands.add(MASTODON_CMD) - commands.add(TOOT_CMD) - help.add("To toot on Mastodon:") - help.add(Utils.helpFormat("%c $TOOT_CMD <message>")) - properties[AUTO_POST_PROP] = "false" - initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt b/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt deleted file mode 100644 index a7416c2..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ModuleException.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -/** - * The `ModuleException` class. - */ -class ModuleException @JvmOverloads constructor( - val debugMessage: String, - message: String? = null, - cause: Throwable? = null -) : Exception(message, cause) { - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt b/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt deleted file mode 100644 index 944dbc1..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Ping.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - -/** - * The Ping module. - */ -class Ping : AbstractModule() { - override val name = "Ping" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - event.bot().sendIRC().action(channel, randomPing()) - } - - companion object { - /** - * The ping responses. - */ - @JvmField - val PINGS = listOf( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!" - ) - - @JvmStatic - fun randomPing(): String { - return PINGS[PINGS.indices.random()] - } - - /** - * The ping command. - */ - private const val PING_CMD = "ping" - } - - init { - commands.add(PING_CMD) - help.add("To ping the bot:") - help.add(helpFormat("%c $PING_CMD")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt deleted file mode 100644 index a299d8d..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * RockPaperScissors.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - - -/** - * Simple module example in Kotlin. - */ -class RockPaperScissors : AbstractModule() { - override val name = "RockPaperScissors" - - init { - with(commands) { - add(Hands.ROCK.name.lowercase()) - add(Hands.PAPER.name.lowercase()) - add(Hands.SCISSORS.name.lowercase()) - } - - with(help) { - add("To play Rock Paper Scissors:") - add( - helpFormat( - "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" - + " | ${Hands.SCISSORS.name.lowercase()}" - ) - ) - } - } - - enum class Hands(val action: String) { - ROCK("crushes") { - override fun beats(hand: Hands): Boolean { - return hand == SCISSORS - } - }, - PAPER("covers") { - override fun beats(hand: Hands): Boolean { - return hand == ROCK - } - }, - SCISSORS("cuts") { - override fun beats(hand: Hands): Boolean { - return hand == PAPER - } - }; - - abstract fun beats(hand: Hands): Boolean - } - - companion object { - // For testing. - fun winLoseOrDraw(player: String, bot: String): String { - val hand = Hands.valueOf(player.uppercase()) - val botHand = Hands.valueOf(bot.uppercase()) - - return when { - hand == botHand -> "draw" - hand.beats(botHand) -> "win" - else -> "lose" - } - } - } - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - val hand = Hands.valueOf(cmd.uppercase()) - val botHand = Hands.entries[(0..Hands.entries.size).random()] - when { - hand == botHand -> { - event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.") - } - - hand.beats(botHand) -> { - event.respond("${hand.name.bold()} ${hand.action} ${botHand.name} » You ${"win".bold()}!") - } - - else -> { - event.respond("${botHand.name.bold()} ${botHand.action} ${hand.name} » You ${"lose".bold()}!") - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt b/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt deleted file mode 100644 index dcae5e7..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ /dev/null @@ -1,236 +0,0 @@ -/* - * StockQuote.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage -import net.thauvin.erik.mobibot.msg.PublicMessage -import org.json.JSONException -import org.json.JSONObject -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URL - -/** - * The StockQuote module. - */ -class StockQuote : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) - - override val name = "StockQuote" - - /** - * Returns the specified stock quote from Alpha Vantage. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val messages = getQuote(args, properties[API_KEY_PROP]) - for (msg in messages) { - event.sendMessage(channel, msg) - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The API property key. - */ - const val API_KEY_PROP = "alphavantage-api-key" - - /** - * The Invalid Symbol error string. - */ - const val INVALID_SYMBOL = "Invalid symbol." - - // API URL - private const val API_URL = "https://www.alphavantage.co/query?function=" - - // Quote command - private const val STOCK_CMD = "stock" - - @Throws(ModuleException::class) - private fun getJsonResponse(response: String, debugMessage: String): JSONObject { - return if (response.isNotBlank()) { - val json = JSONObject(response) - try { - val info = json.getString("Information") - if (info.isNotEmpty()) { - throw ModuleException(debugMessage, info.unescapeXml()) - } - } catch (ignore: JSONException) { - // Do nothing - } - try { - var error = json.getString("Note") - if (error.isNotEmpty()) { - throw ModuleException(debugMessage, error.unescapeXml()) - } - error = json.getString("Error Message") - if (error.isNotEmpty()) { - throw ModuleException(debugMessage, error.unescapeXml()) - } - } catch (ignore: JSONException) { - // Do nothing - } - json - } else { - throw ModuleException(debugMessage, "Empty Response.") - } - } - - /** - * Retrieves a stock quote. - */ - @JvmStatic - @Throws(ModuleException::class) - fun getQuote(symbol: String, apiKey: String?): List<Message> { - if (apiKey.isNullOrBlank()) { - throw ModuleException( - "${StockQuote::class.java.name} is disabled.", - "${STOCK_CMD.capitalise()} is disabled. The API key is missing." - ) - } - val messages = mutableListOf<Message>() - if (symbol.isNotBlank()) { - val debugMessage = "getQuote($symbol)" - var response: String - try { - with(messages) { - // Search for symbol/keywords - response = URL( - "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" - + apiKey.encodeUrl() - ).reader().body - var json = getJsonResponse(response, debugMessage) - val symbols = json.getJSONArray("bestMatches") - if (symbols.isEmpty) { - messages.add(ErrorMessage(INVALID_SYMBOL)) - } else { - val symbolInfo = symbols.getJSONObject(0) - - // Get quote for symbol - response = URL( - "${API_URL}GLOBAL_QUOTE&symbol=" - + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" - + apiKey.encodeUrl() - ).reader().body - json = getJsonResponse(response, debugMessage) - val quote = json.getJSONObject("Global Quote") - if (quote.isEmpty) { - add(ErrorMessage(INVALID_SYMBOL)) - } else { - - add( - PublicMessage( - "Symbol: " + quote.getString("01. symbol").unescapeXml() - + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' - ) - ) - - val pad = 10 - - add( - PublicMessage( - "Price:".padEnd(pad).prependIndent() - + quote.getString("05. price").unescapeXml() - ) - ) - add( - PublicMessage( - "Previous:".padEnd(pad).prependIndent() - + quote.getString("08. previous close").unescapeXml() - ) - ) - - val data = arrayOf( - "Open" to "02. open", - "High" to "03. high", - "Low" to "04. low", - "Volume" to "06. volume", - "Latest" to "07. latest trading day" - ) - - data.forEach { - add( - NoticeMessage( - "${it.first}:".padEnd(pad).prependIndent() - + quote.getString(it.second).unescapeXml() - ) - ) - } - - add( - NoticeMessage( - "Change:".padEnd(pad).prependIndent() - + quote.getString("09. change").unescapeXml() - + " [" + quote.getString("10. change percent").unescapeXml() + ']' - ) - ) - } - } - } - } catch (e: IOException) { - throw ModuleException("$debugMessage: IOE", "An IO error has occurred retrieving a stock quote.", e) - } catch (e: NullPointerException) { - throw ModuleException("$debugMessage: NPE", "An error has occurred retrieving a stock quote.", e) - } - } else { - messages.add(ErrorMessage(INVALID_SYMBOL)) - } - return messages - } - } - - init { - commands.add(STOCK_CMD) - help.add("To retrieve a stock quote:") - help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>")) - initProperties(API_KEY_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/War.class b/bin/main/net/thauvin/erik/mobibot/modules/War.class deleted file mode 100644 index 6c36e541b639c5b04b624194b3b2df6b3d11ba55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2452 zcmeHJUvm>T5MMb??6`r@5CVjMYDuZxQXf#BNMXjcP07@D7&{5m;i02DE4IR&<VrfX zdE`?u)6T$qzXso<)79MtNOEL4Q<&*PAFN&Jw7b7u?N9QbfBpUv0Pe%LB`6SBliYfC z%;Kq#9@pZT7b#!(%Ay>`AvfL=rb|#HaAuE9nHMrS@;3cF9#{gi&Cd4s0|IBCcHVv* zSS{peoj|#@(dcgWTDW2EQM=LKa!S9^Yi)K3Tuv<v4`MCszBh0R?=v}65!%iT9yeNT zG$^*(%^wLYKg|tMdeZ3s-0ZdpEaf3M)l|}0ChZOjV_t?O0yDodjqbk_QrHJLt=4uh zNK*}Y3C<H(e49sGYo*I@krZBUOU;c6r+i2Q6-`9QtWc6pwDLn9nR~P{zcuDmL=&Yg z#fsLmiq>64>sdwX+g5~z224t2sn6+w(|>E-@Qu|hu-)Xuasp7RD5G|N!YlKbK!jD! z$1&#NB*(ro|K1NL=M-@}Rzo`Cw#On~tx>g`z@XSvG>VO-YRa`1Lr#@;^}}*<X%Vc6 zCtN$kH1(0zDD#))(C21?2h6K!%iW|$<qurq0R&xc3>)$G6kUHP2)+u3S=<^GhQ@Np zcZI|b*~LsOc0&Rl^E`z7KWkQj6}YqjXW=q|bJgQyax_`&V1m6k^10q(*!hliDq!Ib z)56WO#iAVxL*Pc|pIj}-^-|Uz$nt9Kcw_>X`mwYk;u~@*jKDw*$Do9R80UIN>5;d` zt*@Do#!Kjxe)M|E_Tn&HFT)K2zno0t0~UY4o+WVYDD*9C=|k`rk2EuZQBL`fmnCP# z)nOuZfVcN=-dL$;#&b<*yc`~^y8~T2itaJf$WzPpI}poolaS{p*Y3>~_&-5MAL%+E zP#LIL2mGOM%q!!~Qg@FP(se@ycnu0;@qT+GxMn&S@0Z{<fzyYd1_BFRVGKUct_=;` z27CeW&p-)QVHW04TSYyU<xI{)C0YL@n=inrWPTbJ(fSPP1^5)b;J*M^#_=4k+)d^H zl|NwRr=<%oVD(k9?+T7{xCh{KxQgRKqF3M=P@EC?4A*DiI_hrM8p`9|zR2;`&GGgn zT9<(=L*j=(6>8}FD_p%;%$%no-W?CZ&EmEc{MR|*w%-%(Dil}H5OJ?yj!qO(fi-r? U%)mD|7N8CuN`O1~b69Tu4Ue7BiU0rr diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt b/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt deleted file mode 100644 index 80a06fa..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Weather2.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.aksingh.owmjapis.api.APIException -import net.aksingh.owmjapis.core.OWM -import net.aksingh.owmjapis.core.OWM.Country -import net.aksingh.owmjapis.model.CurrentWeather -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.capitalizeWords -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage -import net.thauvin.erik.mobibot.msg.PublicMessage -import org.pircbotx.Colors -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import kotlin.math.roundToInt - -/** - * The `Weather2` module. - */ -class Weather2 : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) - - override val name = "Weather" - - /** - * Fetches the weather data from a specific city. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val messages = getWeather(args, properties[API_KEY_PROP]) - if (messages[0].isError) { - helpResponse(event) - } else { - for (msg in messages) { - event.sendMessage(channel, msg) - } - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The OpenWeatherMap API Key property. - */ - const val API_KEY_PROP = "owm-api-key" - - // Weather command - private const val WEATHER_CMD = "weather" - - /** - * Converts and rounds temperature from °F to °C. - */ - fun ftoC(d: Double): Pair<Int, Int> { - val c = (d - 32) * 5 / 9 - return d.roundToInt() to c.roundToInt() - } - - /** - * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. - */ - fun getCountry(countryCode: String): Country { - for (c in Country.entries) { - if (c.value.equals(countryCode, ignoreCase = true)) { - return c - } - } - return Country.UNITED_STATES - } - - /** - * Retrieves the weather data. - */ - @JvmStatic - @Throws(ModuleException::class) - fun getWeather(query: String, apiKey: String?): List<Message> { - if (apiKey.isNullOrBlank()) { - throw ModuleException( - "${Weather2::class.java.name} is disabled.", - "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." - ) - } - val owm = OWM(apiKey) - val messages = mutableListOf<Message>() - owm.unit = OWM.Unit.IMPERIAL - if (query.isNotBlank()) { - val argv = query.split(",") - if (argv.size in 1..2) { - val city = argv[0].trim() - val code: String = if (argv.size > 1 && argv[1].isNotBlank()) { - argv[1].trim() - } else { - "US" - } - try { - val country = getCountry(code) - val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) { - owm.currentWeatherByZipCode(city.toInt(), country) - } else { - owm.currentWeatherByCityName(city, country) - } - if (cwd.hasCityName()) { - messages.add( - PublicMessage( - "City: ${cwd.cityName}, " + - country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" - ) - ) - cwd.mainData?.let { - with(it) { - if (hasTemp()) { - temp?.let { t -> - val (f, c) = ftoC(t) - messages.add(PublicMessage("Temperature: ${f}°F, ${c}°C")) - } - } - if (hasHumidity()) { - humidity?.let { h -> - messages.add(NoticeMessage("Humidity: ${h.roundToInt()}%")) - } - } - } - } - if (cwd.hasWindData()) { - cwd.windData?.let { - if (it.hasSpeed()) { - it.speed?.let { s -> - val w = mphToKmh(s) - messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h")) - } - } - } - } - if (cwd.hasWeatherList()) { - val condition = StringBuilder("Condition:") - cwd.weatherList?.let { - for (w in it) { - w?.let { - condition.append(' ') - .append(w.getDescription().capitalise()) - .append('.') - } - } - messages.add(NoticeMessage(condition.toString())) - } - } - if (cwd.hasCityId()) { - cwd.cityId?.let { - if (it > 0) { - messages.add( - NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) - ) - } else { - messages.add( - NoticeMessage( - "https://openweathermap.org/find?q=" - + "$city,${code.uppercase()}".encodeUrl(), - Colors.GREEN - ) - ) - } - } - } - } - } catch (e: APIException) { - if (e.code == 404) { - throw ModuleException( - "getWeather($query): API ${e.code}", - "The requested city was not found.", - e - ) - } else { - throw ModuleException("getWeather($query): API ${e.code}", e.message, e) - } - } catch (e: NullPointerException) { - throw ModuleException("getWeather($query): NPE", "Unable to perform weather lookup.", e) - } - } - } - if (messages.isEmpty()) { - messages.add(ErrorMessage("Invalid syntax.")) - } - return messages - } - - /** - * Converts and rounds temperature from mph to km/h. - */ - fun mphToKmh(w: Double): Pair<Int, Int> { - val kmh = w * 1.60934 - return w.roundToInt() to kmh.roundToInt() - } - } - - init { - commands.add(WEATHER_CMD) - with(help) { - add("To display weather information:") - add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) - add("For example:") - add(helpFormat("%c $WEATHER_CMD paris, fr")) - add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") - } - initProperties(API_KEY_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt deleted file mode 100644 index a72efab..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * WolframAlpha.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.isHttpSuccess -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URL - -class WolframAlpha : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java) - - override val name = "WolframAlpha" - - private fun getUnits(unit: String?): String { - return if (unit?.lowercase() == METRIC) { - METRIC - } else { - IMPERIAL - } - } - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val query = args.trim().split("units=", limit = 2, ignoreCase = true) - event.sendMessage( - queryWolfram( - query[0].trim(), - units = if (query.size == 2) { - getUnits(query[1].trim()) - } else { - getUnits(properties[UNITS_PROP]) - }, - appId = properties[APPID_KEY_PROP] - ) - ) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The Wolfram Alpha API Key property. - */ - const val APPID_KEY_PROP = "wolfram-appid" - - /** - * The Wolfram units properties - */ - const val UNITS_PROP = "wolfram-units" - - const val METRIC = "metric" - const val IMPERIAL = "imperial" - - // Wolfram command - private const val WOLFRAM_CMD = "wolfram" - - // Wolfram Alpha API URL - private const val API_URL = "http://api.wolframalpha.com/v1/spoken?appid=" - - @JvmStatic - @Throws(ModuleException::class) - fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String { - if (!appId.isNullOrEmpty()) { - try { - val urlReader = URL("${API_URL}${appId}&units=${units}&i=" + query.encodeUrl()).reader() - if (urlReader.responseCode.isHttpSuccess()) { - return urlReader.body - } else { - throw ModuleException( - "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", - urlReader.body.ifEmpty { - "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" - } - ) - } - } catch (ioe: IOException) { - throw ModuleException( - "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe - ) - } - } else { - throw ModuleException("wolfram($query): No API Key", "No Wolfram Alpha API key specified.") - } - } - } - - init { - commands.add(WOLFRAM_CMD) - with(help) { - add("To get answers from Wolfram Alpha:") - add(Utils.helpFormat("%c $WOLFRAM_CMD <query> [units=(${METRIC}|${IMPERIAL})]")) - add("For example:") - add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas")) - add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric")) - } - initProperties(APPID_KEY_PROP, UNITS_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt b/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt deleted file mode 100644 index 18072bc..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ /dev/null @@ -1,390 +0,0 @@ -/* - * WorldTime.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.pircbotx.hooks.types.GenericMessageEvent -import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.time.temporal.ChronoField - -/** - * The WorldTime module. - */ -class WorldTime : AbstractModule() { - override val name = "WorldTime" - - companion object { - /** - * Beats (Internet Time) keyword - */ - const val BEATS_KEYWORD = ".beats" - - /** - * Supported countries - */ - val COUNTRIES_MAP = buildMap<String, String> { - put("AG", "America/Antigua") - put("AI", "America/Anguilla") - put("AE", "Asia/Dubai") - put("AD", "Europe/Andorra") - put("AKDT", "America/Anchorage") - put("AF", "Asia/Kabul") - put("AKST", "America/Anchorage") - put("AL", "Europe/Tirane") - put("AM", "Asia/Yerevan") - put("AO", "Africa/Luanda") - put("AQ", "Antarctica/South_Pole") - put("AR", "America/Argentina/Buenos_Aires") - put("AS", "Pacific/Pago_Pago") - put("AT", "Europe/Vienna") - put("AU", "Australia/Sydney") - put("AW", "America/Aruba") - put("AX", "Europe/Mariehamn") - put("AZ", "Asia/Baku") - put("BA", "Europe/Sarajevo") - put("BB", "America/Barbados") - put("BD", "Asia/Dhaka") - put("BE", "Europe/Brussels") - put("BEAT", BEATS_KEYWORD) - put("BF", "Africa/Ouagadougou") - put("BG", "Europe/Sofia") - put("BH", "Asia/Bahrain") - put("BI", "Africa/Bujumbura") - put("BJ", "Africa/Porto-Novo") - put("BL", "America/St_Barthelemy") - put("BM", "Atlantic/Bermuda") - put("BMT", BEATS_KEYWORD) - put("BN", "Asia/Brunei") - put("BO", "America/La_Paz") - put("BQ", "America/Kralendijk") - put("BR", "America/Sao_Paulo") - put("BS", "America/Nassau") - put("BT", "Asia/Thimphu") - put("BW", "Africa/Gaborone") - put("BY", "Europe/Minsk") - put("BZ", "America/Belize") - put("CA", "America/Montreal") - put("CC", "Indian/Cocos") - put("CD", "Africa/Kinshasa") - put("CDT", "America/Chicago") - put("CET", "CET") - put("CF", "Africa/Bangui") - put("CG", "Africa/Brazzaville") - put("CH", "Europe/Zurich") - put("CI", "Africa/Abidjan") - put("CK", "Pacific/Rarotonga") - put("CL", "America/Santiago") - put("CM", "Africa/Douala") - put("CN", "Asia/Shanghai") - put("CO", "America/Bogota") - put("CR", "America/Costa_Rica") - put("CST", "America/Chicago") - put("CU", "Cuba") - put("CV", "Atlantic/Cape_Verde") - put("CW", "America/Curacao") - put("CX", "Indian/Christmas") - put("CY", "Asia/Nicosia") - put("CZ", "Europe/Prague") - put("DE", "Europe/Berlin") - put("DJ", "Africa/Djibouti") - put("DK", "Europe/Copenhagen") - put("DM", "America/Dominica") - put("DO", "America/Santo_Domingo") - put("DZ", "Africa/Algiers") - put("EC", "Pacific/Galapagos") - put("EDT", "America/New_York") - put("EE", "Europe/Tallinn") - put("EG", "Africa/Cairo") - put("EH", "Africa/El_Aaiun") - put("ER", "Africa/Asmara") - put("ES", "Europe/Madrid") - put("EST", "America/New_York") - put("ET", "Africa/Addis_Ababa") - put("FI", "Europe/Helsinki") - put("FJ", "Pacific/Fiji") - put("FK", "Atlantic/Stanley") - put("FM", "Pacific/Yap") - put("FO", "Atlantic/Faroe") - put("FR", "Europe/Paris") - put("GA", "Africa/Libreville") - put("GB", "Europe/London") - put("GD", "America/Grenada") - put("GE", "Asia/Tbilisi") - put("GF", "America/Cayenne") - put("GG", "Europe/Guernsey") - put("GH", "Africa/Accra") - put("GI", "Europe/Gibraltar") - put("GL", "America/Thule") - put("GM", "Africa/Banjul") - put("GMT", "GMT") - put("GN", "Africa/Conakry") - put("GP", "America/Guadeloupe") - put("GQ", "Africa/Malabo") - put("GR", "Europe/Athens") - put("GS", "Atlantic/South_Georgia") - put("GT", "America/Guatemala") - put("GU", "Pacific/Guam") - put("GW", "Africa/Bissau") - put("GY", "America/Guyana") - put("HK", "Asia/Hong_Kong") - put("HN", "America/Tegucigalpa") - put("HR", "Europe/Zagreb") - put("HST", "Pacific/Honolulu") - put("HT", "America/Port-au-Prince") - put("HU", "Europe/Budapest") - put("ID", "Asia/Jakarta") - put("IE", "Europe/Dublin") - put("IL", "Asia/Tel_Aviv") - put("IM", "Europe/Isle_of_Man") - put("IN", "Asia/Kolkata") - put("IO", "Indian/Chagos") - put("IQ", "Asia/Baghdad") - put("IR", "Asia/Tehran") - put("IS", "Atlantic/Reykjavik") - put("IT", "Europe/Rome") - put("JE", "Europe/Jersey") - put("JM", "Jamaica") - put("JO", "Asia/Amman") - put("JP", "Asia/Tokyo") - put("KE", "Africa/Nairobi") - put("KG", "Asia/Bishkek") - put("KH", "Asia/Phnom_Penh") - put("KI", "Pacific/Tarawa") - put("KM", "Indian/Comoro") - put("KN", "America/St_Kitts") - put("KP", "Asia/Pyongyang") - put("KR", "Asia/Seoul") - put("KW", "Asia/Riyadh") - put("KY", "America/Cayman") - put("KZ", "Asia/Oral") - put("LA", "Asia/Vientiane") - put("LB", "Asia/Beirut") - put("LC", "America/St_Lucia") - put("LI", "Europe/Vaduz") - put("LK", "Asia/Colombo") - put("LR", "Africa/Monrovia") - put("LS", "Africa/Maseru") - put("LT", "Europe/Vilnius") - put("LU", "Europe/Luxembourg") - put("LV", "Europe/Riga") - put("LY", "Africa/Tripoli") - put("MA", "Africa/Casablanca") - put("MC", "Europe/Monaco") - put("MD", "Europe/Chisinau") - put("MDT", "America/Denver") - put("ME", "Europe/Podgorica") - put("MF", "America/Marigot") - put("MG", "Indian/Antananarivo") - put("MH", "Pacific/Majuro") - put("MK", "Europe/Skopje") - put("ML", "Africa/Timbuktu") - put("MM", "Asia/Yangon") - put("MN", "Asia/Ulaanbaatar") - put("MO", "Asia/Macau") - put("MP", "Pacific/Saipan") - put("MQ", "America/Martinique") - put("MR", "Africa/Nouakchott") - put("MS", "America/Montserrat") - put("MST", "America/Denver") - put("MT", "Europe/Malta") - put("MU", "Indian/Mauritius") - put("MV", "Indian/Maldives") - put("MW", "Africa/Blantyre") - put("MX", "America/Mexico_City") - put("MY", "Asia/Kuala_Lumpur") - put("MZ", "Africa/Maputo") - put("NA", "Africa/Windhoek") - put("NC", "Pacific/Noumea") - put("NE", "Africa/Niamey") - put("NF", "Pacific/Norfolk") - put("NG", "Africa/Lagos") - put("NI", "America/Managua") - put("NL", "Europe/Amsterdam") - put("NO", "Europe/Oslo") - put("NP", "Asia/Kathmandu") - put("NR", "Pacific/Nauru") - put("NU", "Pacific/Niue") - put("NZ", "Pacific/Auckland") - put("OM", "Asia/Muscat") - put("PA", "America/Panama") - put("PDT", "America/Los_Angeles") - put("PE", "America/Lima") - put("PF", "Pacific/Tahiti") - put("PG", "Pacific/Port_Moresby") - put("PH", "Asia/Manila") - put("PK", "Asia/Karachi") - put("PL", "Europe/Warsaw") - put("PM", "America/Miquelon") - put("PN", "Pacific/Pitcairn") - put("PR", "America/Puerto_Rico") - put("PS", "Asia/Gaza") - put("PST", "America/Los_Angeles") - put("PT", "Europe/Lisbon") - put("PW", "Pacific/Palau") - put("PY", "America/Asuncion") - put("QA", "Asia/Qatar") - put("RE", "Indian/Reunion") - put("RO", "Europe/Bucharest") - put("RS", "Europe/Belgrade") - put("RU", "Europe/Moscow") - put("RW", "Africa/Kigali") - put("SA", "Asia/Riyadh") - put("SB", "Pacific/Guadalcanal") - put("SC", "Indian/Mahe") - put("SD", "Africa/Khartoum") - put("SE", "Europe/Stockholm") - put("SG", "Asia/Singapore") - put("SH", "Atlantic/St_Helena") - put("SI", "Europe/Ljubljana") - put("SJ", "Atlantic/Jan_Mayen") - put("SK", "Europe/Bratislava") - put("SL", "Africa/Freetown") - put("SM", "Europe/San_Marino") - put("SN", "Africa/Dakar") - put("SO", "Africa/Mogadishu") - put("SR", "America/Paramaribo") - put("SS", "Africa/Juba") - put("ST", "Africa/Sao_Tome") - put("SV", "America/El_Salvador") - put("SX", "America/Lower_Princes") - put("SY", "Asia/Damascus") - put("SZ", "Africa/Mbabane") - put("TC", "America/Grand_Turk") - put("TD", "Africa/Ndjamena") - put("TF", "Indian/Kerguelen") - put("TG", "Africa/Lome") - put("TH", "Asia/Bangkok") - put("TJ", "Asia/Dushanbe") - put("TK", "Pacific/Fakaofo") - put("TL", "Asia/Dili") - put("TM", "Asia/Ashgabat") - put("TN", "Africa/Tunis") - put("TO", "Pacific/Tongatapu") - put("TR", "Europe/Istanbul") - put("TT", "America/Port_of_Spain") - put("TV", "Pacific/Funafuti") - put("TW", "Asia/Taipei") - put("TZ", "Africa/Dar_es_Salaam") - put("UA", "Europe/Kiev") - put("UG", "Africa/Kampala") - put("UK", "Europe/London") - put("UM", "Pacific/Wake") - put("US", "America/New_York") - put("UTC", "UTC") - put("UY", "America/Montevideo") - put("UZ", "Asia/Tashkent") - put("VA", "Europe/Vatican") - put("VC", "America/St_Vincent") - put("VE", "America/Caracas") - put("VG", "America/Tortola") - put("VI", "America/St_Thomas") - put("VN", "Asia/Ho_Chi_Minh") - put("VU", "Pacific/Efate") - put("WF", "Pacific/Wallis") - put("WS", "Pacific/Apia") - put("YE", "Asia/Aden") - put("YT", "Indian/Mayotte") - put("ZA", "Africa/Johannesburg") - put("ZM", "Africa/Lusaka") - put("ZULU", "Zulu") - put("ZW", "Africa/Harare") - ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } - .forEach { tz -> put(tz, tz) } - } - - // The Time command - private const val TIME_CMD = "time" - - // The zones arguments - private const val ZONES_ARGS = "zones" - - // The default zone - private const val DEFAULT_ZONE = "PST" - - // Date/Time Format - private var dtf = - DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") - - /** - * Returns the current Internet (beat) Time. - */ - private fun internetTime(): String { - val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00")) - val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60 - + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt() - return "%c%03d".format('@', beats) - } - - /** - * Returns the time for the given timezone/city. - */ - @JvmStatic - fun time(query: String = DEFAULT_ZONE): String { - val tz = COUNTRIES_MAP[(if (query.isNotBlank()) query.trim().uppercase() else DEFAULT_ZONE)] - return if (tz != null) { - if (BEATS_KEYWORD == tz) { - "The current Internet Time is ${internetTime().bold()} $BEATS_KEYWORD" - } else { - (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) - + tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ').bold()) - } - } else { - "Unsupported country/zone. Please try again." - } - } - } - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.equals(ZONES_ARGS, true)) { - event.sendMessage("The supported countries/zones are: ") - event.sendList(COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, 14, isIndent = true) - } else { - event.respond(time(args)) - } - } - - override val isPrivateMsgEnabled = true - - init { - with(help) { - add("To display a country's current date/time:") - add(helpFormat("%c $TIME_CMD [<country code or zone>]")) - add("For a listing of the supported countries/zones:") - add(helpFormat("%c $TIME_CMD $ZONES_ARGS")) - } - commands.add(TIME_CMD) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt deleted file mode 100644 index 0607936..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * ErrorMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.msg - -/** - * The `ErrorMessage` class. - */ -class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isError = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/Message.kt b/bin/main/net/thauvin/erik/mobibot/msg/Message.kt deleted file mode 100644 index 23a33b9..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/Message.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Message.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.msg - -import net.thauvin.erik.semver.Constants - -/** - * The `Message` class. - */ -open class Message @JvmOverloads constructor( - var msg: String, - var color: String = DEFAULT_COLOR, - var isNotice: Boolean = false, - isError: Boolean = false, - var isPrivate: Boolean = false -) { - companion object { - var DEFAULT_COLOR = Constants.EMPTY - } - - init { - if (isError) { - isNotice = true - } - } - - /** Error flag. */ - var isError = isError - set(value) { - if (value) isNotice = true - field = value - } - - override fun toString(): String { - return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, msg='$msg')" - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt deleted file mode 100644 index 037d504..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * NoticeMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.msg - -/** - * The `NoticeMessage` class. - */ -class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isNotice = true) - diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt deleted file mode 100644 index b424fdf..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * PrivateMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.msg - -/** - * The `PrivateMessage` class. - */ -class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isPrivate = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt deleted file mode 100644 index 9c5e088..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * PublicMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.msg - -/** - * The `PublicMessage` class. - */ -class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color) diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt deleted file mode 100644 index 91f2dd9..0000000 --- a/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * SocialManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.social - -import net.thauvin.erik.mobibot.Addons -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.util.* - -/** - * Social Manager. - */ -class SocialManager { - private val entries: MutableSet<Int> = HashSet() - private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) - private val modules = ArrayList<SocialModule>() - private val timer = Timer(true) - - /** - * Adds social modules. - */ - fun add(addons: Addons, vararg modules: SocialModule) { - modules.forEach { - if (addons.add(it)) { - this.modules.add(it) - } - } - } - - /** - * Returns the number of entries. - */ - fun entriesCount(): Int = entries.size - - /** - * Sends a social notification (dm, etc.) - */ - fun notification(msg: String) { - modules.forEach { - it.notification(msg) - } - } - - /** - * Posts to social media. - */ - fun postEntry(index: Int) { - if (entries.contains(index)) { - modules.forEach { - it.postEntry(index) - } - entries.remove(index) - } - } - - /** - * Queues an entry for posting to social media. - */ - fun queueEntry(index: Int) { - if (modules.isNotEmpty()) { - entries.add(index) - if (logger.isDebugEnabled) { - logger.debug("Scheduling {} for posting on social media.", index.toLinkLabel()) - } - timer.schedule(SocialTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L) - } - } - - /** - * Removes entries from queue. - */ - fun removeEntry(index: Int) { - entries.remove(index) - } - - /** - * Posts all entries on shutdown. - */ - fun shutdown() { - timer.cancel() - entries.forEach { - postEntry(it) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt deleted file mode 100644 index b594670..0000000 --- a/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SocialModule.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.social - -import net.thauvin.erik.mobibot.commands.links.LinksManager -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.modules.AbstractModule -import net.thauvin.erik.mobibot.modules.ModuleException -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -abstract class SocialModule : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) - - abstract val handle: String? - abstract val isAutoPost: Boolean - - abstract fun formatEntry(entry: EntryLink): String - - /** - * Sends a DM. - */ - fun notification(msg: String) { - if (isEnabled && !handle.isNullOrBlank()) { - try { - post(message = msg, isDm = true) - if (logger.isDebugEnabled) logger.debug("Notified $handle on $name: $msg") - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn("Failed to notify $handle on $name: $msg", e) - } - } - } - - abstract fun post(message: String, isDm: Boolean): String - - /** - * Post entry to social media. - */ - fun postEntry(index: Int) { - if (isAutoPost && LinksManager.entries.links.size >= index) { - try { - if (logger.isDebugEnabled) { - logger.debug("Posting {} to $name.", index.toLinkLabel()) - } - post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn( - "Failed to post entry ${index.toLinkLabel()} on $name.", - e - ) - } - } - } - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - try { - event.respond(post("$args (by ${event.user.nick} on $channel)", false)) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt deleted file mode 100644 index 3fd315e..0000000 --- a/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SocialTimer.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.social - -import java.util.* - -class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() { - override fun run() { - socialManager.postEntry(index) - } -} diff --git a/bin/test/current.xml b/bin/test/current.xml deleted file mode 100644 index 535a400..0000000 --- a/bin/test/current.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ current.xml - ~ - ~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - ~ - ~ Redistribution and use in source and binary forms, with or without - ~ modification, are permitted provided that the following conditions are met: - ~ - ~ Redistributions of source code must retain the above copyright notice, this - ~ list of conditions and the following disclaimer. - ~ - ~ Redistributions in binary form must reproduce the above copyright notice, - ~ this list of conditions and the following disclaimer in the documentation - ~ and/or other materials provided with the distribution. - ~ - ~ Neither the name of this project nor the names of its contributors may be - ~ used to endorse or promote products derived from this software without - ~ specific prior written permission. - ~ - ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - ~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - ~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - ~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - ~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - ~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - ~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - --> - -<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> - <channel> - <title>#mobibot IRC Links - https://www.mobitopia.org/mobibot/logs - Links from irc.example.com on #mobibot - en - Sun, 31 Oct 2021 21:45:11 GMT - 2021-10-31T21:45:11Z - en - - Example 2 - https://www.example.com/2 - Posted by <b>Skynx</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> - tag2-1 - tag2-2 - Sun, 31 Oct 2021 21:45:11 GMT - https://www.foo.com - mobibot@irc.libera.chat (Skynx) - 2021-10-31T00:01:00Z - - - Example 1 - https://www.example.com/1 - Posted by <b>ErikT</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> - <br/><br/>ErikT: This is comment 1. <br/>Skynx: This is comment 2. - - tag1-1 - tag1-2 - Sun, 31 Oct 2021 21:43:15 GMT - https://www.example.com/ - mobibot@irc.libera.chat (ErikT) - 2021-10-31T00:00:00Z - - - diff --git a/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt b/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt deleted file mode 100644 index afb8ce6..0000000 --- a/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * AddonsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot - -import assertk.assertThat -import assertk.assertions.containsExactly -import assertk.assertions.isEqualTo -import assertk.assertions.size -import net.thauvin.erik.mobibot.commands.ChannelFeed -import net.thauvin.erik.mobibot.commands.Cycle -import net.thauvin.erik.mobibot.commands.Die -import net.thauvin.erik.mobibot.commands.Ignore -import net.thauvin.erik.mobibot.commands.links.Comment -import net.thauvin.erik.mobibot.commands.links.View -import net.thauvin.erik.mobibot.modules.* -import kotlin.test.Test -import java.util.* - -class AddonsTest { - private val p = Properties().apply { - put("disabled-modules", "war,dice Lookup") - put("disabled-commands", "View | comment") - } - private val addons = Addons(p) - - @Test - fun addTest() { - // Modules - addons.add(Joke()) - addons.add(RockPaperScissors()) - addons.add(War()) - addons.add(Dice()) - addons.add(Lookup()) - assertThat(addons::modules).size().isEqualTo(2) - assertThat(addons.names.modules, "names.modules").containsExactly("Joke", "RockPaperScissors") - - // Commands - addons.add(View()) - addons.add(Comment()) - addons.add(Cycle()) - addons.add(Die()) // invisible - addons.add(ChannelFeed("channel")) // no properties, disabled - p[Ignore.IGNORE_PROP] = "nick" - addons.add(Ignore()) - assertThat(addons::commands).size().isEqualTo(3) - - assertThat(addons.names.ops, "names.ops").containsExactly("cycle") - - assertThat(addons.names.commands, "names.command").containsExactly( - "joke", - "rock", - "paper", - "scissors", - "ignore" - ) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt deleted file mode 100644 index a3994ec..0000000 --- a/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * ExceptionSanitizer.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot - -import net.thauvin.erik.mobibot.Utils.obfuscate -import net.thauvin.erik.mobibot.Utils.replaceEach -import net.thauvin.erik.mobibot.modules.ModuleException - -object ExceptionSanitizer { - /** - * Returns a sanitized exception to avoid displaying api keys, etc. in CI logs. - */ - fun ModuleException.sanitize(vararg sanitize: String): ModuleException { - val search = sanitize.filter { it.isNotBlank() }.toTypedArray() - if (search.isNotEmpty()) { - val obfuscate = search.map { it.obfuscate() }.toTypedArray() - with(this) { - if (!cause?.message.isNullOrBlank()) { - return ModuleException( - debugMessage, - cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), - this - ) - } else if (!message.isNullOrBlank()) { - return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) - } - } - } - return this - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt b/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt deleted file mode 100644 index 6e5fa8f..0000000 --- a/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * FeedReaderTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import com.rometools.rome.io.FeedException -import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed -import net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test -import java.io.IOException -import java.net.MalformedURLException -import java.net.UnknownHostException - -/** - * The `FeedReader Test` class. - */ -class FeedReaderTest { - @Test - fun readFeedTest() { - var messages = readFeed("https://feeds.thauvin.net/ethauvin") - assertThat(messages, "messages").all { - size().isEqualTo(10) - index(1).prop(Message::msg).contains("erik.thauvin.net") - } - - messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") - assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing") - - messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) - assertThat(messages, "messages").size().isEqualTo(84) - messages.forEachIndexed { i, m -> - if (i % 2 == 0) { - assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum") - } else { - assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/") - } - } - - assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) - - assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java) - - assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) - - assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt b/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt deleted file mode 100644 index 1384a72..0000000 --- a/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * LocalProperties.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -import org.testng.annotations.BeforeSuite -import java.io.IOException -import java.net.InetAddress -import java.net.UnknownHostException -import java.nio.file.Files -import java.nio.file.Paths -import java.util.* - -/** - * Access to `local.properties`. - */ -open class LocalProperties { - @BeforeSuite(alwaysRun = true) - fun loadProperties() { - val localPath = Paths.get("local.properties") - if (Files.exists(localPath)) { - try { - Files.newInputStream(localPath).use { stream -> localProps.load(stream) } - } catch (ignore: IOException) { - // Do nothing - } - } - } - - companion object { - private val localProps = Properties() - - fun getHostName(): String { - val ciName = System.getenv("CI_NAME") - return ciName ?: try { - InetAddress.getLocalHost().hostName - } catch (ignore: UnknownHostException) { - "Unknown Host" - } - } - - fun getProperty(key: String): String { - return if (localProps.containsKey(key)) { - localProps.getProperty(key) - } else { - val env = System.getenv(keyToEnv(key)) - env?.let { - localProps.setProperty(key, env) - } - env - } - } - - private fun keyToEnv(key: String): String { - return key.replace('-', '_').uppercase() - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt b/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt deleted file mode 100644 index b527fc5..0000000 --- a/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * PinboardTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot - -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.entries.EntryLink -import org.testng.Assert.assertFalse -import org.testng.Assert.assertTrue -import kotlin.test.Test -import java.net.URL - -class PinboardTest : LocalProperties() { - private val pinboard = Pinboard() - - @Test - fun testPinboard() { - val apiToken = getProperty("pinboard-api-token") - val url = "https://www.example.com/${(1000..5000).random()}" - val ircServer = "irc.test.com" - val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) - - pinboard.setApiToken(apiToken) - - pinboard.addPin(ircServer, entry) - assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin") - - entry.link = "https://www.example.com/${(5001..9999).random()}" - pinboard.updatePin(ircServer, url, entry) - assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin") - - entry.title = "Foo Title" - pinboard.updatePin(ircServer, entry.link, entry) - assertTrue(validatePin(apiToken, url = entry.link, entry.title), "updatePin(${entry.title}") - - pinboard.deletePin(entry) - assertFalse(validatePin(apiToken, url = entry.link), "deletePin") - } - - private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { - val response = - URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body - - matches.forEach { - if (!response.contains(it)) { - return false - } - } - - return response.contains(url) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt deleted file mode 100644 index 7b7ba8c..0000000 --- a/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt +++ /dev/null @@ -1,278 +0,0 @@ -/* - * UtilsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.length -import net.thauvin.erik.mobibot.Utils.appendIfMissing -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.capitalizeWords -import net.thauvin.erik.mobibot.Utils.colorize -import net.thauvin.erik.mobibot.Utils.cyan -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.getIntProperty -import net.thauvin.erik.mobibot.Utils.green -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.lastOrEmpty -import net.thauvin.erik.mobibot.Utils.obfuscate -import net.thauvin.erik.mobibot.Utils.plural -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.red -import net.thauvin.erik.mobibot.Utils.replaceEach -import net.thauvin.erik.mobibot.Utils.reverseColor -import net.thauvin.erik.mobibot.Utils.toIntOrDefault -import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import net.thauvin.erik.mobibot.Utils.toUtcDateTime -import net.thauvin.erik.mobibot.Utils.today -import net.thauvin.erik.mobibot.Utils.underline -import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import org.pircbotx.Colors -import org.testng.annotations.BeforeClass -import kotlin.test.Test -import java.io.File -import java.io.IOException -import java.net.URL -import java.time.LocalDateTime -import java.util.* - -/** - * The `Utils Test` class. - */ -class UtilsTest { - private val ascii = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - private val cal = Calendar.getInstance() - private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) - private val test = "This is a test." - - @BeforeClass - fun setUp() { - cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 - } - - @Test - fun testAppendIfMissing() { - val dir = "dir" - val sep = '/' - val url = "https://erik.thauvin.net" - assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") - .isEqualTo(dir + File.separatorChar) - assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") - assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") - } - - @Test - fun testBold() { - assertThat(1.bold(), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - assertThat(2L.bold(), "bold(2L)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) - assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) - } - - - @Test - fun testCapitalise() { - assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test") - assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test") - assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test) - assertThat("".capitalise(), "capitalize()").isEqualTo("") - } - - @Test - fun textCapitaliseWords() { - assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") - assertThat("Already Capitalized".capitalizeWords(), "already capitalized") - .isEqualTo("Already Capitalized") - assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") - } - - @Test - fun testColorize() { - assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( - Colors.REVERSE + ascii + Colors.REVERSE - ) - assertThat(ascii.colorize(Colors.RED), "red.colorize()") - .isEqualTo(Colors.RED + ascii + Colors.NORMAL) - assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") - .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") - assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") - assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) - assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") - .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) - } - - @Test - fun testCyan() { - assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) - } - - @Test - fun testEncodeUrl() { - assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello%20G%C3%BCnter") - } - - @Test - fun testGetIntProperty() { - val p = Properties() - p["one"] = "1" - p["two"] = "two" - assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1) - assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2) - assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3) - } - - @Test - fun testGreen() { - assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) - } - - @Test - fun testHelpCmdSyntax() { - val bot = "mobibot" - assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") - .isEqualTo("$bot: $test $bot $test") - assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") - .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") - } - - @Test - fun testHelpFormat() { - assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") - .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") - assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") - .isEqualTo(test.prependIndent()) - assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") - .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) - - } - - @Test - fun testIsoLocalDate() { - assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17") - assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17") - } - - @Test - fun testLastOrEmpty() { - val two = listOf("1", "2") - assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2") - val one = listOf("1") - assertThat(one.lastOrEmpty(), "lastOrEmpty(1)").isEqualTo("") - } - - @Test - fun testObfuscate() { - assertThat(ascii.obfuscate(), "obfuscate()").all { - length().isEqualTo(ascii.length) - isEqualTo(("x".repeat(ascii.length))) - } - assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ") - } - - @Test - fun testPlural() { - val week = "week" - val weeks = "weeks" - - for (i in -1..3) { - assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week) - } - } - - @Test - fun testReplaceEach() { - val search = arrayOf("one", "two", "three") - val replace = arrayOf("1", "2", "3") - assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") - .isEqualTo(replace.joinToString(",")) - - assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) - - assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") - .isEqualTo(test.replace("t", "").replace("e", "E")) - - assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") - .isEqualTo(test) - } - - @Test - fun testRed() { - assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED)) - } - - @Test - fun testReverseColor() { - assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) - } - - @Test - fun testToday() { - assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate()) - } - - @Test - fun testToIntOrDefault() { - assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10) - assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2) - } - - @Test - fun testUnderline() { - assertThat(ascii.underline()).isEqualTo(ascii.colorize(Colors.UNDERLINE)) - } - - @Test - fun testUnescapeXml() { - assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( - "" - ) - } - - @Test - @Throws(IOException::class) - fun testUrlReader() { - val reader = URL("https://postman-echo.com/status/200").reader() - assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}") - assertThat(reader.responseCode).isEqualTo(200) - } - - @Test - fun testUtcDateTime() { - assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30") - assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt deleted file mode 100644 index 99ba951..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * InfoTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import assertk.assertThat -import assertk.assertions.isEqualTo -import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime -import kotlin.test.Test - -class InfoTest { - @Test - fun testToUptime() { - assertThat( - 547800300076L.toUptime(), - "upTime(full)" - ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") - assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") - assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") - assertThat( - 1320300000L.toUptime(), - "upTime(weeks days hours minutes)" - ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") - assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") - assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") - assertThat(59000L.toUptime(), "upTime(59 seconds)").isEqualTo("59 seconds") - assertThat(0L.toUptime(), "upTime(0 second)").isEqualTo("0 second") - - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt deleted file mode 100644 index b6fbbff..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * RecapTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.matches -import assertk.assertions.prop -import assertk.assertions.size -import kotlin.test.Test - -class RecapTest { - @Test - fun storeRecapTest() { - for (i in 1..20) { - Recap.storeRecap("sender$i", "test $i", false) - } - assertThat(Recap.recaps, "Recap.recaps").all { - size().isEqualTo(Recap.MAX_RECAPS) - prop(MutableList::first) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) - prop(MutableList::last) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) - } - - Recap.storeRecap("sender", "test action", true) - assertThat(Recap.recaps.last()) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt deleted file mode 100644 index fa2bb70..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * LinksManagerTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.links - -import assertk.all -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isEqualTo -import assertk.assertions.isTrue -import assertk.assertions.size -import net.thauvin.erik.mobibot.Constants -import kotlin.test.Test - -class LinksManagerTest { - private val linksManager = LinksManager() - - @Test - fun fetchTitle() { - assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") - assertThat( - linksManager.fetchTitle("https://www.google.com/foo"), - "fetchTitle(Foo)" - ).isEqualTo(Constants.NO_TITLE) - } - - @Test - fun testMatches() { - assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue() - assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue() - } - - @Test - fun matchTagKeywordsTest() { - linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3") - val tags = mutableListOf() - - linksManager.matchTagKeywords("Test title with key2", tags) - assertThat(tags, "tags").contains("key2") - tags.clear() - - linksManager.matchTagKeywords("Test key3 title with key1", tags) - assertThat(tags, "tags(key1, key3)").all { - contains("key1") - contains("key3") - size().isEqualTo(2) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt deleted file mode 100644 index e315891..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * ViewTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.links - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.prop -import net.thauvin.erik.mobibot.entries.EntryLink -import kotlin.test.Test - -class ViewTest { - @Test - fun testParseArgs() { - val view = View() - - for (i in 1..10) { - LinksManager.entries.links.add( - EntryLink( - "https://www.example.com/$i", - "Example $i", - "nick$i", - "login$i", - "#channel", - emptyList() - ) - ) - } - - assertThat(view.parseArgs("1"), "parseArgs(1)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("") - } - - assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all { - prop(Pair::first).isEqualTo(1) - prop(Pair::second).isEqualTo("foo") - } - - assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all { - prop(Pair::first).isEqualTo(2) - prop(Pair::second).isEqualTo("foo") - } - - assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all { - prop(Pair::first).isEqualTo(3) - prop(Pair::second).isEqualTo("foo bar") - } - - assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("foo bar") - } - - assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("${Int.MAX_VALUE}1") - } - - assertThat(view.parseArgs("1a"), "parseArgs(1a)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("1a") - } - - assertThat(view.parseArgs("20"), "parseArgs(20)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("") - } - - assertThat(view.parseArgs(""), "parseArgs()").all { - prop(Pair::first).isEqualTo(LinksManager.entries.links.size - View.MAX_ENTRIES) - prop(Pair::second).isEqualTo("") - } - - LinksManager.entries.links.clear() - - assertThat(view.parseArgs("4"), "parseArgs(4)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("") - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt deleted file mode 100644 index 37b884b..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SeenTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.seen - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import kotlin.test.Test -import kotlin.io.path.deleteIfExists -import kotlin.io.path.fileSize - -class SeenTest { - private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") - private val seen = Seen(tmpFile.toAbsolutePath().toString()) - private val nick = "ErikT" - - @BeforeClass - fun saveTest() { - seen.add("ErikT") - assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) - } - - @AfterClass(alwaysRun = true) - fun afterClass() { - tmpFile.deleteIfExists() - } - - @Test(priority = 1, groups = ["commands"]) - fun loadTest() { - seen.clear() - assertThat(seen::seenNicks).isEmpty() - seen.load() - assertThat(seen::seenNicks).key(nick).isNotNull() - } - - @Test - fun addTest() { - val last = seen.seenNicks[nick]?.lastSeen - seen.add(nick.lowercase()) - assertThat(seen).all { - prop(Seen::seenNicks).size().isEqualTo(1) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(nick.lowercase()) - } - } - - @Test(priority = 10, groups = ["commands"]) - fun clearTest() { - seen.clear() - seen.save() - seen.load() - assertThat(seen::seenNicks).size().isEqualTo(0) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt deleted file mode 100644 index cc09237..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * TellMessageTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.commands.tell - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isFalse -import assertk.assertions.isTrue -import assertk.assertions.prop -import kotlin.test.Test -import java.time.Duration -import java.time.LocalDateTime -import java.time.temporal.Temporal - -/** - * The `TellMessageTest` class. - */ -class TellMessageTest { - private fun isValidDate(date: Temporal): Boolean { - return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 - } - - @Test - fun testTellMessage() { - val message = "Test message." - val recipient = "recipient" - val sender = "sender" - val tellMessage = TellMessage(sender, recipient, message) - assertThat(tellMessage).all { - prop(TellMessage::sender).isEqualTo(sender) - prop(TellMessage::recipient).isEqualTo(recipient) - prop(TellMessage::message).isEqualTo(message) - } - assertThat(isValidDate(tellMessage.queued), "isValidDate()").isTrue() - assertThat(tellMessage.isMatch(sender), "isMatch(sender)").isTrue() - assertThat(tellMessage.isMatch(recipient), "isMatch(recipient)").isTrue() - assertThat(tellMessage.isMatch("foo"), "isMatch(foo)").isFalse() - tellMessage.isReceived = false - assertThat(tellMessage.receptionDate, "receptionDate").isEqualTo(LocalDateTime.MIN) - tellMessage.isReceived = true - assertThat(isValidDate(tellMessage.receptionDate), "isValidDate(creationDate)").isTrue() - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt deleted file mode 100644 index 5acba78..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * TellMessagesMgrTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.commands.tell - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import kotlin.test.Test -import java.time.LocalDateTime -import kotlin.io.path.createTempFile -import kotlin.io.path.deleteIfExists -import kotlin.io.path.fileSize - -class TellMessagesMgrTest { - private val testFile = createTempFile(suffix = ".ser") - private val maxDays = 10L - private val testMessages = mutableListOf().apply { - for (i in 0..5) { - this.add(i, TellMessage("sender$i", "recipient$i", "message $i")) - } - } - - @BeforeClass - fun saveTest() { - TellManager.save(testFile.toAbsolutePath().toString(), testMessages) - assertThat(testFile.fileSize()).isGreaterThan(0) - } - - @AfterClass - fun afterClass() { - testFile.deleteIfExists() - } - - @Test - fun cleanTest() { - testMessages.add(TellMessage("sender", "recipient", "message").apply { - queued = LocalDateTime.now().minusDays(maxDays) - }) - val size = testMessages.size - assertThat(TellManager.clean(testMessages, maxDays + 2), "clean(maxDays=${maxDays + 2})").isFalse() - assertThat(TellManager.clean(testMessages, maxDays), "clean(maxDays=$maxDays)").isTrue() - assertThat(testMessages, "testMessages").size().isEqualTo(size - 1) - } - - @Test - fun loadTest() { - val messages = TellManager.load(testFile.toAbsolutePath().toString()) - for (i in messages.indices) { - assertThat(messages).index(i).all { - prop(TellMessage::sender).isEqualTo(testMessages[i].sender) - prop(TellMessage::recipient).isEqualTo(testMessages[i].recipient) - prop(TellMessage::message).isEqualTo(testMessages[i].message) - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt deleted file mode 100644 index 0ad26b5..0000000 --- a/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * EntriesUtilsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries - -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isEqualTo -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment -import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink -import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import kotlin.test.Test - -class EntriesUtilsTest { - private val comment = EntryComment("comment", "nick") - private val links = buildList { - for (i in 0..5) { - add( - EntryLink( - "https://www.mobitopia.org/$i", - "Mobitopia$i", - "Skynx$i", - "JimH$i", - "#mobitopia$i", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) - ) - } - } - - @Test - fun printCommentTest() { - assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") - } - - @Test - fun printLinkTest() { - for (i in links.indices) { - assertThat( - printLink(i - 1, links[i]), "link $i" - ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") - } - - assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0) - assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]") - } - - @Test - fun printTagsTest() { - for (i in links.indices) { - assertThat( - printTags(i - 1, links[i]), "tag $i" - ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") - } - } - - @Test - fun toLinkLabelTest() { - assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt deleted file mode 100644 index f421099..0000000 --- a/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * EntryLinkTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.entries - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import com.rometools.rome.feed.synd.SyndCategory -import com.rometools.rome.feed.synd.SyndCategoryImpl -import kotlin.test.Test -import java.security.SecureRandom -import java.util.* - -/** - * The `EntryUtilsTest` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-19 - * @since 1.0 - */ -class EntryLinkTest { - private val entryLink = EntryLink( - "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) - - @Test - fun testAddDeleteComment() { - var i = 0 - while (i < 5) { - entryLink.addComment("c$i", "u$i") - i++ - } - assertThat(entryLink.comments, "comments").size().isEqualTo(i) - i = 0 - for (comment in entryLink.comments) { - assertThat(comment).all { - prop(EntryComment::comment).isEqualTo("c$i") - prop(EntryComment::nick).isEqualTo("u$i") - } - i++ - } - - val r = SecureRandom() - while (entryLink.comments.size > 0) { - entryLink.deleteComment(r.nextInt(entryLink.comments.size)) - } - assertThat(entryLink.comments, "hasComments()").isEmpty() - entryLink.addComment("nothing", "nobody") - entryLink.setComment(0, "something", "somebody") - val comment = entryLink.getComment(0) - assertThat(comment, "comment[first]").all { - prop(EntryComment::nick).isEqualTo("somebody") - prop(EntryComment::comment).isEqualTo("something") - } - assertThat(entryLink.deleteComment(comment), "deleteComment").isTrue() - assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse() - } - - @Test - fun testConstructor() { - val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" }) - val link = EntryLink("link", "title", "nick", "channel", Date(), tags) - assertThat(link, "link").all { - prop(EntryLink::tags).size().isEqualTo(tags.size) - prop(EntryLink::tags).index(0).prop(SyndCategory::getName).isEqualTo("tag1") - } - } - - @Test - fun testMatches() { - assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue() - assertThat(entryLink.matches("skynx"), "match(nick)").isTrue() - assertThat(entryLink.matches("www.mobitopia.org"), "matches(url)").isTrue() - assertThat(entryLink.matches("foo"), "matches(foo)").isFalse() - assertThat(entryLink.matches(""), "matches(empty)").isFalse() - assertThat(entryLink.matches(null), "matches(null)").isFalse() - } - - - @Test - fun testTags() { - val tags: List = entryLink.tags - for ((i, tag) in tags.withIndex()) { - assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}") - } - assertThat(entryLink::tags).size().isEqualTo(5) - entryLink.setTags("-tag5, tag4") - entryLink.setTags("+mobitopia") - entryLink.setTags("-mobitopia") - assertThat( - entryLink.formatTags(","), - "formatTags(',')" - ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") - entryLink.setTags("-tag4 tag5") - assertThat( - entryLink.formatTags(" ", ","), "formatTag(' ',',')" - ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") - val size = entryLink.tags.size - entryLink.setTags("") - assertThat(entryLink.tags, "setTags('')").size().isEqualTo(size) - entryLink.setTags(" ") - assertThat(entryLink.tags, "setTags(' ')").size().isEqualTo(size) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt deleted file mode 100644 index 1611009..0000000 --- a/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * FeedMgrTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.entries - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.Utils.today -import org.testng.annotations.BeforeSuite -import kotlin.test.Test -import java.nio.file.Paths -import java.util.* -import kotlin.io.path.deleteIfExists -import kotlin.io.path.fileSize -import kotlin.io.path.name - -class FeedMgrTest { - private val entries = Entries() - private val channel = "mobibot" - - @BeforeSuite(alwaysRun = true) - fun beforeSuite() { - entries.logsDir = "src/test/resources/" - entries.ircServer = "irc.example.com" - entries.channel = channel - entries.backlogs = "https://www.mobitopia.org/mobibot/logs" - } - - @Test - fun testFeedMgr() { - // Load the feed - assertThat(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31") - - assertThat(entries.links, "entries.links").size().isEqualTo(2) - entries.links.forEachIndexed { i, entryLink -> - assertThat(entryLink, "entryLink[${i + 1}]").all { - prop(EntryLink::title).isEqualTo("Example ${i + 1}") - prop(EntryLink::link).isEqualTo("https://www.example.com/${i + 1}") - prop(EntryLink::channel).isEqualTo(channel) - } - entryLink.tags.forEachIndexed { y, tag -> - assertThat(tag.name, "tag${i + 1}-${y + 1}").isEqualTo("tag${i + 1}-${y + 1}") - } - } - - with(entries.links.first()) { - assertThat(nick, "nick[first]").isEqualTo("ErikT") - assertThat(date, "date[first]").isEqualTo(Date(1635638400000L)) - assertThat(comments.first(), "comments[first]").all { - prop(EntryComment::comment).endsWith("comment 1.") - prop(EntryComment::nick).isEqualTo("ErikT") - } - assertThat(comments.last(), "comments[last]").all { - prop(EntryComment::comment).endsWith("comment 2.") - prop(EntryComment::nick).isEqualTo("Skynx") - } - } - - assertThat(entries.links, "links").index(1).all { - prop(EntryLink::nick).isEqualTo("Skynx") - prop(EntryLink::date).isEqualTo(Date(1635638460000L)) - } - - val currentFile = Paths.get("${entries.logsDir}test.xml") - val backlogFile = Paths.get("${entries.logsDir}${today()}.xml") - - // Save the feed - FeedsManager.saveFeed(entries, currentFile.name) - - assertThat(currentFile, "currentFile").exists() - assertThat(backlogFile, "backlogFile").exists() - - assertThat(currentFile.fileSize(), "currentFile == backlogFile").isEqualTo(backlogFile.fileSize()) - - // Load the test feed - entries.links.clear() - FeedsManager.loadFeed(entries, currentFile.name) - - entries.links.forEachIndexed { i, entryLink -> - assertThat(entryLink.title, "entryLink.title[${i + 1}]").isEqualTo("Example ${i + 1}") - } - - assertThat(currentFile.deleteIfExists(), "currentFile.deleteIfExists()").isTrue() - assertThat(backlogFile.deleteIfExists(), "backlogFile.deleteIfExists()").isTrue() - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt deleted file mode 100644 index 3bd4c0f..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * CalcTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isInstanceOf -import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate -import kotlin.test.Test - -/** - * The `CalcTest` class. - */ -class CalcTest { - @Test - fun testCalculate() { - assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") - assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") - assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}") - assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt deleted file mode 100644 index 085f228..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * ChatGptTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.hasNoCause -import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.LocalProperties -import kotlin.test.Test - -class ChatGptTest : LocalProperties() { - @Test - fun testApiKey() { - assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } - .isInstanceOf(ModuleException::class.java) - .hasNoCause() - } - - @Test(groups = ["modules", "no-ci"]) - fun testChat() { - val apiKey = getProperty(ChatGpt.API_KEY_PROP) - assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) - ).contains("XMLHttpRequest") - assertThat( - ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) - ).contains("URLEncoder") - - assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isInstanceOf(ModuleException::class.java) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt deleted file mode 100644 index d0ecbc9..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * CryptoPricesTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isGreaterThan -import assertk.assertions.prop -import net.thauvin.erik.crypto.CryptoPrice -import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice -import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName -import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies -import org.testng.annotations.BeforeClass -import kotlin.test.Test - -/** - * The `CryptoPricesTest` class. - */ -class CryptoPricesTest { - @BeforeClass - @Throws(ModuleException::class) - fun before() { - loadCurrencies() - } - - @Test - @Throws(ModuleException::class) - fun testMarketPrice() { - var price = currentPrice(listOf("BTC")) - assertThat(price, "currentPrice(BTC)").all { - prop(CryptoPrice::base).isEqualTo("BTC") - prop(CryptoPrice::currency).isEqualTo("USD") - prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) - } - - price = currentPrice(listOf("ETH", "EUR")) - assertThat(price, "currentPrice(ETH, EUR)").all { - prop(CryptoPrice::base).isEqualTo("ETH") - prop(CryptoPrice::currency).isEqualTo("EUR") - prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) - } - } - - @Test - fun testGetCurrencyName() { - assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") - assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt deleted file mode 100644 index 8d8c997..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * CurrencyConverterTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isInstanceOf -import assertk.assertions.matches -import assertk.assertions.prop -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency -import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage -import org.testng.annotations.BeforeClass -import kotlin.test.Test - - -/** - * The `CurrencyConvertTest` class. - */ -class CurrencyConverterTest : LocalProperties() { - - @BeforeClass - @Throws(ModuleException::class) - fun before() { - val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) - loadSymbols(apiKey) - } - - @Test - fun testConvertCurrency() { - val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) - assertThat( - convertCurrency(apiKey, "100 USD to EUR").msg, - "convertCurrency(100 USD to EUR)" - ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) - assertThat( - convertCurrency(apiKey, "1 USD to GBP").msg, - "convertCurrency(1 USD to BGP)" - ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) - assertThat( - convertCurrency(apiKey, "100,000.00 CAD to USD").msg, - "convertCurrency(100,000.00 GBP to USD)" - ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) - assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all { - prop(Message::msg).contains("You're kidding, right?") - isInstanceOf(PublicMessage::class.java) - } - assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all { - prop(Message::msg).contains("Invalid query.") - isInstanceOf(ErrorMessage::class.java) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt deleted file mode 100644 index 8bafede..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * DiceTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules - - -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.matches -import kotlin.test.Test - -class DiceTest { - @Test - fun testRoll() { - assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") - assertThat(Dice.roll(2, 1), "roll(2d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") - assertThat(Dice.roll(5, 1), "roll(5d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") - assertThat(Dice.roll(2, 6), "roll(2d6)") - .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) - assertThat(Dice.roll(3, 7), "roll(3d7)") - .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt deleted file mode 100644 index 85c990c..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * GoogleSearchTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test - -/** - * The `GoogleSearchTest` class. - */ -class GoogleSearchTest : LocalProperties() { - @Test - fun testAPIKeys() { - assertThat( - searchGoogle("", "apikey", "cssKey").first(), - "searchGoogle(empty)" - ).isInstanceOf(ErrorMessage::class.java) - - assertFailure { searchGoogle("test", "", "apiKey") } - .isInstanceOf(ModuleException::class.java).hasNoCause() - - assertFailure { searchGoogle("test", "apiKey", "") } - .isInstanceOf(ModuleException::class.java).hasNoCause() - - assertFailure { searchGoogle("test", "apiKey", "cssKey") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("API key not valid. Please pass a valid API key.") - } - - @Test\n@DisableOnCi - @Throws(ModuleException::class) - fun testSearchGoogle() { - val apiKey = getProperty(GoogleSearch.API_KEY_PROP) - val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP) - - try { - var query = "mobibot" - var messages = searchGoogle(query, apiKey, cseKey) - assertThat(messages, "searchGoogle($query)").all { - isNotEmpty() - index(0).prop(Message::msg).contains(query, true) - } - - query = "adadflkjl" - messages = searchGoogle(query, apiKey, cseKey) - assertThat(messages, "searchGoogle($query)").index(0).all { - isInstanceOf(ErrorMessage::class.java) - prop(Message::msg).isEqualTo("No results found.") - } - } catch (e: ModuleException) { - // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI")) { - throw e.sanitize(apiKey, cseKey) - } else { - throw e - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt deleted file mode 100644 index 0af8e75..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * JokeTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage -import kotlin.test.Test - -/** - * The `JokeTest` class. - */ -class JokeTest { - @Test - @Throws(ModuleException::class) - fun testRandomJoke() { - val joke = randomJoke() - assertThat(joke, "randomJoke()").all { - size().isGreaterThan(0) - each { - it.isInstanceOf(PublicMessage::class.java) - it.prop(Message::msg).doesNotContain("\n") - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt deleted file mode 100644 index a3fc226..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * LookupTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.assertThat -import assertk.assertions.any -import assertk.assertions.contains -import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup -import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois -import kotlin.test.Test - -/** - * The `Lookup Test` class. - */ -class LookupTest { - @Test - @Throws(Exception::class) - fun testLookup() { - var result = nslookup("apple.com") - assertThat(result, "lookup(apple.com)").contains("17.253.144.10") - - result = nslookup("204.122.16.136") - assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") - } - - @Test - @Throws(Exception::class) - fun testWhois() { - val result = whois("17.178.96.59", Lookup.WHOIS_HOST) - assertThat(result, "whois(17.178.96.59").any { it.contains("Apple Inc.") } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt deleted file mode 100644 index f4b5e99..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * MastodonTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.assertThat -import assertk.assertions.contains -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot -import kotlin.test.Test - -class MastodonTest : LocalProperties() { - @Test - @Throws(ModuleException::class) - fun testToot() { - val msg = "Testing Mastodon API from ${getHostName()}" - assertThat( - toot( - getProperty(Mastodon.ACCESS_TOKEN_PROP), - getProperty(Mastodon.INSTANCE_PROP), - getProperty(Mastodon.HANDLE_PROP), - msg, - true - ) - ).contains(msg) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt deleted file mode 100644 index 1416eec..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * ModuleExceptionTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import org.testng.annotations.DataProvider -import kotlin.test.Test -import java.io.IOException -import java.lang.reflect.Method - -/** - * The `ModuleExceptionTest` class. - */ -class ModuleExceptionTest { - companion object { - const val DEBUG_MESSAGE = "debugMessage" - const val MESSAGE = "message" - } - - @DataProvider(name = "dp") - fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array> { - return arrayOf( - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) - ) - } - - @Test(dataProvider = "dp") - fun testGetDebugMessage(e: ModuleException) { - assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) - } - - @Test(dataProvider = "dp") - fun testGetMessage(e: ModuleException) { - assertThat(e).hasMessage(MESSAGE) - } - - @Test - fun testSanitizeMessage() { - val apiKey = "1234567890" - var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) - assertThat( - e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" - ).isNotNull().all { - contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") - doesNotContain(apiKey, "me") - } - - e = ModuleException(DEBUG_MESSAGE, MESSAGE, null) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE) - - e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException()) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE) - - e = ModuleException(DEBUG_MESSAGE, apiKey) - assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() - .doesNotContain(apiKey) - - val msg: String? = null - e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg)) - assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull() - - e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey")) - assertThat( - e.sanitize(" ", apiKey, "foo").message, - "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" - ).isNotNull().all { - doesNotContain(apiKey) - endsWith("xxx is xxxxxxxxxx") - } - assertThat(e.sanitize(), "exception should be unchanged").isEqualTo(e) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt deleted file mode 100644 index d1399e0..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * PingTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isNotEmpty -import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing -import kotlin.test.Test - -/** - * The `PingTest` class. - */ -class PingTest { - @Test - fun testPingsArray() { - assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty() - } - - @Test - fun testRandomPing() { - for (i in 0..9) { - assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing()) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt deleted file mode 100644 index f836b0e..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * RockPaperScissorsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules - -import assertk.assertThat -import assertk.assertions.isEqualTo -import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw -import kotlin.test.Test - -class RockPaperScissorsTest { - @Test - fun testWinLoseOrDraw() { - assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win") - assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win") - assertThat(winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win") - assertThat(winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose") - assertThat(winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose") - assertThat(winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose") - assertThat(winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt deleted file mode 100644 index d96812b..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * StockQuoteTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test - -/** - * The `StockQuoteTest` class. - */ -class StockQuoteTest : LocalProperties() { - private fun buildMatch(label: String): String { - return "${label}:[ ]+[0-9.]+".prependIndent() - } - - @Test - @Throws(ModuleException::class) - fun testGetQuote() { - val apiKey = getProperty(StockQuote.API_KEY_PROP) - try { - var symbol = "apple inc" - val messages = getQuote(symbol, apiKey) - assertThat(messages, "response not empty").isNotEmpty() - assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) - assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) - assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) - .matches(buildMatch("Previous").toRegex()) - assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) - - symbol = "blahfoo" - assertThat(getQuote(symbol, apiKey).first(), "getQuote($symbol)").all { - isInstanceOf(ErrorMessage::class.java) - prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) - } - assertThat(getQuote("", "apikey").first(), "getQuote(empty)").all { - isInstanceOf(ErrorMessage::class.java) - prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) - } - assertFailure { getQuote("test", "") }.isInstanceOf(ModuleException::class.java).hasNoCause() - } catch (e: ModuleException) { - // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI")) { - throw e.sanitize(apiKey) - } else { - throw e - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt deleted file mode 100644 index 66f4d22..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Weather2Test.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import net.aksingh.owmjapis.api.APIException -import net.aksingh.owmjapis.core.OWM -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP -import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC -import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry -import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather -import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh -import net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test - -/** - * The `Weather2Test` class. - */ -class Weather2Test : LocalProperties() { - @Test - fun testFtoC() { - val t = ftoC(32.0) - assertThat(t.second, "32 °F is 0 °C").isEqualTo(0) - } - - @Test - fun testGetCountry() { - assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) - assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) - - val country = OWM.Country.entries.toTypedArray() - repeat(3) { - val rand = country[(country.indices).random()] - assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) - } - } - - @Test - fun testMphToKmh() { - val w = mphToKmh(0.62) - assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1) - } - - @Test - @Throws(ModuleException::class) - fun testWeather() { - var query = "98204" - var messages = getWeather(query, getProperty(API_KEY_PROP)) - assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { - contains("Everett, United States") - contains("US") - } - assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS") - - query = "San Francisco" - messages = getWeather(query, getProperty(API_KEY_PROP)) - assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { - contains("San Francisco") - contains("US") - } - assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959") - - query = "London, GB" - messages = getWeather(query, getProperty(API_KEY_PROP)) - assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { - contains("London, United Kingdom") - contains("GB") - } - assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("2643743") - - try { - query = "Foo, US" - getWeather(query, getProperty(API_KEY_PROP)) - } catch (e: ModuleException) { - assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java) - } - - query = "test" - assertFailure { getWeather(query, "") }.isInstanceOf(ModuleException::class.java).hasNoCause() - assertFailure { getWeather(query, null) }.isInstanceOf(ModuleException::class.java).hasNoCause() - - messages = getWeather("", "apikey") - assertThat(messages, "getWeather(empty)").index(0).prop(Message::isError).isTrue() - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt deleted file mode 100644 index 855522c..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * WolframAlphaTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.modules - -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.hasMessage -import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram -import kotlin.test.Test - -class WolframAlphaTest : LocalProperties() { - @Test - fun testAppId() { - assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("Error 1: Invalid appid") - - assertFailure { queryWolfram("1 gallon to liter", appId = "") } - .isInstanceOf(ModuleException::class.java) - } - - @Test(groups = ["modules", "no-ci"]) - @Throws(ModuleException::class) - fun queryWolframTest() { - val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) - try { - var query = "SFO to SEA" - assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles") - - query = "SFO to LAX" - assertThat( - queryWolfram(query, WolframAlpha.METRIC, apiKey), - "queryWolfram($query)" - ).contains("kilometers") - } catch (e: ModuleException) { - // Avoid displaying api key in CI logs - if ("true" == System.getenv("CI")) { - throw e.sanitize(apiKey) - } else { - throw e - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt deleted file mode 100644 index 7931f33..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * WordTimeTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.thauvin.erik.mobibot.modules - -import assertk.assertThat -import assertk.assertions.endsWith -import assertk.assertions.matches -import assertk.assertions.startsWith -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD -import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP -import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time -import org.pircbotx.Colors -import kotlin.test.Test -import java.time.ZoneId - -/** - * The `WordTimeTest` class. - */ -class WordTimeTest { - @Test - fun testTime() { - assertThat(time(), "time()").matches( - ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + - "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + - "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() - ) - assertThat(time(""), "time()").endsWith("Los Angeles".bold()) - assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) - assertThat(time("GB"), "time(GB)").endsWith("London".bold()) - assertThat(time("FR"), "time(FR)").endsWith("Paris".bold()) - assertThat(time("BLAH"), "time(BLAH)").startsWith("Unsupported") - assertThat(time("BEAT"), "time($BEATS_KEYWORD)").matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) - } - - @Test - fun testZones() { - COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { - assertThat(ZoneId.of(it.value)) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt b/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt deleted file mode 100644 index 32a0495..0000000 --- a/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * MessageTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package net.thauvin.erik.mobibot.msg - -import assertk.all -import assertk.assertThat -import assertk.assertions.isFalse -import assertk.assertions.isTrue -import assertk.assertions.prop -import kotlin.test.Test - -class MessageTest { - @Test - fun testConstructor() { - var msg = Message("foo") - - msg.isError = true - assertThat(msg.isNotice, "message is notice").isTrue() - - msg = Message("foo", isError = true) - assertThat(msg.isNotice, "message is notice too").isTrue() - } - - @Test - fun testErrorMessage() { - val msg = ErrorMessage("foo") - assertThat(msg).all { - prop(Message::isError).isTrue() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - } - - @Test - fun testIsError() { - val msg = Message("foo") - msg.isError = true - assertThat(msg).all { - prop(Message::isError).isTrue() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - msg.isError = false - assertThat(msg).all { - prop(Message::isError).isFalse() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - } - - @Test - fun testNoticeMessage() { - val msg = NoticeMessage("food") - assertThat(msg).all { - prop(Message::isError).isFalse() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - } - - @Test - fun testPrivateMessage() { - val msg = PrivateMessage("foo") - assertThat(msg).all { - prop(Message::isPrivate).isTrue() - prop(Message::isError).isFalse() - prop(Message::isNotice).isFalse() - } - } - - @Test - fun testPublicMessage() { - val msg = PublicMessage("foo") - assertThat(msg).all { - prop(Message::isError).isFalse() - prop(Message::isNotice).isFalse() - prop(Message::isPrivate).isFalse() - } - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 88634ae..01f14f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,11 +14,11 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231110234054" + const val VERSION = "0.8.0-rc+20231111131146" @JvmField val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1699688454937L), ZoneId.systemDefault() + Instant.ofEpochMilli(1699737106723L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml deleted file mode 100644 index 16644cc..0000000 --- a/src/main/resources/log4j2.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a40d20458bbd889ff195cb5108640cdcfa993ff8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 12 Nov 2023 14:19:38 -0800 Subject: [PATCH 740/842] Updated copyright --- .github/workflows/{gradle.yml => bld.yml} | 0 .idea/copyright/BSD_3.xml | 2 +- .idea/libraries/bld.xml | 3 +- .idea/misc.xml | 11 ++++++ .../java/net/thauvin/erik/MobibotBuild.java | 3 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 35 +++++++++++++++++-- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 2 +- .../erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 2 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../erik/mobibot/DisableOnCiCondition.kt | 2 +- .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 2 +- .../erik/mobibot/commands/RecapTest.kt | 2 +- .../commands/links/LinksManagerTest.kt | 2 +- .../erik/mobibot/commands/links/ViewTest.kt | 2 +- .../erik/mobibot/commands/seen/SeenTest.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 2 +- .../commands/tell/TellMessagesMgrTest.kt | 2 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 2 +- .../erik/mobibot/entries/EntryLinkTest.kt | 2 +- .../erik/mobibot/entries/FeedMgrTest.kt | 2 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../erik/mobibot/modules/ChatGptTest.kt | 2 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- .../mobibot/modules/CurrencyConverterTest.kt | 2 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 2 +- .../erik/mobibot/modules/MastodonTest.kt | 2 +- .../mobibot/modules/ModuleExceptionTest.kt | 2 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/Weather2Test.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- .../erik/mobibot/modules/WordTimeTest.kt | 2 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 2 +- 103 files changed, 146 insertions(+), 102 deletions(-) rename .github/workflows/{gradle.yml => bld.yml} (100%) diff --git a/.github/workflows/gradle.yml b/.github/workflows/bld.yml similarity index 100% rename from .github/workflows/gradle.yml rename to .github/workflows/bld.yml diff --git a/.idea/copyright/BSD_3.xml b/.idea/copyright/BSD_3.xml index 275eca9..3c57002 100644 --- a/.idea/copyright/BSD_3.xml +++ b/.idea/copyright/BSD_3.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index cf75013..ca84ff0 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -6,6 +6,7 @@ + @@ -14,4 +15,4 @@ - \ No newline at end of file + diff --git a/.idea/misc.xml b/.idea/misc.xml index e853f87..8232032 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,6 +6,17 @@ + diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 9b6b32a..e7dbde3 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -1,7 +1,7 @@ /* * MobibotBuild.java * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -89,6 +89,7 @@ public class MobibotBuild extends Project { .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) + // Misc. .include(dependency("com.rometools", "rome", "2.1.0")) .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index c6f16cb..2c5f05d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index ea89f4c..98ef74a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 371b523..d82f011 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 47dd7c2..ac4d6af 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index ac01b0a..7cb5aed 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 01f14f3..7a3d41c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,3 +1,34 @@ +/* + * ReleaseInfo.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -14,11 +45,11 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231111131146" + const val VERSION = "0.8.0-rc+20231111131825" @JvmField val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1699737106723L), ZoneId.systemDefault() + Instant.ofEpochMilli(1699737505341L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index d69c0c5..e4760d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index e0b091a..5f79472 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 9084660..038e378 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 18a8b1d..9608ca8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index c1708a9..f271bfa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 8d2b154..d083c10 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 8ee8c4f..ed0b6ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index f2a13ba..ec7823b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 1aaefd7..b2293b0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 0f492c9..20a6635 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 41b2c4b..85a03ab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 0159017..77154c7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 1f89982..7f76d35 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 4a881a7..33d6fef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index a8300f0..802bbde 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 90d8bb3..1443d44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index 2d7add6..fba6b99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index dca99cf..ff4278d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index c59cad9..1662857 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 1626a58..825e374 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index 2a3856b..cfd2c27 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 83083fd..c9ee0f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 1445f9f..7924977 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index e9d18ae..061ca6a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index 229bc5f..b65a4da 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index cc48d59..d17fbb5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index 15506e0..e8676ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 6e58fd9..9c09626 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index c0cc2a8..e18d692 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 8098bbf..4a69446 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index e7017ab..f786cb2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 8dcf6d8..8c8e736 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 9003783..b7aae28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 09791a8..bd92332 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,7 +1,7 @@ /* * ChatGpt.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 4464732..4614a4c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 954f4d1..da0efd8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 84f2280..8420fb1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index a9b15a5..f426d1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 91b9ad2..2760fa7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index eecc55e..9ab2ead 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 1141c0e..3be3a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 9a0e641..a7416c2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 86b311a..944dbc1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index d224568..a299d8d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 6b591cd..dcae5e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt index 0c64465..8799279 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -1,7 +1,7 @@ /* * War.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 83eba95..80a06fa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 8d9c4e6..a72efab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 0c379b3..18072bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index b9f5212..0607936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 84e3a1e..d41ec8c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 2be52aa..037d504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 8d34d28..b424fdf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 9e67ecb..9c5e088 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index 3fb3874..91f2dd9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index 47a33d9..b594670 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index e779a3c..3fd315e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 7f65548..9dd6034 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 01d98ca..2fbcb71 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -1,7 +1,7 @@ /* * DisableOnCi.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt index 24f1ca0..1d9baf4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -1,7 +1,7 @@ /* * DisableOnCiCondition.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index b02188e..a3994ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index cfc3d4e..46f9368 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 95b6c08..c039d74 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index cf9d59a..3b81950 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index a024cff..682b350 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 33a411f..99ba951 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 34dce0e..b6fbbff 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index eea09db..fa2bb70 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index b5028e3..e315891 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 331f97c..52810eb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index f2c6f35..b28c45a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index 9ab1bce..e3c9c1f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 8e9bd6f..0ad26b5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 495ed15..5028e6e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index de6075d..b56e6ed 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 6df5616..3bd4c0f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 00fa43f..dc1133a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,7 +1,7 @@ /* * ChatGptTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 9847080..3eaa4c5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 57affe5..a177715 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 0aaacb9..8bafede 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index b0c154a..93c2560 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 9b8dcb3..0af8e75 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index e67c8ad..a3fc226 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index d189d75..f4b5e99 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 9dda5b3..efc267a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index a6ff707..d1399e0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index fb8865b..f836b0e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index ae8cff2..d96812b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index e135866..66f4d22 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 417f9fc..5faffc2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 5c8cc84..35368b2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 477c1a4..32a0495 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: From 44c1e8db3e98a238d462721f27ce82330acab99a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 12 Nov 2023 16:06:40 -0800 Subject: [PATCH 741/842] Fixed bld-ci badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbcacc2..041f0dc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.20-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) -[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) +[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: From e34ea334d66885c7e2a06df7103fc58247282496 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 25 Nov 2023 18:08:13 -0800 Subject: [PATCH 742/842] Added detekt extension --- README.md | 2 +- config/detekt/baseline.xml | 31 ++++++------- lib/bld/bld-wrapper.properties | 4 +- release_info.txt | 1 + .../java/net/thauvin/erik/MobibotBuild.java | 43 +++++++++++++------ .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 36 ++-------------- .../thauvin/erik/mobibot/commands/Ignore.kt | 1 - .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 3 -- .../net/thauvin/erik/mobibot/UtilsTest.kt | 3 -- .../erik/mobibot/commands/seen/SeenTest.kt | 14 +++--- .../mobibot/commands/tell/TellMessageTest.kt | 3 -- .../erik/mobibot/entries/EntryLinkTest.kt | 7 --- .../thauvin/erik/mobibot/modules/CalcTest.kt | 3 -- .../erik/mobibot/modules/ChatGptTest.kt | 4 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 3 -- .../mobibot/modules/CurrencyConverterTest.kt | 4 -- .../erik/mobibot/modules/GoogleSearchTest.kt | 7 +-- .../thauvin/erik/mobibot/modules/JokeTest.kt | 3 -- .../erik/mobibot/modules/LookupTest.kt | 3 -- .../mobibot/modules/ModuleExceptionTest.kt | 3 -- .../thauvin/erik/mobibot/modules/PingTest.kt | 3 -- .../erik/mobibot/modules/StockQuoteTest.kt | 3 -- .../erik/mobibot/modules/Weather2Test.kt | 3 -- .../erik/mobibot/modules/WolframAlphaTest.kt | 4 +- .../erik/mobibot/modules/WordTimeTest.kt | 3 -- 27 files changed, 68 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 041f0dc..fe1920f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.20-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 5c6be73..641e97c 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -1,15 +1,15 @@ - + - + CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) - CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> LongMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) - LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) fun main(args: Array<String>) - LongMethod:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> - LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) fun main(args: Array<String>) + LongMethod:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> + LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent ) - LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) + LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 MagicNumber:ChatGpt.kt$ChatGpt.Companion$429 @@ -52,22 +52,22 @@ NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?) NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message - NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>) + NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>) NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) NestedBlockDepth:GoogleSearch.kt$GoogleSearch$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) - NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> + NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> NestedBlockDepth:LinksManager.kt$LinksManager$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String NestedBlockDepth:Posting.kt$Posting$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) - NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> + NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> NestedBlockDepth:Tell.kt$Tell$fun send(event: GenericUserEvent) NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveSerialData(file: String, data: Any, logger: Logger, description: String) NestedBlockDepth:Weather2.kt$Weather2$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) - NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> ReturnCount:Addons.kt$Addons$fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException @@ -76,18 +76,19 @@ SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String - ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> - ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> + ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> + ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> ThrowsCount:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String - ThrowsCount:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> + ThrowsCount:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject - ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> ThrowsCount:WolframAlpha.kt$WolframAlpha.Companion$@JvmStatic @Throws(ModuleException::class) fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException TooManyFunctions:EntryLink.kt$EntryLink : Serializable TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter TooManyFunctions:Tell.kt$Tell : AbstractCommand + UtilityClassWithPublicConstructor:LocalProperties.kt$LocalProperties WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.* WildcardImport:EntryLinkTest.kt$import assertk.assertions.* WildcardImport:FeedMgrTest.kt$import assertk.assertions.* diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 8375666..e219f09 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,9 +1,9 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3-SNAPSHOT +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3 bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0-SNAPSHOT -bld.extensions-pmd=com.uwyn.rife2:bld-testng:0.9.2 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= diff --git a/release_info.txt b/release_info.txt index f5efde2..b00e5ba 100644 --- a/release_info.txt +++ b/release_info.txt @@ -17,6 +17,7 @@ object {{v className/}} { const val VERSION = "{{v version/}}" @JvmField + @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( Instant.ofEpochMilli({{v epoch/}}L), ZoneId.systemDefault() ) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index e7dbde3..d1c489a 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -35,9 +35,10 @@ import rife.bld.BuildCommand; import rife.bld.Project; import rife.bld.dependencies.Repository; import rife.bld.extension.CompileKotlinOperation; -import rife.bld.extension.CompileKotlinOptions; +import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; +import rife.bld.operations.exceptions.ExitStatusException; import rife.tools.FileUtils; import rife.tools.exceptions.FileUtilsErrorException; @@ -68,12 +69,13 @@ public class MobibotBuild extends Project { autoDownloadPurge = true; repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io")); - var log4j = version(2, 21, 1); + var log4j = version(2, 22, 0); + var kotlin = version(1, 9, 21); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) - .include(dependency("org.apache.commons", "commons-lang3", "3.13.0")) + .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) .include(dependency("org.apache.commons", "commons-text", "1.11.0")) .include(dependency("commons-codec", "commons-codec", "1.16.0")) .include(dependency("commons-net", "commons-net", "3.10.0")) @@ -81,7 +83,10 @@ public class MobibotBuild extends Project { .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "32.1.3-jre")) // Kotlin - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", version(1, 9, 20))) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging @@ -97,13 +102,13 @@ public class MobibotBuild extends Project { .include(dependency("org.json", "json", "20231013")) .include(dependency("org.jsoup", "jsoup", "1.16.2")) // Thauvin - .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.1")) - .include(dependency("net.thauvin.erik", "jokeapi", "0.9.0")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.0")) + .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.2-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "jokeapi", "0.9.1-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.1-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 27, 0))) - .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 20))) + .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 21))) .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); @@ -132,11 +137,6 @@ public class MobibotBuild extends Project { releaseInfo(); new CompileKotlinOperation() .fromProject(this) - .compileOptions( - new CompileKotlinOptions() - .jdkRelease(javaRelease) - .verbose(true) - ) .execute(); } @@ -150,6 +150,23 @@ public class MobibotBuild extends Project { FileUtils.copy(new File(buildDistDirectory(), jarFileName()), new File(deploy, "mobibot.jar")); } + @BuildCommand(summary = "Checks source with Detekt") + public void detekt() throws ExitStatusException, IOException, InterruptedException { + new DetektOperation() + .fromProject(this) + .baseline("config/detekt/baseline.xml") + .execute(); + } + + @BuildCommand(value = "detekt-baseline", summary = "Creates the Detekt baseline") + public void detektBaseline() throws ExitStatusException, IOException, InterruptedException { + new DetektOperation() + .fromProject(this) + .baseline("config/detekt/baseline.xml") + .createBaseline(true) + .execute(); + } + @BuildCommand(summary = "Generates JaCoCo Reports") public void jacoco() throws IOException { new JacocoReportOperation() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 98ef74a..d037e44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -63,7 +63,7 @@ object Constants { * User-Agent */ const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 7a3d41c..fbdfe48 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,34 +1,3 @@ -/* - * ReleaseInfo.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -45,11 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231111131825" + const val VERSION = "0.8.0-rc+20231125180722" @JvmField + @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1699737505341L), ZoneId.systemDefault() + Instant.ofEpochMilli(1700964443060L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index d083c10..2109693 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -143,5 +143,4 @@ class Ignore : AbstractCommand() { ignored.addAll(value.split(LinksManager.TAG_MATCH)) } } - } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 2fbcb71..22b0146 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -41,4 +41,4 @@ import org.junit.jupiter.api.extension.ExtendWith @Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @ExtendWith(DisableOnCiCondition::class) -annotation class DisabledOnCi +annotation class DisableOnCi diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 46f9368..bf4408f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -42,9 +42,6 @@ import java.net.MalformedURLException import java.net.UnknownHostException import kotlin.test.Test -/** - * The `FeedReader Test` class. - */ class FeedReaderTest { @Test fun readFeedTest() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 682b350..a65b3a5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -68,9 +68,6 @@ import java.time.LocalDateTime import java.util.* import kotlin.test.Test -/** - * The `Utils Test` class. - */ class UtilsTest { private val ascii = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 52810eb..62a1db7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -48,18 +48,18 @@ class SeenTest { seen.clear() assertThat(seen::seenNicks).isEmpty() seen.load() - assertThat(seen::seenNicks).key(nick).isNotNull() + assertThat(seen::seenNicks).key(NICK).isNotNull() } @Test @Order(2) fun addTest() { - val last = seen.seenNicks[nick]?.lastSeen - seen.add(nick.lowercase()) + val last = seen.seenNicks[NICK]?.lastSeen + seen.add(NICK.lowercase()) assertThat(seen).all { prop(Seen::seenNicks).size().isEqualTo(1) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(nick.lowercase()) + prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) + prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(NICK.lowercase()) } } @@ -75,12 +75,12 @@ class SeenTest { companion object { private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") private val seen = Seen(tmpFile.toAbsolutePath().toString()) - private const val nick = "ErikT" + private const val NICK = "ErikT" @JvmStatic @BeforeClass fun beforeClass() { - seen.add(nick) + seen.add(NICK) assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index b28c45a..69bc2e9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -41,9 +41,6 @@ import java.time.LocalDateTime import java.time.temporal.Temporal import kotlin.test.Test -/** - * The `TellMessageTest` class. - */ class TellMessageTest { private fun isValidDate(date: Temporal): Boolean { return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 5028e6e..3a3bdda 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -39,13 +39,6 @@ import java.security.SecureRandom import java.util.* import kotlin.test.Test -/** - * The `EntryUtilsTest` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-19 - * @since 1.0 - */ class EntryLinkTest { private val entryLink = EntryLink( "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 3bd4c0f..04afe8c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -39,9 +39,6 @@ import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate import kotlin.test.Test -/** - * The `CalcTest` class. - */ class CalcTest { @Test fun testCalculate() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index dc1133a..603c353 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -35,7 +35,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.DisabledOnCi +import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.LocalProperties import kotlin.test.Test @@ -48,7 +48,7 @@ class ChatGptTest : LocalProperties() { } @Test - @DisabledOnCi + @DisableOnCi fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 3eaa4c5..10b3a43 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -41,9 +41,6 @@ import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies import kotlin.test.Test -/** - * The `CryptoPricesTest` class. - */ class CryptoPricesTest { init { loadCurrencies() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index a177715..8ab64c0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -44,10 +44,6 @@ import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import kotlin.test.Test - -/** - * The `CurrencyConvertTest` class. - */ class CurrencyConverterTest : LocalProperties() { init { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 93c2560..4942266 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -34,7 +34,7 @@ import assertk.all import assertk.assertFailure import assertk.assertThat import assertk.assertions.* -import net.thauvin.erik.mobibot.DisabledOnCi +import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle @@ -42,9 +42,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import kotlin.test.Test -/** - * The `GoogleSearchTest` class. - */ class GoogleSearchTest : LocalProperties() { @Test fun testAPIKeys() { @@ -65,7 +62,7 @@ class GoogleSearchTest : LocalProperties() { } @Test - @DisabledOnCi + @DisableOnCi @Throws(ModuleException::class) fun testSearchGoogle() { val apiKey = getProperty(GoogleSearch.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 0af8e75..441d435 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -38,9 +38,6 @@ import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import kotlin.test.Test -/** - * The `JokeTest` class. - */ class JokeTest { @Test @Throws(ModuleException::class) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index a3fc226..8699c0f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -37,9 +37,6 @@ import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois import kotlin.test.Test -/** - * The `Lookup Test` class. - */ class LookupTest { @Test @Throws(Exception::class) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index efc267a..416b3ab 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -39,9 +39,6 @@ import org.junit.jupiter.params.provider.MethodSource import java.io.IOException import kotlin.test.Test -/** - * The `ModuleExceptionTest` class. - */ class ModuleExceptionTest { companion object { const val DEBUG_MESSAGE = "debugMessage" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index d1399e0..bbf9eb1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -36,9 +36,6 @@ import assertk.assertions.isNotEmpty import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing import kotlin.test.Test -/** - * The `PingTest` class. - */ class PingTest { @Test fun testPingsArray() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index d96812b..0816b17 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -41,9 +41,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import kotlin.test.Test -/** - * The `StockQuoteTest` class. - */ class StockQuoteTest : LocalProperties() { private fun buildMatch(label: String): String { return "${label}:[ ]+[0-9.]+".prependIndent() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 66f4d22..7ae2b83 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -45,9 +45,6 @@ import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh import net.thauvin.erik.mobibot.msg.Message import kotlin.test.Test -/** - * The `Weather2Test` class. - */ class Weather2Test : LocalProperties() { @Test fun testFtoC() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 5faffc2..3bfe0d1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -36,7 +36,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.DisabledOnCi +import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram @@ -54,7 +54,7 @@ class WolframAlphaTest : LocalProperties() { } @Test - @DisabledOnCi + @DisableOnCi @Throws(ModuleException::class) fun queryWolframTest() { val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 35368b2..ae40e3a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -42,9 +42,6 @@ import org.pircbotx.Colors import java.time.ZoneId import kotlin.test.Test -/** - * The `WordTimeTest` class. - */ class WordTimeTest { @Test fun testTime() { From 3a773f7d46accd1cdea0543c8b538f6fe5180d84 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 25 Nov 2023 18:14:41 -0800 Subject: [PATCH 743/842] Added snapshots repository --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index d1c489a..d8f52e4 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -50,8 +50,7 @@ import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; -import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; -import static rife.bld.dependencies.Repository.MAVEN_LOCAL; +import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; import static rife.bld.dependencies.Scope.test; @@ -67,7 +66,12 @@ public class MobibotBuild extends Project { javaRelease = 17; downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io")); + repositories = List.of( + MAVEN_LOCAL, + SONATYPE_SNAPSHOTS_LEGACY, + MAVEN_CENTRAL, + new Repository("https://jitpack.io") + ); var log4j = version(2, 22, 0); var kotlin = version(1, 9, 21); From 8412a8c26dbe8d78348eb28b2e9365f3916e101b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 26 Nov 2023 00:43:44 -0800 Subject: [PATCH 744/842] Removed snapshot repository --- .idea/misc.xml | 2 ++ src/bld/java/net/thauvin/erik/MobibotBuild.java | 16 +++++++--------- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 8232032..26293d3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,6 +4,8 @@ + +

  23. Apache Commons Net
  24. CryptoPrice
  25. exp4j
  26. +
  27. Google Vertex AI
  28. JokeAPI
  29. jsoup
  30. kotlinx-cli
  31. @@ -80,9 +81,10 @@
  32. Performing Google searches
    mobibot: google mobitopia on irc
  33. -
  34. Getting answers from Wolfram Alpha and ChatGPT +
  35. Getting answers from Wolfram Alpha, ChatGPT and Google Gemini
    mobibot: wolfram days until christmas
    mobibot: chatgpt explain quantum computing in simple terms
    +
    mobibot: gemini what are all the colors in a rainbow?
  36. Displaying weather information
    mobibot: weather san francisco
    From a10104dd9dc889841a92b5a4fb38d0fb34a97924 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 17 Dec 2023 22:11:56 -0800 Subject: [PATCH 746/842] Added website link to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index fe1920f..b360d96 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,5 @@ Some very basic instructions: # launch /usr/bin/nohup java -jar mobibot.jar & ``` + +For a listing of features, see the [website](https://mobitopia.org/mobibot/). From 356f62990d191a21b85b2dbce589dc7e4287f155 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 18 Dec 2023 14:14:08 -0800 Subject: [PATCH 747/842] Added support for ChatGPT 3.5 Turbo --- properties/mobibot.properties | 1 + .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +-- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 29 +++++++++---------- .../thauvin/erik/mobibot/modules/Gemini.kt | 14 ++++----- .../erik/mobibot/modules/ChatGptTest.kt | 3 +- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index bc31e8a..a7526cc 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -87,6 +87,7 @@ disabled-modules=mastodon # gcloud config set project PROJECT_ID # gcloud auth login LOGIN # gcloud auth application-default login +# #gemini-project-id= #gemini-location=us-west1 #gemini-max-tokens=1024 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index f75b334..f805722 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231217213124" + const val VERSION = "0.8.0-rc+20231218140603" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1702877484912L), ZoneId.systemDefault() + Instant.ofEpochMilli(1702937164085L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index bd92332..e2ad717 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -34,10 +34,8 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage -import org.apache.commons.text.WordUtils import org.json.JSONException import org.json.JSONObject -import org.json.JSONWriter import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -48,7 +46,7 @@ import java.net.http.HttpRequest import java.net.http.HttpResponse class ChatGpt : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) + val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) override val name = CHATGPT_NAME @@ -60,7 +58,7 @@ class ChatGpt : AbstractModule() { properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (answer.isNotBlank()) { - event.sendMessage(WordUtils.wrap(answer, 400)) + event.sendMessage(answer) } else { event.respond("$name is stumped.") } @@ -95,7 +93,7 @@ class ChatGpt : AbstractModule() { const val MAX_TOKENS_PROP = "chatgpt-max-tokens" // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/completions" + private const val API_URL = "https://api.openai.com/v1/chat/completions" // ChatGPT command private const val CHATGPT_CMD = "chatgpt" @@ -105,7 +103,7 @@ class ChatGpt : AbstractModule() { @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String { if (!apiKey.isNullOrEmpty()) { - val prompt = JSONWriter.valueToString("Q:$query\nA:") + val content = query.replace("\"", "\\\"") val request = HttpRequest.newBuilder() .uri(URI.create(API_URL)) .header("Content-Type", "application/json") @@ -114,14 +112,15 @@ class ChatGpt : AbstractModule() { .POST( HttpRequest.BodyPublishers.ofString( """{ - "model": "text-davinci-003", - "prompt": $prompt, - "temperature": 0, - "max_tokens": $maxTokens, - "top_p": 1, - "frequency_penalty": 0, - "presence_penalty": 0 - }""".trimIndent() + "model": "gpt-3.5-turbo-1106", + "max_tokens": $maxTokens, + "messages": [ + { + "role": "user", + "content": "$content" + } + ] + }""".trimIndent() ) ) .build() @@ -131,7 +130,7 @@ class ChatGpt : AbstractModule() { try { val jsonResponse = JSONObject(response.body()) val choices = jsonResponse.getJSONArray("choices") - return choices.getJSONObject(0).getString("text").trim() + return choices.getJSONObject(0).getJSONObject("message").getString("content").trim() } catch (e: JSONException) { throw ModuleException( "$CHATGPT_CMD($query): JSON", diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index 2f7c842..6f9b5ff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -1,16 +1,12 @@ package net.thauvin.erik.mobibot.modules -import com.google.auth.Credentials import com.google.cloud.vertexai.VertexAI -import com.google.cloud.vertexai.api.GenerateContentResponse import com.google.cloud.vertexai.api.GenerationConfig import com.google.cloud.vertexai.generativeai.preview.ChatSession import com.google.cloud.vertexai.generativeai.preview.GenerativeModel import com.google.cloud.vertexai.generativeai.preview.ResponseHandler import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage -import okio.IOException -import org.apache.commons.text.WordUtils import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -86,7 +82,7 @@ class Gemini : AbstractModule() { val session = ChatSession(model) val response = session.sendMessage(query) - return ResponseHandler.getText(response); + return ResponseHandler.getText(response) } } catch (e: Exception) { throw ModuleException( @@ -96,7 +92,7 @@ class Gemini : AbstractModule() { ) } } else { - throw ModuleException("${GEMINI_CMD}($query)", "No ${GEMINI_NAME} Project ID or Location specified.") + throw ModuleException("${GEMINI_CMD}($query)", "No $GEMINI_NAME Project ID or Location specified.") } } } @@ -105,10 +101,10 @@ class Gemini : AbstractModule() { commands.add(GEMINI_CMD) with(help) { add("To get answers from $name:") - add(Utils.helpFormat("%c ${GEMINI_CMD} ")) + add(Utils.helpFormat("%c $GEMINI_CMD ")) add("For example:") - add(Utils.helpFormat("%c ${GEMINI_CMD} explain quantum computing in simple terms")) - add(Utils.helpFormat("%c ${GEMINI_CMD} how do I make an HTTP request in Javascript?")) + add(Utils.helpFormat("%c $GEMINI_CMD explain quantum computing in simple terms")) + add(Utils.helpFormat("%c $GEMINI_CMD how do I make an HTTP request in Javascript?")) } initProperties(PROJECT_ID_PROP, LOCATION_PROPR, MAX_TOKENS_PROP) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 603c353..7180c13 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -54,11 +54,12 @@ class ChatGptTest : LocalProperties() { assertThat( ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") + assertThat( ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") - assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } + assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, -1) } .isInstanceOf(ModuleException::class.java) } } From c684332b9d804fb3b46de120a9f4b9678098f351 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 24 Dec 2023 12:11:11 -0800 Subject: [PATCH 748/842] Bumped to Kotlin 1.9.22 --- release_info.txt | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 6 +++--- src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/release_info.txt b/release_info.txt index b00e5ba..d2c33ac 100644 --- a/release_info.txt +++ b/release_info.txt @@ -22,7 +22,7 @@ object {{v className/}} { Instant.ofEpochMilli({{v epoch/}}L), ZoneId.systemDefault() ) - const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val WEBSITE = "https://mobitopia.org/mobibot/" const val AUTHOR = "Erik C. Thauvin" const val AUTHOR_URL = "https://erik.thauvin.net/" } diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index d6a294f..ba67d1d 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -71,7 +71,7 @@ public class MobibotBuild extends Project { ); var log4j = version(2, 22, 0); - var kotlin = version(1, 9, 21); + var kotlin = version(1, 9, 22); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) @@ -82,7 +82,7 @@ public class MobibotBuild extends Project { .include(dependency("commons-net", "commons-net", "3.10.0")) // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) - .include(dependency("com.google.guava", "guava", "32.1.3-jre")) + .include(dependency("com.google.guava", "guava", "33.0.0-jre")) .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 1, 0))) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) @@ -109,7 +109,7 @@ public class MobibotBuild extends Project { .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 0))) - .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 21))) + .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index f805722..f499559 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,15 +14,15 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231218140603" + const val VERSION = "0.8.0-rc+20231224114859" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1702937164085L), ZoneId.systemDefault() + Instant.ofEpochMilli(1703447340121L), ZoneId.systemDefault() ) - const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val WEBSITE = "https://mobitopia.org/mobibot/" const val AUTHOR = "Erik C. Thauvin" const val AUTHOR_URL = "https://erik.thauvin.net/" } From 3c4e76e5b916bb9bcc727776c90737f0722d4706 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 16 Jan 2024 10:21:27 -0800 Subject: [PATCH 749/842] Updated copyright --- .idea/intellij-javadocs-4.0.1.xml | 204 ++++++++++++++++++ .../java/net/thauvin/erik/MobibotBuild.java | 10 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 2 +- .../erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../thauvin/erik/mobibot/modules/Gemini.kt | 33 ++- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 2 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../erik/mobibot/DisableOnCiCondition.kt | 2 +- .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 2 +- .../erik/mobibot/commands/RecapTest.kt | 2 +- .../commands/links/LinksManagerTest.kt | 2 +- .../erik/mobibot/commands/links/ViewTest.kt | 2 +- .../erik/mobibot/commands/seen/SeenTest.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 2 +- .../commands/tell/TellMessagesMgrTest.kt | 2 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 2 +- .../erik/mobibot/entries/EntryLinkTest.kt | 2 +- .../erik/mobibot/entries/FeedMgrTest.kt | 2 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../erik/mobibot/modules/ChatGptTest.kt | 2 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- .../mobibot/modules/CurrencyConverterTest.kt | 2 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../erik/mobibot/modules/GeminiTest.kt | 4 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 6 +- .../erik/mobibot/modules/MastodonTest.kt | 2 +- .../mobibot/modules/ModuleExceptionTest.kt | 2 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/Weather2Test.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- .../erik/mobibot/modules/WordTimeTest.kt | 2 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 2 +- 102 files changed, 344 insertions(+), 109 deletions(-) create mode 100644 .idea/intellij-javadocs-4.0.1.xml diff --git a/.idea/intellij-javadocs-4.0.1.xml b/.idea/intellij-javadocs-4.0.1.xml new file mode 100644 index 0000000..dd24abe --- /dev/null +++ b/.idea/intellij-javadocs-4.0.1.xml @@ -0,0 +1,204 @@ + + + + + UPDATE + false + true + + FIELD + METHOD + TYPE + + + PROTECTED + DEFAULT + PUBLIC + + + + + + ^.*(public|protected|private)*.+interface\s+\w+.* + /**\n + * The interface ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + ^.*(public|protected|private)*.+enum\s+\w+.* + /**\n + * The enum ${name}.\n + */ + + + ^.*(public|protected|private)*.+class\s+\w+.* + /**\n + * The type ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + .+ + /**\n + * The type ${name}.\n + */ + + + + + .+ + /**\n + * Instantiates a new ${name}.\n +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*\s*.*(\w(\s*<.+>)*)+\s+get\w+\s*\(.*\).+ + /**\n + * Gets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*(public|protected|private)*\s*.*(void|\w(\s*<.+>)*)+\s+set\w+\s*\(.*\).+ + /**\n + * Sets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+ + /**\n + * The entry point of application.\n + + <#if element.parameterList.parameters?has_content> + *\n +</#if> + * @param ${element.parameterList.parameters[0].name} the input arguments\n +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + .+ + /**\n + * ${name}<#if isNotVoid> ${return}</#if>.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${return}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*.+static.*(\w\s\w)+.+ + /**\n + * The constant ${element.getName()}.\n + */ + + + ^.*(public|protected|private)*.*(\w\s\w)+.+ + /**\n + <#if element.parent.isInterface()> + * The constant ${element.getName()}.\n +<#else> + * The ${name}.\n +</#if> */ + + + .+ + /**\n + <#if element.parent.isEnum()> + *${name} ${typeName}.\n +<#else> + * The ${name}.\n +</#if>*/ + + + + + \ No newline at end of file diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index ba67d1d..1eec4e8 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -1,7 +1,7 @@ /* * MobibotBuild.java * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -70,7 +70,7 @@ public class MobibotBuild extends Project { repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io") ); - var log4j = version(2, 22, 0); + var log4j = version(2, 22, 1); var kotlin = version(1, 9, 22); scope(compile) // PircBotX @@ -83,7 +83,7 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 1, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 2, 0))) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) @@ -91,7 +91,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.9")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.11")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) @@ -101,7 +101,7 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20231013")) - .include(dependency("org.jsoup", "jsoup", "1.17.1")) + .include(dependency("org.jsoup", "jsoup", "1.17.2")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.2")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.1")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 2c5f05d..f3ae0a3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index d037e44..8716e14 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index d82f011..a91c6dc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 6b9c21a..88bed54 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 7cb5aed..88e475e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index f499559..13d9f52 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231224114859" + const val VERSION = "0.8.0-rc+20240116102134" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1703447340121L), ZoneId.systemDefault() + Instant.ofEpochMilli(1705429295075L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index e4760d2..6a8e4ff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 5f79472..5f79fab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 038e378..873d31f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 9608ca8..88b6c6d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index f271bfa..e4e111d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 2109693..6dfff07 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index ed0b6ef..4fae22f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index ec7823b..3495b28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index b2293b0..314ce99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 20a6635..bddd56c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 85a03ab..59b474a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 77154c7..886b26c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 7f76d35..90f3c04 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 33d6fef..54a85b2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 4594da3..f422566 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 1443d44..2fade4e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fba6b99..ea18613 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index ff4278d..2d24ba7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1662857..1a582a9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 825e374..e6971ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index cfd2c27..e51a6bf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index c9ee0f3..1ad12f6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 7924977..69923db 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 061ca6a..790ce0e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index b65a4da..f34af98 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index d17fbb5..ac15595 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index e8676ec..676fbee 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 9c09626..ea536bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index e18d692..d9d5587 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 4a69446..c88f8a4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index f786cb2..4dc8e9e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 8c8e736..17dbde2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index b7aae28..b5fe5b1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index e2ad717..b94b5d7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,7 +1,7 @@ /* * ChatGpt.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 4614a4c..fe95fe0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index da0efd8..4fb7555 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 8420fb1..b6d69ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index 6f9b5ff..18db66d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -1,3 +1,34 @@ +/* + * Gemini.kt + * + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package net.thauvin.erik.mobibot.modules import com.google.cloud.vertexai.VertexAI @@ -63,7 +94,7 @@ class Gemini : AbstractModule() { */ const val MAX_TOKENS_PROP = "gemini-max-tokens" - // ChatGPT command + // Gemini command private const val GEMINI_CMD = "gemini" @JvmStatic diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index f426d1e..1611130 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 2760fa7..1c6a5eb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 9ab2ead..baeebba 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 3be3a5f..166e39f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index a7416c2..32240ca 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 944dbc1..4b56df5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index a299d8d..13afbb7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index dcae5e7..13b9329 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt index 8799279..e0fd947 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -1,7 +1,7 @@ /* * War.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 80a06fa..21bf0ae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index a72efab..ba88711 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 18072bc..8978275 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 0607936..abcd043 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index d41ec8c..4b788ba 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 037d504..ea11c9c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index b424fdf..27b1cbd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 9c5e088..ae909f6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index 91f2dd9..c785106 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index b594670..a13b429 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 3fd315e..e722ed8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 9dd6034..4555884 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 22b0146..8d73a5e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -1,7 +1,7 @@ /* * DisableOnCi.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt index 1d9baf4..b0f4771 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -1,7 +1,7 @@ /* * DisableOnCiCondition.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index a3994ec..d1ca71d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index bf4408f..1cb2645 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index c039d74..c290262 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 3b81950..7e56912 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index a65b3a5..49ee7fa 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 99ba951..add701e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index b6fbbff..875342b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index fa2bb70..59338f4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index e315891..9eb433a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 62a1db7..6d5ba78 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 69bc2e9..33b3598 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index e3c9c1f..d7bde4c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 0ad26b5..983e225 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 3a3bdda..577f425 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index b56e6ed..d2b959e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 04afe8c..e7ef601 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 7180c13..e38e81b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,7 +1,7 @@ /* * ChatGptTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 10b3a43..0e2ef84 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 8ab64c0..5ebb7dc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 8bafede..007f22f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt index 28845a9..db69fe7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt @@ -1,7 +1,7 @@ /* - * ChatGptTest.kt + * GeminiTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 4942266..752b49e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 441d435..118d736 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 8699c0f..048d91a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -44,8 +44,8 @@ class LookupTest { var result = nslookup("apple.com") assertThat(result, "lookup(apple.com)").contains("17.253.144.10") - result = nslookup("204.122.16.136") - assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") + result = nslookup("37.27.52.13") + assertThat(result, "lookup(37.27.52.13)").contains("nix4.thauvin.us") } @Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index f4b5e99..11d4a51 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 416b3ab..ff706fd 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index bbf9eb1..5532e30 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index f836b0e..0e4b4ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 0816b17..3fe9189 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 7ae2b83..f4f85f8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 3bfe0d1..4f827bb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index ae40e3a..1211ea4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 32a0495..44054f1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: From d371bf5a01eea11a733788e9e0828dd614cd771a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 31 Jan 2024 15:55:32 -0800 Subject: [PATCH 750/842] Bumped bld to 1.8.0 --- .idea/libraries/bld.xml | 4 ++-- .vscode/settings.json | 2 +- README.md | 1 + lib/bld/bld-wrapper.jar | Bin 27321 -> 27293 bytes lib/bld/bld-wrapper.properties | 10 +++++----- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index ca84ff0..bff4f62 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.vscode/settings.json b/.vscode/settings.json index 133aa45..5633e79 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.7.5.jar", + "${HOME}/.bld/dist/bld-1.8.0.jar", "lib/compile/*.jar", "lib/runtime/*.jar", "lib/test/*.jar" diff --git a/README.md b/README.md index b360d96..c9191ce 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-7f52ff.svg)](https://kotlinlang.org) +[![bld](https://img.shields.io/badge/1.8.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index d17ad8982355ae5516129c6b07841609641eb492..1ae4f55822672a66d89f6c692ffc7f10b2d438c9 100644 GIT binary patch delta 25301 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 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index e219f09..90471f8 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3 -bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0-SNAPSHOT -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0-SNAPSHOT +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2 +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.4 +bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= -bld.version=1.7.5 +bld.version=1.8.0 From 6c2338b8b6f1852e27bac9c1753197591eff2baf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 5 Feb 2024 17:42:48 -0800 Subject: [PATCH 751/842] Bumped depedencies --- lib/bld/bld-wrapper.jar | Bin 27293 -> 27293 bytes lib/bld/bld-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 1ae4f55822672a66d89f6c692ffc7f10b2d438c9..5e76a01d101edb93ddf297aa6a5ff753385cc8f4 100644 GIT binary patch delta 131 zcmbPxm2vJ>M&1B#W)=|!4h{|mg?^Wbymib#YGa?MF^Jy0%($Bg%rG(gr3+>lrkrO4 wGnms1!BUgWGweYElPfcv!SwzNPcY4$83Lx0GF`#+s?0cuc$Pnyj?Hoa047K%xBvhE delta 131 zcmbPxm2vJ>M&1B#W)=|!4h{|m!HsqkdFz;g)W$whV-UT0nQ=E0m|C<08is7ZU6uP diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 90471f8..77cb51b 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -3,7 +3,7 @@ bld.downloadExtensionSources=true bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2 bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.4 bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0 -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.1 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= From 6da6783395e977b43c7f5572c3f53cbbae4512aa Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 5 Feb 2024 17:43:06 -0800 Subject: [PATCH 752/842] Minor cleanups --- .circleci/config.yml | 2 +- .gitlab-ci.yml | 3 +++ .../java/net/thauvin/erik/MobibotBuild.java | 24 +++++++++---------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 77889be..09d8896 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ defaults: &defaults TERM: dumb CI_NAME: "CircleCI" -defaults_gradle: &defaults_bld +defaults_bld: &defaults_bld steps: - checkout - run: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 052df48..2398bba 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,8 @@ image: openjdk:17 +variables: + CI_NAME: "GitLab CI" + stages: - test diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1eec4e8..024b908 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -50,8 +50,7 @@ import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; -import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; -import static rife.bld.dependencies.Repository.MAVEN_LOCAL; +import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; import static rife.bld.dependencies.Scope.test; @@ -67,8 +66,11 @@ public class MobibotBuild extends Project { javaRelease = 17; downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io") - ); + repositories = List.of( + MAVEN_LOCAL, + MAVEN_CENTRAL, + new Repository("https://jitpack.io"), + SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 22, 1); var kotlin = version(1, 9, 22); @@ -83,15 +85,13 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 2, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 3, 0))) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.11")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.12")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) @@ -103,15 +103,15 @@ public class MobibotBuild extends Project { .include(dependency("org.json", "json", "20231013")) .include(dependency("org.jsoup", "jsoup", "1.17.2")) // Thauvin - .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.2")) + .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.1")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.1")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 0))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From 69c18841bc5dccd682f4a7240cac1cc7b79d59ad Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Feb 2024 01:14:52 -0800 Subject: [PATCH 753/842] Bumped depencendies (common-codec, org.json) --- .vscode/settings.json | 2 ++ src/bld/java/net/thauvin/erik/MobibotBuild.java | 5 ++--- src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5633e79..1b057c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,7 +8,9 @@ "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ "${HOME}/.bld/dist/bld-1.8.0.jar", + "lib/bld/*.jar", "lib/compile/*.jar", + "lib/provided/*.jar", "lib/runtime/*.jar", "lib/test/*.jar" ] diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 024b908..3977d27 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -80,14 +80,13 @@ public class MobibotBuild extends Project { // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) .include(dependency("org.apache.commons", "commons-text", "1.11.0")) - .include(dependency("commons-codec", "commons-codec", "1.16.0")) + .include(dependency("commons-codec", "commons-codec", "1.16.1")) .include(dependency("commons-net", "commons-net", "3.10.0")) // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 3, 0))) // Kotlin - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging @@ -100,7 +99,7 @@ public class MobibotBuild extends Project { .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) - .include(dependency("org.json", "json", "20231013")) + .include(dependency("org.json", "json", "20240205")) .include(dependency("org.jsoup", "jsoup", "1.17.2")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 13d9f52..72ba96c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240116102134" + const val VERSION = "0.8.0-rc+20240209010856" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1705429295075L), ZoneId.systemDefault() + Instant.ofEpochMilli(1707469736725L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" From 26f1a2da1dd5049c5748066ec807067beb64c51a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Feb 2024 01:19:10 -0800 Subject: [PATCH 754/842] Fixed Kotlin badge version number --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9191ce..f051559 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.22-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/1.8.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) From 8aa4dcbcce42fb44196994620cfafa6ae3f96cf7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Feb 2024 16:48:31 -0800 Subject: [PATCH 755/842] Bumped Google Cloud VertexAI dependency to 0.4.0 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 3977d27..c1d5baa 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -85,7 +85,7 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 3, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 4, 0))) // Kotlin .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 72ba96c..a4d58f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240209010856" + const val VERSION = "0.8.0-rc+20240209164526" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1707469736725L), ZoneId.systemDefault() + Instant.ofEpochMilli(1707525927076L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index 18db66d..b1a2c83 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -33,9 +33,9 @@ package net.thauvin.erik.mobibot.modules import com.google.cloud.vertexai.VertexAI import com.google.cloud.vertexai.api.GenerationConfig -import com.google.cloud.vertexai.generativeai.preview.ChatSession -import com.google.cloud.vertexai.generativeai.preview.GenerativeModel -import com.google.cloud.vertexai.generativeai.preview.ResponseHandler +import com.google.cloud.vertexai.generativeai.ChatSession +import com.google.cloud.vertexai.generativeai.GenerativeModel +import com.google.cloud.vertexai.generativeai.ResponseHandler import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage import org.pircbotx.hooks.types.GenericMessageEvent From 0d37c7b15250c5da643a5dc4fcb6462abcaaa12f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 25 Feb 2024 21:24:16 -0800 Subject: [PATCH 756/842] Added the Snyk custom POM --- snyx.xml | 11 +++++++++++ src/bld/java/net/thauvin/erik/MobibotBuild.java | 14 +++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 snyx.xml diff --git a/snyx.xml b/snyx.xml new file mode 100644 index 0000000..8a129a6 --- /dev/null +++ b/snyx.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + + + + + + + diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index c1d5baa..1f72ddf 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -39,11 +39,13 @@ import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; import rife.bld.operations.exceptions.ExitStatusException; +import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; import rife.tools.exceptions.FileUtilsErrorException; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -72,7 +74,7 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 22, 1); + var log4j = version(2, 23, 0); var kotlin = version(1, 9, 22); scope(compile) // PircBotX @@ -85,9 +87,9 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 4, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 5, 0))) // Kotlin - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.0")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.12")) @@ -191,4 +193,10 @@ public class MobibotBuild extends Project { .extension(".kt") .execute(); } + + @BuildCommand(value = "snyk-pom", summary = "Generates the Snyk POM") + public void snykPom() throws FileUtilsErrorException { + PomBuilder.generateInto(publishOperation().info(), publishOperation().dependencies(), + Path.of(workDirectory.getPath(), "snyx.xml").toFile()); + } } From 2a9bb44fecff85e4ef2fd82313b68b6a7620ca43 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 25 Feb 2024 21:32:58 -0800 Subject: [PATCH 757/842] Added Root POM for Snyk --- snyx.xml => pom.xml | 0 src/bld/java/net/thauvin/erik/MobibotBuild.java | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename snyx.xml => pom.xml (100%) diff --git a/snyx.xml b/pom.xml similarity index 100% rename from snyx.xml rename to pom.xml diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1f72ddf..6f4e7cc 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -194,9 +194,9 @@ public class MobibotBuild extends Project { .execute(); } - @BuildCommand(value = "snyk-pom", summary = "Generates the Snyk POM") - public void snykPom() throws FileUtilsErrorException { + @BuildCommand(value = "root-pom", summary = "Generates the POM file in the root directory") + public void rootPom() throws FileUtilsErrorException { PomBuilder.generateInto(publishOperation().info(), publishOperation().dependencies(), - Path.of(workDirectory.getPath(), "snyx.xml").toFile()); + Path.of(workDirectory.getPath(), "pom.xml").toFile()); } } From 7931e4adbe1d96afe2af97ceb2fae7364780524a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 26 Feb 2024 17:01:30 -0800 Subject: [PATCH 758/842] Bumped to bld 1.9.0 --- .github/workflows/bld.yml | 4 ++++ .idea/libraries/bld.xml | 4 ++-- .vscode/launch.json | 11 ----------- .vscode/settings.json | 17 ----------------- README.md | 2 +- lib/bld/bld-wrapper.jar | Bin 27293 -> 27319 bytes lib/bld/bld-wrapper.properties | 10 +++++----- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 8 files changed, 14 insertions(+), 38 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/settings.json diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 19081a4..9bd9d9d 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -47,6 +47,10 @@ jobs: EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} run: ./bld jacoco + - name: Remove pom.xml + if: success() && matrix.java-version == env.COVERAGE_SDK + run: rm -rf pom.xml + - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master if: success() && matrix.java-version == env.COVERAGE_SDK diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index bff4f62..0b615c1 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index c6500f2..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Run Tests", - "request": "launch", - "mainClass": "net.thauvin.erik.MobibotTest" - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 1b057c3..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "java.project.sourcePaths": [ - "src/main/java", - "src/main/resources", - "src/test/java", - "src/bld/java" - ], - "java.configuration.updateBuildConfiguration": "automatic", - "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.8.0.jar", - "lib/bld/*.jar", - "lib/compile/*.jar", - "lib/provided/*.jar", - "lib/runtime/*.jar", - "lib/test/*.jar" - ] -} diff --git a/README.md b/README.md index f051559..0ce0853 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.22-7f52ff.svg)](https://kotlinlang.org) -[![bld](https://img.shields.io/badge/1.8.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![bld](https://img.shields.io/badge/1.9.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 5e76a01d101edb93ddf297aa6a5ff753385cc8f4..c2adfe8730e64fdc0c51dc7994d0d143787eb1e2 100644 GIT binary patch delta 25244 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

    ^*a+>tbBpHE+pL~3DbtKbopH&MqggYR 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 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 77cb51b..95b4a2e 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.4 -bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0 -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.1 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.3 +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.5 +bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.1 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.2 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= -bld.version=1.8.0 +bld.version=1.9.0 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index a4d58f3..e44a35b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240209164526" + const val VERSION = "0.8.0-rc+20240225204340" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1707525927076L), ZoneId.systemDefault() + Instant.ofEpochMilli(1708922620439L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" From cf0bafff940697dfbcb7755df95bae6ae8a10ecc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 3 Mar 2024 22:10:00 -0800 Subject: [PATCH 759/842] Reworked the ChatGPT JSON request programmatically --- .idea/misc.xml | 1 + .../java/net/thauvin/erik/MobibotBuild.java | 4 +-- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 28 ++++++++----------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 26293d3..f63c128 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,6 +6,7 @@ + - - - - \ No newline at end of file + diff --git a/README.md b/README.md index 4ac4370..eb38cfb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.24-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-2.0.0-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/1.9.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/pom.xml b/pom.xml index df4bbe8..3ef1964 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ com.google.code.gson gson - 2.10.1 + 2.11.0 compile @@ -54,31 +54,31 @@ com.google.cloud google-cloud-vertexai - 1.3.0 + 1.4.0 compile org.jetbrains.kotlin kotlin-stdlib - 1.9.24 + 2.0.0 compile org.jetbrains.kotlin kotlin-stdlib-common - 1.9.24 + 2.0.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 1.9.24 + 2.0.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 1.9.24 + 2.0.0 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index d2e4cb1..8a7265e 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -78,7 +78,7 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 23, 1); - var kotlin = version(1, 9, 24); + var kotlin = version(2, 0, 0); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) @@ -88,9 +88,9 @@ public class MobibotBuild extends Project { .include(dependency("commons-codec", "commons-codec", "1.17.0")) .include(dependency("commons-net", "commons-net", "3.10.0")) // Google - .include(dependency("com.google.code.gson", "gson", "2.10.1")) + .include(dependency("com.google.code.gson", "gson", "2.11.0")) .include(dependency("com.google.guava", "guava", "33.2.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.3.0")) + .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.4.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -153,7 +153,7 @@ public class MobibotBuild extends Project { @Override public void updates() throws Exception { super.updates(); - rootPom(); + pomRoot(); } @BuildCommand(summary = "Copies all needed files to the deploy directory") @@ -203,13 +203,13 @@ public class MobibotBuild extends Project { .classTemplate(new File(workDirectory(), "release-info.txt")) .className("ReleaseInfo") .packageName(pkg) - .directory(new File(srcMainDirectory(), "kotlin")) + .directory(srcMainKotlin) .extension(".kt") .execute(); } - @BuildCommand(value = "root-pom", summary = "Generates the POM file in the root directory") - public void rootPom() throws FileUtilsErrorException { + @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") + public void pomRoot() throws FileUtilsErrorException { PomBuilder.generateInto(publishOperation().info(), dependencies(), Path.of(workDirectory.getPath(), "pom.xml").toFile()); } From db0e4d30f1f3b4f32cad66163b121546249351d6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 12 Jul 2024 17:42:39 -0700 Subject: [PATCH 788/842] Updated various dependencies Cleaned up CI workflows --- .circleci/config.yml | 8 ++++++++ .github/workflows/bld.yml | 5 +++-- .gitlab-ci.yml | 18 ++++++++++++++---- bitbucket-pipelines.yml | 12 +++++++++++- lib/bld/bld-wrapper.properties | 10 +++++----- pom.xml | 8 ++++---- .../java/net/thauvin/erik/MobibotBuild.java | 16 ++++++++-------- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 8 files changed, 55 insertions(+), 26 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 09d8896..c5e20a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,6 +9,14 @@ defaults: &defaults defaults_bld: &defaults_bld steps: - checkout + - run: + name: Install Kotlin via SDKMAN! + command: | + curl -s "https://get.sdkman.io" | bash + echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config + echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config + source "$HOME/.sdkman/bin/sdkman-init.sh" + sdk install kotlin 2.0.0 - run: name: Download the bld dependencies command: ./bld download diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 28175b0..21c09a8 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -8,10 +8,12 @@ jobs: env: COVERAGE_SDK: "17" + COVERAGE_KOTLIN: "2.0.0" strategy: matrix: java-version: [17, 21, 22] + kotlin-version: [1.9.24, 2.0.0] steps: - name: Checkout source repository @@ -48,12 +50,11 @@ jobs: run: ./bld jacoco - name: Remove pom.xml - if: success() && matrix.java-version == env.COVERAGE_SDK run: rm -rf pom.xml - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master - if: success() && matrix.java-version == env.COVERAGE_SDK + if: success() && matrix.java-version == env.COVERAGE_SDK && matrix.kotlin-version == env.COVERAGE_KOTLIN env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2398bba..14646f3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: openjdk:17 +image: ubuntu:latest variables: CI_NAME: "GitLab CI" @@ -6,9 +6,19 @@ variables: stages: - test +before_script: + - apt-get update -qq && apt-get install -y curl zip + - curl -s "https://get.sdkman.io" | bash + - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config + - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config + - source "$HOME/.sdkman/bin/sdkman-init.sh" + - sdk install java 17.0.11-tem + - sdk install kotlin 2.0.0 + - source "$HOME/.sdkman/bin/sdkman-init.sh" + test: stage: test script: - - ./bld download - - ./bld compile - - ./bld test + - ./bld download + - ./bld compile + - ./bld test diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 7c85194..5951f4b 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,10 +1,20 @@ -image: openjdk:17 +image: ubuntu:latest pipelines: default: - step: name: Test with bld script: + # Install Java & Kotlin via SDKMAN! + - apt-get update -qq && apt-get install -y curl zip + - curl -s "https://get.sdkman.io" | bash + - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config + - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config + - source "$HOME/.sdkman/bin/sdkman-init.sh" + - sdk install java 17.0.11-tem + - sdk install kotlin 2.0.0 + - source "$HOME/.sdkman/bin/sdkman-init.sh" + # Download, compile and test with bld - ./bld download - ./bld compile - ./bld test diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 1695c0b..0aa69b0 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.5 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.6 -bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.8 -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.4 -bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.4 +bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.8-SNAPSHOT +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.6 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.0-SNAPSHOT +bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=1.9.1 diff --git a/pom.xml b/pom.xml index 3ef1964..8fa40d6 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ commons-net commons-net - 3.10.0 + 3.11.1 compile @@ -48,13 +48,13 @@ com.google.guava guava - 33.2.0-jre + 33.2.1-jre compile com.google.cloud google-cloud-vertexai - 1.4.0 + 1.6.0 compile @@ -150,7 +150,7 @@ org.jsoup jsoup - 1.17.2 + 1.18.1 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 8a7265e..e9343c5 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -86,11 +86,11 @@ public class MobibotBuild extends Project { .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) .include(dependency("org.apache.commons", "commons-text", "1.12.0")) .include(dependency("commons-codec", "commons-codec", "1.17.0")) - .include(dependency("commons-net", "commons-net", "3.10.0")) + .include(dependency("commons-net", "commons-net", "3.11.1")) // Google .include(dependency("com.google.code.gson", "gson", "2.11.0")) - .include(dependency("com.google.guava", "guava", "33.2.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.4.0")) + .include(dependency("com.google.guava", "guava", "33.2.1-jre")) + .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.6.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -109,7 +109,7 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20240303")) - .include(dependency("org.jsoup", "jsoup", "1.17.2")) + .include(dependency("org.jsoup", "jsoup", "1.18.1")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) @@ -118,8 +118,8 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 3))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 3))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); @@ -189,7 +189,7 @@ public class MobibotBuild extends Project { } @BuildCommand(summary = "Generates JaCoCo Reports") - public void jacoco() throws IOException { + public void jacoco() throws Exception { new JacocoReportOperation() .fromProject(this) .sourceFiles(srcMainKotlin) @@ -197,7 +197,7 @@ public class MobibotBuild extends Project { } @BuildCommand(value = "release-info", summary = "Generates the ReleaseInfo class") - public void releaseInfo() { + public void releaseInfo() throws Exception { new GeneratedVersionOperation() .fromProject(this) .classTemplate(new File(workDirectory(), "release-info.txt")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index c892cf0..3abf162 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240509074831" + const val VERSION = "0.8.0-rc+20240712110931" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1715266111851L), ZoneId.systemDefault() + Instant.ofEpochMilli(1720807771484L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" From c3a8018cc39881f7f5d508583c41712575207550 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 12 Jul 2024 21:58:34 -0700 Subject: [PATCH 789/842] Cleaned up CI workflows --- .circleci/config.yml | 58 ++++++++++-------- .github/workflows/bld.yml | 48 +++++++-------- .gitlab-ci.yml | 8 +-- .idea/libraries/bld.xml | 4 +- bitbucket-pipelines.yml | 6 +- lib/bld/bld-wrapper.jar | Bin 27319 -> 29578 bytes lib/bld/bld-wrapper.properties | 2 +- .../erik/mobibot/modules/ChatGptTest.kt | 2 +- 8 files changed, 68 insertions(+), 60 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c5e20a6..17b50f1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,8 @@ -version: 2 +version: 2.1 + +orbs: + sdkman: joshdholtz/sdkman@0.2.0 + defaults: &defaults working_directory: ~/repo environment: @@ -6,26 +10,31 @@ defaults: &defaults TERM: dumb CI_NAME: "CircleCI" -defaults_bld: &defaults_bld - steps: - - checkout - - run: - name: Install Kotlin via SDKMAN! - command: | - curl -s "https://get.sdkman.io" | bash - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config - source "$HOME/.sdkman/bin/sdkman-init.sh" - sdk install kotlin 2.0.0 - - run: - name: Download the bld dependencies - command: ./bld download - - run: - name: Compile source with bld - command: ./bld compile - - run: - name: Run tests with bld - command: ./bld test +commands: + build_and_test: + parameters: + reports-dir: + type: string + default: "build/reports/test_results" + steps: + - checkout + - sdkman/setup-sdkman + - sdkman/sdkman-install: + candidate: kotlin + version: 2.0.0 + - run: + name: Download dependencies + command: ./bld download + - run: + name: Compile source + command: ./bld compile + - run: + name: Run tests + command: ./bld jacoco -reports-dir=<< parameters.reports-dir >> + - store_test_results: + path: << parameters.reports-dir >> + - store_artifacts: + path: build/reports/jacoco/test/html jobs: bld_jdk17: @@ -34,7 +43,8 @@ jobs: docker: - image: cimg/openjdk:17.0 - <<: *defaults_bld + steps: + - build_and_test bld_jdk20: <<: *defaults @@ -42,10 +52,10 @@ jobs: docker: - image: cimg/openjdk:20.0 - <<: *defaults_bld + steps: + - build_and_test workflows: - version: 2 bld: jobs: - bld_jdk17 diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 21c09a8..a3c4951 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,19 +1,29 @@ name: bld-ci -on: [push, pull_request, workflow_dispatch] +on: [ push, pull_request, workflow_dispatch ] + +env: + ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} + CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} + CI_NAME: "GitHub CI" + COVERAGE_JDK: "21" + COVERAGE_KOTLIN: "2.0.0" + EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} + KOTLIN_HOME: /usr/share/kotlinc + MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} + MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} + MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} + OWM_API_KEY: ${{ secrets.OWM_API_KEY }} + PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} jobs: build-bld-project: runs-on: ubuntu-latest - env: - COVERAGE_SDK: "17" - COVERAGE_KOTLIN: "2.0.0" - strategy: matrix: - java-version: [17, 21, 22] - kotlin-version: [1.9.24, 2.0.0] + java-version: [ 17, 21, 22 ] + kotlin-version: [ 1.9.24, 2.0.0 ] steps: - name: Checkout source repository @@ -21,40 +31,28 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK ${{ matrix.java-version }} + - name: Set up JDK ${{ matrix.java-version }} with Kotlin ${{ matrix.kotlin-version }} uses: actions/setup-java@v4 with: distribution: "zulu" java-version: ${{ matrix.java-version }} - - name: Grant bld execute permission - run: chmod +x bld - - - name: Download the bld dependencies + - name: Download dependencies run: ./bld download - - name: Compile source with bld + - name: Compile source run: ./bld compile - - name: Run tests with bld - env: - CI_NAME: "GitHub CI" - ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} - CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} - OWM_API_KEY: ${{ secrets.OWM_API_KEY }} - PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} - MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} - MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} - MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} - EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} + - name: Run tests run: ./bld jacoco - name: Remove pom.xml + if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN run: rm -rf pom.xml - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master - if: success() && matrix.java-version == env.COVERAGE_SDK && matrix.kotlin-version == env.COVERAGE_KOTLIN + if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 14646f3..10b9b0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: ubuntu:latest +image: fedora:latest variables: CI_NAME: "GitLab CI" @@ -7,13 +7,13 @@ stages: - test before_script: - - apt-get update -qq && apt-get install -y curl zip + - dnf -qy update && dnf -y install zip - curl -s "https://get.sdkman.io" | bash - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config - source "$HOME/.sdkman/bin/sdkman-init.sh" - - sdk install java 17.0.11-tem - - sdk install kotlin 2.0.0 + - sdk install java + - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" test: diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index a2969be..2fb5ff0 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 5951f4b..ace99d2 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -5,14 +5,14 @@ pipelines: - step: name: Test with bld script: - # Install Java & Kotlin via SDKMAN! + # Install latest Java & Kotlin via SDKMAN! - apt-get update -qq && apt-get install -y curl zip - curl -s "https://get.sdkman.io" | bash - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config - source "$HOME/.sdkman/bin/sdkman-init.sh" - - sdk install java 17.0.11-tem - - sdk install kotlin 2.0.0 + - sdk install java + - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" # Download, compile and test with bld - ./bld download diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 8a6c6415420d24c2f97b665923c81552b4add950..f46bb77a290005df0476b73c34554b894620bf01 100644 GIT binary patch delta 27557 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 A#Q*>R delta 25368 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 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 0aa69b0..e9d6ec3 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -7,4 +7,4 @@ bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.6 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.9.1 +bld.version=2.0.0-SNAPSHOT diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index c031295..1809a9f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -49,7 +49,7 @@ class ChatGptTest : LocalProperties() { @Test fun testChatOnCoverage() { - if (System.getenv("CI") == null || System.getenv("COVERAGE_SDK") != null) { + if (System.getenv("CI") == null || System.getenv("COVERAGE_JDK") != null) { assertThat( ChatGpt.chat("how do I encode a URL in java?", getProperty(ChatGpt.API_KEY_PROP), 60) ).contains("URLEncoder") From 8d9acd8184f2c36abfde1ab37efb00c2b95f8636 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 15 Jul 2024 21:00:28 -0700 Subject: [PATCH 790/842] Moved to Gemini 1.5 Flash --- .../thauvin/erik/mobibot/modules/Gemini.kt | 28 +++++++++---------- .../erik/mobibot/modules/GeminiTest.kt | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index c0faefa..2e4ed91 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -35,7 +35,6 @@ import com.google.cloud.vertexai.VertexAI import com.google.cloud.vertexai.api.GenerationConfig import com.google.cloud.vertexai.api.HarmCategory import com.google.cloud.vertexai.api.SafetySetting -import com.google.cloud.vertexai.generativeai.ChatSession import com.google.cloud.vertexai.generativeai.GenerativeModel import com.google.cloud.vertexai.generativeai.ResponseHandler import net.thauvin.erik.mobibot.Utils @@ -57,7 +56,7 @@ class Gemini : AbstractModule() { val answer = chat( args.trim(), properties[PROJECT_ID_PROP], - properties[LOCATION_PROPR], + properties[LOCATION_PROP], properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (!answer.isNullOrEmpty()) { @@ -83,17 +82,17 @@ class Gemini : AbstractModule() { const val GEMINI_NAME = "Gemini" /** - * The Google cloud project ID. + * The Google cloud project ID property. */ const val PROJECT_ID_PROP = "gemini-project-id" /** - * The Vertex AI location. + * The Vertex AI location property. */ - const val LOCATION_PROPR = "gemini-location" + const val LOCATION_PROP = "gemini-location" /** - * The max tokens property. + * The max number of tokens property. */ const val MAX_TOKENS_PROP = "gemini-max-tokens" @@ -112,31 +111,30 @@ class Gemini : AbstractModule() { try { VertexAI(projectId, location).use { vertexAI -> val generationConfig = GenerationConfig.newBuilder().setMaxOutputTokens(maxToken).build() - val safetySettings = Arrays.asList( + val safetySettings = listOf( SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_HATE_SPEECH) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build(), SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build(), SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build(), SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_HARASSMENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build() ) - val model = GenerativeModel.Builder().setModelName("gemini-pro-vision") + val model = GenerativeModel.Builder().setModelName("gemini-1.5-flash-001") .setGenerationConfig(generationConfig) .setVertexAi(vertexAI).build() .withSafetySettings(safetySettings) - val session = ChatSession(model) - val response = session.sendMessage(query) + val response = model.generateContent(query) return ResponseHandler.getText(response) } } catch (e: Exception) { @@ -161,7 +159,7 @@ class Gemini : AbstractModule() { add(Utils.helpFormat("%c $GEMINI_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $GEMINI_CMD how do I make an HTTP request in Javascript?")) } - initProperties(PROJECT_ID_PROP, LOCATION_PROPR, MAX_TOKENS_PROP) + initProperties(PROJECT_ID_PROP, LOCATION_PROP, MAX_TOKENS_PROP) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt index db69fe7..1f0202f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt @@ -49,7 +49,7 @@ class GeminiTest : LocalProperties() { @DisableOnCi fun chatPrompt() { val projectId = getProperty(Gemini.PROJECT_ID_PROP) - val location = getProperty(Gemini.LOCATION_PROPR) + val location = getProperty(Gemini.LOCATION_PROP) val maxTokens = getProperty(Gemini.MAX_TOKENS_PROP).toInt() assertThat( From ebf05fa398a328d8fec8867172f5ef43e7f7d273 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 15 Jul 2024 21:01:00 -0700 Subject: [PATCH 791/842] Fixed root POM generation --- pom.xml | 8 ++++---- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 8fa40d6..71e21a8 100644 --- a/pom.xml +++ b/pom.xml @@ -2,10 +2,10 @@ 4.0.0 - - - - + net.thauvin.erik.mobibot + mobibot + 0.8.0-rc+20240715201342 + mobibot diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index e9343c5..5b15c86 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -210,7 +210,7 @@ public class MobibotBuild extends Project { @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") public void pomRoot() throws FileUtilsErrorException { - PomBuilder.generateInto(publishOperation().info(), dependencies(), - Path.of(workDirectory.getPath(), "pom.xml").toFile()); + PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(), + new File(workDirectory, "pom.xml")); } } From 2905dec49f58fd9916091ade8033093a09c203af Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Aug 2024 18:06:24 -0700 Subject: [PATCH 792/842] Bumped bld to version 2.0.1 --- .idea/bld.xml | 6 ++++++ .idea/libraries/bld.xml | 4 ++-- lib/bld/bld-wrapper.jar | Bin 29578 -> 29577 bytes lib/bld/bld-wrapper.properties | 10 +++++----- 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 .idea/bld.xml diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index 2fb5ff0..4dd96bf 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index f46bb77a290005df0476b73c34554b894620bf01..1d2b318f1cbb9e83f5b4e6d4d922146c95191b40 100644 GIT binary patch delta 27484 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 Date: Fri, 9 Aug 2024 18:10:59 -0700 Subject: [PATCH 793/842] Updated dependencies Bumped Kotlin to version 2.0.1 Bumped Commons Lang3 to version 3.16.0 Bumped slf4j-api to version 2.0.15 Bumped Commons Codec to version 1.17.1 Bumped Google Cloud VertexAI to version 1.7.0 --- README.md | 4 ++-- pom.xml | 18 +++++++++--------- .../java/net/thauvin/erik/MobibotBuild.java | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index eb38cfb..a4db76e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.0.0-7f52ff.svg)](https://kotlinlang.org) -[![bld](https://img.shields.io/badge/1.9.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Kotlin](https://img.shields.io/badge/kotlin-2.0.10-7f52ff.svg)](https://kotlinlang.org) +[![bld](https://img.shields.io/badge/2.0.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/pom.xml b/pom.xml index 71e21a8..7996b05 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240715201342 + 0.8.0-rc+20240809180718 mobibot @@ -18,7 +18,7 @@ org.apache.commons commons-lang3 - 3.14.0 + 3.16.0 compile @@ -30,7 +30,7 @@ commons-codec commons-codec - 1.17.0 + 1.17.1 compile @@ -54,31 +54,31 @@ com.google.cloud google-cloud-vertexai - 1.6.0 + 1.7.0 compile org.jetbrains.kotlin kotlin-stdlib - 2.0.0 + 2.0.10 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.0.0 + 2.0.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.0.0 + 2.0.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.0.0 + 2.0.10 compile @@ -96,7 +96,7 @@ org.slf4j slf4j-api - 2.0.13 + 2.0.15 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 5b15c86..1e6649b 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -78,19 +78,19 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 23, 1); - var kotlin = version(2, 0, 0); + var kotlin = version(2, 0, 10); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) - .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) + .include(dependency("org.apache.commons", "commons-lang3", "3.16.0")) .include(dependency("org.apache.commons", "commons-text", "1.12.0")) - .include(dependency("commons-codec", "commons-codec", "1.17.0")) + .include(dependency("commons-codec", "commons-codec", "1.17.1")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google .include(dependency("com.google.code.gson", "gson", "2.11.0")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.6.0")) + .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.7.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -99,7 +99,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.13")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.15")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) From ebc3da70e4b27d4b748446c02be1fbfa319da1ee Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 8 Sep 2024 17:39:18 -0700 Subject: [PATCH 794/842] Bumped bld to version 2.1.0 --- .idea/libraries/bld.xml | 4 ++-- .idea/libraries/compile.xml | 4 ++-- .idea/libraries/runtime.xml | 4 ++-- .idea/libraries/test.xml | 4 ++-- README.md | 4 ++-- lib/bld/bld-wrapper.jar | Bin 29577 -> 30440 bytes lib/bld/bld-wrapper.properties | 10 +++++----- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index 4dd96bf..5c4010c 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/.idea/libraries/compile.xml +++ b/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/.idea/libraries/runtime.xml +++ b/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/.idea/libraries/test.xml +++ b/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/README.md b/README.md index a4db76e..e438c94 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.0.10-7f52ff.svg)](https://kotlinlang.org) -[![bld](https://img.shields.io/badge/2.0.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Kotlin](https://img.shields.io/badge/kotlin-2.0.20-7f52ff.svg)](https://kotlinlang.org) +[![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 1d2b318f1cbb9e83f5b4e6d4d922146c95191b40..5425f1c05b18a00f2f1d69df4fc5885b310c372a 100644 GIT binary patch delta 15855 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 Ai~s-t delta 15009 zcmZ9z18`tLvj!S_V>=t$wl}tI+dLa=jE!v@8{4*>jcxnw|KC@2-+gmx>hw&1{mq%F zshU&W)5jN};YXl|iZb93=pZmKFd#qT-ZkS8{UHAPj%5ToU_DhAUj#q1T`u?K4}j!V6`AI$U)hHiF@gOLooduB1!A{!GdN%LAR@3(X`~p*s>S+7RUopnyY{~`$ZwLE2C@LzdNx970Fj4u1+kmq|5n#H5DQQ#ln zr_@}WQf&k1j}Q0H{Rgxc9+)`$%+&qg6FLH&r*7@;lRRDm&&KeUvkx3%;l@(&V4(8v zAj|r2Xl2JxO;6+l6kqa%QnqE1i5(mUh#1abKsFR{&6Q)s`>8o0b?=gYENs(L`D6*U zvElbE%+d$OFjTs@g@=Imx+SqfshcTLL>r<@!r=mG_9GFpsBCNb%!#qoYd-x(08<+i zvOOAG!&J1r8AWtKfh?}WEJQk$<(Oe`n)cDzx2BpJB)8|f`_t;5xmIqHGp+H=X{NKL zA=#Trr4DogmUL z?qTxN5|hAU&n;ivrBr0*cWpN%I;@ze5nh!ML&J>09_wHLKrO4aRuiC2 zuPt5yiSs!LYG{hSw3aL1e2mcNEZH3T(NzkVeepvUU=@!E^N<+-5Qtn-=KU=l&@h07 z$<{#>hC|x{u`_ZGSNZMD^GHrRh2{>oHU+GMRFA+cqrJV}3fwJi!A|M7h@t!@H%wU8 zPtC3ctifHgQWZrrTxJ9Q()u3(ilab6rkzr1t-S-AsmHG<48(kGZ?E>XXPX4^3|Mb! z@cU>nQtgwfvHU96YN;bFR{=c`CT(~z(Y)%g;(q6I6mRnYeS(`ZiYLm&p71n-ZlhW8 zd%2PEz_wp@GUe%O$?A;_3vBE^U3A}w9&7F#4wJ+`c8HO?f*IIPbTIug7-WFB*+hS0 zuV%)zuC3T;-X*#5(}ym{KVRqiK<#EuU+3esw~wM3L*?)Rre3eLuWPeIP&W^{?UT3$ zRQ}lwr2jXGyqxO%3c0qSd>glBVMD96d)Jq>4>di#j`K76wty7=^pwqwmX5Nv;?>S~ z-TT%|-Ywf%kJi2G;x%#hKO2Bvi(9*Rv!hMdW;zt|)rKABro4yHl`R}}{^v84oK(O5 zN9%fFJ48~O`+9fl=5{~W`Py^3BNuvgIA^M>hwdbZyq!rtNu>Ks=Y;uFFLwKpy%rIo z-EEcIz={9yT$ms(Bu`xZpYM6ODTKi>A=imK-ffkXBn$f9*mhCdmGwZ2b|t(~0vO+= zk|Pbg-d8f+Y2h%vcc6QKc%~mpgQFrQH^SMy#YqXBcRJ?*)z*5ET(pc$y0-SZ`oD!; z=HMXX)Lg^cu$U#hwWdH%E|3+*O)i>m_ECA6MxsTYUgq{)o0ttSR<8>_iE#? zN;zj?teM@qG!^%ejt17xCo5{SmF8#E_2y^hRMj+$8B5EGtBcE9i?yC&EcyN>^}wx& zx;{t%sTNtBZWVWK`pGHT0W&Q$x#^EO_xSusdZ5td)^=nIi+B&LSR#8v589b-t#3$l zuOuHZ>2$dDji;r3!fpqVn4JTDtgD=Nb!Vn?3Wit8?V>fo;D8NtMRp;L{#bs>2}Xi` zpZUawtc67a3g$2swQ87DAPiBEeqyNjhGN$gP4W zxz|W>pr~7T5`hDu)5m*r*ZyHH<>!@pE=Zhs&a6gd^!lG;G@dX<)MP>C+`!p0vmCKr3~LR_KoPbTWe%a7`_a`QP1rB>LQ-jG`m+acAm{zy9i1* zwZx#CNO|ixD(N7$%>Ex?(>eC1$NS)qxUlYi+96@Y*9wgqY$PR3qUc94nz8X#GjpwG zhXO%OOt>1_WrIIs+=kxUv~acdDdq=m3QYs(%m79vh=rT5hNLcvom88(RmWJ10Q=yq zlg471_3a&lX}Hxr#vwW@of28ZPV65wDetReTMt*wq6Jj5s9;DkRP5_#6FO#* z0YJHZb*e*n!pavrcpT($2Z@eqIAn2Y(}2j3!w+dqX#T)xlXonwUGzF^Wu$qOt^ACT z5-M>-Fq|b+nM|6~8I)?xmC%Fs^Y}~6vxNeWAWODEWf?k?U}tD|(Bw0}IH**+%XQmu z%sPA-*!=wNnk(dKbXt|b9&dx`1>7Mn9iS4qDp_+7geBKY$Y0&u!e{VA(>21SN7eez zKw2O4CNw7(esrw@-w%Cb3{m@LF=$cqtd_pz#j__k2rfy2F^!vu2RfCt5>UVCddrrT z83mIP&#F+F+%S6-Ht`Wu{;20`(@;o7B7dSj$ckaCnCGl~jbP~h8K;(1M5UIvd?2N{ zwvA`9u(j~`R(Iycxzwj_5cMdm${nn9)ZtAeG_fmJP`j(64AGDe!3a8f1Qja`Uhd|b zj4oLh&a^1^rs4Gt7Bor*#09a=%u1i?k5P`9JyfSM>MpZzhsg2WB|wN%7SzJmYrx<_ z6u;kDxdkphsxC!m7tV?NaKP9)2!LfDm6uTVXu<9A)(A*!5Wcd2nCU^a!NV`?|%)^HV(T=A*Nq*|ql3P0r zMlHg#3?}SStMJ){r>@XjD))8U7)(##LrT5`5_CCAHtG#=P2VPq+ffE`pF+91^H@!` zgi1AGVM0d|bR0!ttnT{k0yrvsHsK$Y#UaJ!b`9UFPAUhddMri_YqXa+CMq{_KA$xa8PT0dbXajH>i#ZF;=JFB)~U6O*5(W3w?0<6jn<@ z3B4e>5|gVhzPY+FAqxH(u!#!N9=7U`zk!$Om`djdHzH_|qliXx0JL{gGCS_+!jQbz zQM*yQp(aTKT;6#omCrD`#XA(WPsvzT+dj5Xufja?yn9=X418R}c{S2Fii)CliRf=A zn}oG02@;^*_+>h3<~uX}2!BuS5(13_+o=*>@~^fehSo^S0oO<}zQmtMKeNc_&S^Bs z>~JTt%vlG!mHCn(0IM`q8RUkDSo#|WL~ThrEu|a;Ei~%TiUENBCcG)%9V+CGY?oEW zm!S(pZTXjICqMY3Kw2~^G~G>-dxTy9VJMBh6LSB9ymwI#hY&xhcfwda)I4Zhw)}|9 z^KTkd`zL9b;$9U`a=zY)+5%Tn{g%qYzd`?+!dmKwN!%Sk+T=GinG7t>rk{+a)#P_8 z%02lWmf%N8D^XsdH{ENZn+Kg4O%UU5$|7&Iqr{k7X}=5Txg|2DgbKNj&%!H_#57H?fAp^N3(Bb8*jP)f$1hH@=Uxc zHl-g8*-9V)=&Udb8jg+LoG07-Y4GfUO-!&1){qYpRF84s-i-g{3{?^hO0&|tV}!eR zj8MIgGRT)Nrg(xv5Yi&iCmeTh9nIAy#gGb)Uy~V*QM8o8v+mGL1RvdIeu}FRk*7lF zFxzMhuBn@CIEwb-YWUo@c9>ke205;iTbQwqxx`@x%qeOajY@N4@$Dn^%VDSeE-mU| zv{!Ye23Oa~CR5-rOCrarr8_D*fuTOxQBK*4`juR1v_+J?fnZV5af>c29_jn{IwsJv zl=vK){T=TEQvSEyZr4CBS4q#r?7X{^e6 z6!3>!Cw01hk2EE;-Ee)S1HN@G|L(wQ)X*ju3(Lc6&JwCd7ZN<1$Hoc^yCL%EkYC4S zbiPr$yvC}`T1WGjq@IbJnx2l^-kf?V@v8YKz^X-$lNDeH9jj?G>Q2ba!Tr4&T$j1v zUZ*~RJlKS0i3vZ&<-=PBswMkn`bKAOvNf!V@o9`igyvC_JH!eg``>(`z z;1n`y*<8jx?np@2$45xHd+)jOZ?Ox#7oiU!4goF;;|rLKK2E7mtwKZzD?Ho5&gW3bIg~ zlBMfh`U(_w_c4+?l=UIg?2qt&9%>1E0=VxZb4jv>6{IzF`;E6ebb`3rTdHT$G;4fX z@O~~q9kYp?TDRseo2p0cOE~JBhMEL?cCi~V*GgOQd_vN-FA=)8YudZ+GF!bgeFaIv z$sID6Ob46E9gv`&@}rV|PX6UKBSz<`ERJ;HvL*9cMlnWdtaQI1kX0H^_6Mj{ZP_ zL$37!a1wt<8R0^6iDlqokw3^~%;|2%N=SAHNFGN8f=Ch99_wBo(w!ZA%_SjlMP|kG zwhSd%Ovt*S(_lmLA+vO+DR#7EfXkF=VJV0Gp2oUA+i`yB_(tEK$4$S1Xw2^&k;dze z?cTx%nls5HJK5q)k8sna!7y|4ov)3&V8r^4#tSA3?x;vymnYz7x?_u4mCVb&IFGe@ z-@db}xQ=Vkzp%dG{#cxAYk;auV-TkXj7FCa@Z3{N6gvAlFL>@8ZO4>qfrKuX#o&)D zK8;ozWVUBU5~8MbDLVGP`3}2LgHeO^mnUz))elC$e>9Z)3*T2BPlWs|W?S1Bjz9LV z!Dsf<4Z)I1#fC|yBT?T6^oyj*=V&~Td^NagTAL&!DS8;s%E-D4!Q>M@ zjt)S4^p_CPKZnY$XO?Xo>c~}S%sW4L7LSfdcv6!rljm5Zr zw|JDfY8}S|45~)bt5qZ$2E~BjcNozz=v$pt!({j!{cRYj0&zR^vFkn~3zt^u{Yp>G zCX9g)#at-czo8k)qdVA$(Kc67WG4X;%InBwB}Z^O)dDndvrVjK}+wJ zZrwQjuI-Bwte`--*^-p$>iRf4NpMNe%|h{`Xen+9_%_v}a)C6QEXMIOPosjpm}gVj zg6etemn09eH(uuIqQN}5?Vn?H0huZ@c9W_`+6QK^M`bt7u^Mia2AOI;<8n9oHZNkW zdNsm?y-bZA_@Qe?B0l&(4nbq?3eA)r1T2}lYmivS zCS_G0APbp?U_Crnd@@-z47EX064UZSP51p?*IrN#I6a$mV%xO~;&5^L5(A@FQ8LVb z-0i%+vl^Gl(7BbpV%p;4Z&%ZgE+`0EpU!4o1H`FnV_4ioh7IEj9)@(2hz03z*)lCcR6NWgmzy0)-OV8-sI^{do4kNXAjaoZSC9a*;))}GmRlC& z%gE=4PK{*v`*cnFBu_3PB%DbKVwns0rjC$9&0%ZS%xE>98p&(C;hfHI!vuA;8>fh( z4(FJ})^|}G*OmROkve)|at5G4&Iva2!+q0B+{l1BPU{-UO$ z)~aeJGdZ;Fj;cfSqEPNbZW;cXE~J<`m;A_&jDH6#WWM~R>3a|CUU>l~Vf8CXSSe%b zKiA}2Cmcy(?+$6Z0b}o?st~AHT?Os1H}h#_h^Lh1=$vAmy4i}hSJgl!73KZ7h!Rd4sn`nk&%~Qn;Dw!d zF%zepTvWP$?_0E(;P3Pt+Hh_^R&x+mk<<4Z+Mkc4%xN!fO|5&zFu6GsrT8^3|Dp3c z*VV{K>qd(85F;65^16Im)OLyrpp7U2mK{GmXcEr#WIh7Cobb0(T~#f2G0ol&vtAQe zGfU248Yx4&Ey|vt5iDMGS2VLaid)q_7L|cLf9{QG`ZoPvRJDb^a%ovjM|SnB&qc)I zFNO1w@Cxa?qrKiC*~=BkDj0;uVq@PsPYn!j>c4NA$h~v?(2CRWGZozd8d5`g!*6?! z-Z|e7guS0KuDUhNv{2Sm%-_%(hbJ=K&Ygy*1$G9wyBaSDKBwqTRsXFF`~!KD0Io5X z>NaOH5=Vv63>IAtB&7y>R((fTaULW*&ImNq=1qIc8c7ddbR_2DBF93@&^NtR)kat9 zeIqNC$^5nXj*te{)|)p#@n`0F-gLdSjbU(Tp{|8k$>C54%~TSZ^1!5iKRVmf*Ld$Y zA||$K+ftiK?NXOT`q#eo6;QWu?mSq{u8N9stYyz{@Z#!T7ruN~VFIk0qH2EM@~xjP zc1_*feH#rWOH{sWN&?|tHznnQ4uTWotazei**F&I>};#;tZe6i#7TvP<;*4JdBbg& zMW3MGM#Md)los@%CkH=>_gQAg2}j`Sig{uNOT}oBO6^_FwH2RXner;Qx#}0;47aN%U~#k9^|>thwtWr`6xtt2kOp4+XQ6ozyhKRu zYN&y1qBvGRtQ&X!`pEQ@hD7#3-5U6TH89{u`*R%RH48LGSvd_fy z54KY@=(`ioVKy)Nnz81mtV8BugZwmVKf`Yxu^PX0&9Rr`n~=hFJURCW=XKJforHAQ z8r)(6{vQ5*Urm8Wc(2#%@E}ZmrtrSVIjACzOLjH#^gsnJSaWvkILmj+XUi28e#8Gg0T^3-EJCp>x4_w%O3TvB0CKpfK`DvxS@?w(3=V2ErGVbO9kq*X0RJFP{QM- zZQ1>~xR~%uHjDZ=wf}2^rxBda0#$R&X=lNt_ReL!JyWpzcTy%St$x}w^Ss0}?V&a>RJHGngM_f2mT}9%t zjvA0Z*_qmWA@szgBn;1JP9_Uf^NnBBh7vPO{?w(OGmn8W`hptE z$()!F-{bTJp*o@mVd4t9c6Hx9;{08LYR_esM{J)^GhAST#kh3qw`LY9T=n{vP9KyX{GPVduTYdvw-SbqrX*$yc12ui<-+)%$!tZ4}U} zBLCQYCBxN!ZeY!L%MJFR=K(c)V#zvWZ0T^g-lk|N>Y#wDPhd+dX=T+05FMI_fak9* zFD~MTz0%ed#@0v0Mgti`0Nq~q5Tzn6xc9QvPESME)N^2c`({zf*O$Ro9Os$}q4#;- z53o`B&-hq3t>m}q-`KBMaYHH8f&=A8q=d0|Dse_12rX9lQE!!@1+gb6)SudI)r!B| zEz-q=Lc?MTeBFfn#6|po=y-I-0^b?6&u<5KiFA019wE}nC!n|{*wvqsdFM_K(?Y8m zbmJ&g7HwlMR2HqT76p6*i~1qAiW)E2xTLxt;uLG4t=BJL2lyIqVu_*cYeXX4%WD1* z2Yeb|byTsANz1u>b83$Td}C??cn7ni^=+UB%+F zL;I%x9VlA&izJ+9>;YM~fA>`fXW8Uyr&>4p*DU@=wk+C%4=zEp2gBDW&WC`+zur!4 zy=+m8nIIGP&fz>h#v;0x>DJ@VMa0a1!OY^1q+6Qol&``d|Lxt5C$6i=%%^WtZ&n{u zM`#osv7p0g`-6b2WL^W&2Ici5)&+%HLoADq%bz#P5&JQpbP0@${5k~{;W+Q~;#@oL zGyC)#HVksX@rZ$WHnf91#q1BZtl#gI`$~5&N@{EyxmR8}#lIAei=u~JIKK6|z)?8l zqAdK_v!upQM8i;FUDGa&Pc~_#wZy3!xMS0nt0)zX=AZx!6U|6%W!d1ar`RZP60!y} zcj+hqB3Q#ZPol*p%!k>sb}OwY?bqW5GBB-NS|#Ymi7T*l2U+IgUHT}ig^+B7hH&&x z$=LkecdSqp{+!5H-Um59Ixtg1S8sKn@y7x&V1Q>J)K;jZ`3^ zw5Qthc^de2&4GU8jy(zX&XaRVR)oa| zU--Q(F(%(3?|>y8G}YzA`FsbXA-TFxzYXrAW)KLgI1y=ss#>cX0K>K% zWr>JJt$JlpKt8>N$YW@R{iP)$L4BI2G_)>yOALZX0h_=gcRG+Dcr_uPXXzD~PjNho z&Ocv#EDs}eBxKCM2f+-jXs^g32LjJYIid=INZJ-8QD6>Mn=b7!59^9VpXW1s<$vFS zL;MX1N-^C)O8B2It2TPmxj7t*iF$WB;1ks{F*yr-`5&fFs4Yd4_251vPApyQN{6Oydm1Zm@^L+j4MQ*DP}ff zCVnNjB8w4;c!JIRR9%t2j(Hso@6YCHs%_g^BlB>A6LQbI?SwGRUUyki<&a9VuP{)S z(S!+K%Q*>6<~47>Cg~tD+Np*#*VP&6pYb+~{oz%l_w1t-^~oqf6&ZG>9?tP$ zNr^-ykMO+v%Prycs3iIj^`ujF>;Q)qU)+$Rv!2gyxKa*tuHNwc+r@+Tw6;q%dB;Fd z^eg?#iN}Imcj$L7XveKz<(}|7r%2vG5kj#v*39NO!`1{oD#q~k&P_cs8aTr^!$In> zeFmvy?->++feOsG#1+~e^rx;tPj~*OUK}tEIw>!jxOmY~`Nk_lpT+xEe}Ndll1y^7 z6SdR~o6>Rt;tP3xOU#SJ`JjldTdg zo;KSR+Pt=md*lhTQcFVdi9ES{$?ORaH`;*M3rD#eddwu+@Zi9C$ee`*o^_K&p+#X` zP=vIkx4D|>zQuspuaETREFi>a0`>*RXJ9m5UH=?~7pZGh(Kz26iKvS$@e<_$40Rzo zmO^+s9HSEQF>D@UaNv}$j)G!pMZ!(Daj9%Kt(P_1!os3-_3(#_=@25#)Ke6jg_`GG zCts^ScY?;DPu8fM?B~!_&@%e!`1Mh^8#Br=n%=qPikn6IX}M=40?;Wg%!MtM)29tU z0mm|)o6x1dy8;Ct*|Q*8^&nC@^?z8SM)>y02tWT>j-*>-sJ9BOJyuH0LG3@0i<1E` zyuM3DAr}n(U{;Dk9i*B@HyL!=rK;16!m=3%4wmT79>Sp%Au$cZ_nqub!OH9&DrKV4 z`V}_6CmYD9jvq)74zv#GF6|HT4b#@WO%F?_7MxL^fn-56W!0| z$MG36tqn;GKL=KJj%0dB5}=9)PmtyUPJM<{=uC`^P?2{u49k;x-jt2T^gU<=wb}0@OJ{XE^ z9f^fHi^bvccGH@bfje7!D!;gpKip!ItzzwiUT?pa+K>K#l%=+$VqAF`+6}7F-7Kq` zqW}4;->$GR0xW54=o%enXnysju59tF(_ZE74C<Dd{2Vz_al|Zt}(&^XP}&t+&IT3j7go2^8iTQy)Z_sS;~jMraDN4oK5r zmCV}CN>fIvAZR6+S(jz&kA#f%eRT!=B^L&w4q(KD?JgP681ka zEbx=J33udf<+0Cnl$dbJGh>S4r_7E@RMkO{%pzzzWLhs6Z7UR;Np#NUPTCq6Q@)|{ zu3E5J1GIwZQ|rnmIPSi1o23ja>G&ic%v#W;J?L2YVhv_8`;>H^yI`eED!RrI75_YZ zZy?cF&^xpUTZ2t%X|_dkPd`?wf_jGMYp|5EqZVC|ASd(p(5m}1i#uR~NWUji=Cdi?&$)J3ggJ@~-PQ(8hCO3TaJ$z^ zT}|*wrd%^jeBZN4pBiA%t}P5YCiV)@Op#l%Rd(l=7gLBBZv}n(RcZhecA|i};k(h5RIzE*RR0GSq z1$C#8$eyS_A-SjxXz!Z0Hp zl>fA(s)x&1R<#69VQS2^YQAv}Jh+y%1XP0w>NDDd4P=nmY%lWGVLP*ewl`(UVaiFc z>DyNJT%ymzm0WtWL?^wTVQzY-KgL{2{aECOY1PTA-8-DB*7}9J)sM1&6Tpj93xJqT zeFjownnwUU$;RxOw*)uU{qcf-1$&}A*r}dLD!sz@Qt%Co-^dqMYh`?c4fl*66^wTU z&l&l}#b2P{at`qn3v%k$8}I(gNOkv`53E~Owh+*qApSm;e}?C^3~1rqiIo@r{c30O zt!rqGZO@eoiUVXZ98w)g)3DJd0KhB*H|-KAu0MG6K#$L0xBK>(rj98c@6#Z~g$;X{fNFx`s zCSjgEQDdR%LKLusz?Rt83;awg6~eEL<|BtupLN*SgRLwe6)fQ%yYI%0H=3>a!+@gnH$!RvXH zqe?Om`Re)>1r*isPeJAMG;Q=IcQ0AnvRZ~j(;v);6>J_mzDL`l8qAtFZE9QeQ)=i)Snf1{Um+_Hz`(>5`lCphWlEV%K*?g;tJJd8= zlnj>ieQ?P`rt|i#d=whB4tz!5Nmm!r=k|ZmX4e5Jy5CX=0QE3%TdyHolft^Jm^E@g zclr*u1^a?Z--HWgejgN}Z055SIi;y+a+)P4aj3nvEJ9FoC8#d6S4+*7H|4cYaaU$VXU(Gy9jUl7j?aDyC6h{1a~? zxYz-~zpS5;A(2SPQtw2vkAha|&zuKbrA!}*tWi|&tQ`~6%MOTy)EOn&0fnLTGJe)`Z^|1?p? zv7P&Je7UO;=KDN+nVi7Yq)cL2ZY9lnp^4uKgR

    s$*rls@!sHBO;ZCZI;P{yKGwD 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 L_^*Fz5Rm@|Z!jRr diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 26eaf6a..47fd61f 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.5 -bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.8 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.7 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.0 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.6 +bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.1 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=2.0.1 +bld.version=2.1.0 From c60531f6170ed701ac742986cb8c5e13a6b823ea Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 8 Sep 2024 19:30:53 -0700 Subject: [PATCH 795/842] Moved to LangChain4J for ChatGPT and Gemini modules --- .gitignore | 1 + config/detekt/baseline.xml | 6 +- lib/bld/bld-wrapper.properties | 2 +- pom.xml | 50 +++++++++----- properties/mobibot.properties | 13 +--- .../java/net/thauvin/erik/MobibotBuild.java | 19 ++++-- .../net/thauvin/erik/mobibot/Mobibot.kt | 4 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +- .../modules/{ChatGpt.kt => ChatGpt2.kt} | 67 ++++-------------- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../mobibot/modules/{Gemini.kt => Gemini2.kt} | 68 +++++-------------- .../{ChatGptTest.kt => ChatGpt2Test.kt} | 12 ++-- .../modules/{GeminiTest.kt => Gemini2Test.kt} | 15 ++-- website/index.html | 2 +- 14 files changed, 102 insertions(+), 163 deletions(-) rename src/main/kotlin/net/thauvin/erik/mobibot/modules/{ChatGpt.kt => ChatGpt2.kt} (62%) rename src/main/kotlin/net/thauvin/erik/mobibot/modules/{Gemini.kt => Gemini2.kt} (58%) rename src/test/kotlin/net/thauvin/erik/mobibot/modules/{ChatGptTest.kt => ChatGpt2Test.kt} (84%) rename src/test/kotlin/net/thauvin/erik/mobibot/modules/{GeminiTest.kt => Gemini2Test.kt} (79%) diff --git a/.gitignore b/.gitignore index 6c5fc87..d2daf1c 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ local.properties logs mobibot.properties out +/target/ diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index b20abc9..719ca4e 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -71,9 +71,6 @@ ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException ReturnCount:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) - SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException - SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException - SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> @@ -82,7 +79,9 @@ ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> ThrowsCount:WolframAlpha.kt$WolframAlpha.Companion$@JvmStatic @Throws(ModuleException::class) fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String + TooGenericExceptionCaught:ChatGpt2.kt$ChatGpt2.Companion$e: Exception TooGenericExceptionCaught:Gemini.kt$Gemini.Companion$e: Exception + TooGenericExceptionCaught:Gemini2.kt$Gemini2.Companion$e: Exception TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException TooManyFunctions:EntryLink.kt$EntryLink : Serializable @@ -94,6 +93,7 @@ WildcardImport:FeedMgrTest.kt$import assertk.assertions.* WildcardImport:FeedReaderTest.kt$import assertk.assertions.* WildcardImport:FeedsManager.kt$import com.rometools.rome.feed.synd.* + WildcardImport:Gemini2Test.kt$import assertk.assertions.* WildcardImport:GeminiTest.kt$import assertk.assertions.* WildcardImport:GoogleSearchTest.kt$import assertk.assertions.* WildcardImport:JokeTest.kt$import assertk.assertions.* diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 47fd61f..ed0fdb7 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,7 +1,7 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.6 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.1 diff --git a/pom.xml b/pom.xml index 7996b05..5d1b89a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240809180718 + 0.8.0-rc+20240908192921 mobibot @@ -18,7 +18,7 @@ org.apache.commons commons-lang3 - 3.16.0 + 3.17.0 compile @@ -51,34 +51,28 @@ 33.2.1-jre compile - - com.google.cloud - google-cloud-vertexai - 1.7.0 - compile - org.jetbrains.kotlin kotlin-stdlib - 2.0.10 + 2.0.20 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.0.10 + 2.0.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.0.10 + 2.0.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.0.10 + 2.0.20 compile @@ -96,25 +90,49 @@ org.slf4j slf4j-api - 2.0.15 + 2.0.16 compile org.apache.logging.log4j log4j-api - 2.23.1 + 2.24.0 compile org.apache.logging.log4j log4j-core - 2.23.1 + 2.24.0 compile org.apache.logging.log4j log4j-slf4j2-impl - 2.23.1 + 2.24.0 + compile + + + dev.langchain4j + langchain4j-open-ai + 0.34.0 + compile + + + dev.langchain4j + langchain4j-google-ai-gemini + 0.34.0 + compile + + + dev.langchain4j + langchain4j-core + 0.34.0 + compile + + + dev.langchain4j + langchain4j + 0.34.0 compile diff --git a/properties/mobibot.properties b/properties/mobibot.properties index a7526cc..fd7b104 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -45,7 +45,6 @@ disabled-modules=mastodon # Automatically post links to Mastodon #mastodon-auto-post=true - # # Get Exchange Rate API key from: https://www.exchangerate-api.com/ # @@ -75,19 +74,13 @@ disabled-modules=mastodon #wolfram-units=imperial # -# ChatGPT/OpenAI API key from: https://beta.openai.com/account/api-keys +# Get ChatGPT/OpenAI API key from: https://platform.openai.com/api-keys # #chatgpt-api-key= #chatgpt-max-tokens=1024 # -# Set a Vertex AI Gemini API project in Google Cloud: -# https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/quickstart-multimodal -# Don't forget to: -# gcloud config set project PROJECT_ID -# gcloud auth login LOGIN -# gcloud auth application-default login +# Get Google Gemini API key from https://ai.google.dev/gemini-api/docs/api-key # -#gemini-project-id= -#gemini-location=us-west1 +#gemini-api-key= #gemini-max-tokens=1024 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1e6649b..980f2a6 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -77,20 +77,20 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 23, 1); - var kotlin = version(2, 0, 10); + var log4j = version(2, 24, 0); + var kotlin = version(2, 0, 20); + var langchain = version(0, 34, 0); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) - .include(dependency("org.apache.commons", "commons-lang3", "3.16.0")) + .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) .include(dependency("org.apache.commons", "commons-text", "1.12.0")) .include(dependency("commons-codec", "commons-codec", "1.17.1")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google .include(dependency("com.google.code.gson", "gson", "2.11.0")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.7.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -99,10 +99,15 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.15")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) + // LangChain4J + .include(dependency("dev.langchain4j", "langchain4j-open-ai", langchain)) + .include(dependency("dev.langchain4j", "langchain4j-google-ai-gemini", langchain)) + .include(dependency("dev.langchain4j", "langchain4j-core", langchain)) + .include(dependency("dev.langchain4j", "langchain4j", langchain)) // Misc. .include(dependency("com.rometools", "rome", "2.1.0")) .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) @@ -118,8 +123,8 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 3))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 3))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 0))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 0))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 88bed54..1e49a88 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -396,11 +396,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Load the modules addons.add(Calc()) - addons.add(ChatGpt()) + addons.add(ChatGpt2()) addons.add(CryptoPrices()) addons.add(CurrencyConverter()) addons.add(Dice()) - addons.add(Gemini()) + addons.add(Gemini2()) addons.add(GoogleSearch()) addons.add(Info(tell, seen)) addons.add(Joke()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 3abf162..5b5e5d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240712110931" + const val VERSION = "0.8.0-rc+20240908190240" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1720807771484L), ZoneId.systemDefault() + Instant.ofEpochMilli(1725847361020L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt similarity index 62% rename from src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt index c7ba4ba..854bb9f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt @@ -31,23 +31,16 @@ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Constants +import dev.langchain4j.model.openai.OpenAiChatModel +import dev.langchain4j.model.openai.OpenAiChatModelName import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage -import org.json.JSONArray -import org.json.JSONException -import org.json.JSONObject import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse -class ChatGpt : AbstractModule() { - val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) +class ChatGpt2 : AbstractModule() { + val logger: Logger = LoggerFactory.getLogger(ChatGpt2::class.java) override val name = CHATGPT_NAME @@ -93,9 +86,6 @@ class ChatGpt : AbstractModule() { */ const val MAX_TOKENS_PROP = "chatgpt-max-tokens" - // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/chat/completions" - // ChatGPT command private const val CHATGPT_CMD = "chatgpt" @@ -103,48 +93,15 @@ class ChatGpt : AbstractModule() { @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String { if (!apiKey.isNullOrEmpty()) { - val jsonObject = JSONObject() - jsonObject.put("model", "gpt-3.5-turbo-1106") - jsonObject.put("max_tokens", maxTokens) - val message = JSONObject() - message.put("role", "user") - message.put("content", query) - val messages = JSONArray() - messages.put(message) - jsonObject.put("messages", messages) - - val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST(HttpRequest.BodyPublishers.ofString(jsonObject.toString())) - .build() try { - val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) - if (response.statusCode() == 200) { - try { - val jsonResponse = JSONObject(response.body()) - val choices = jsonResponse.getJSONArray("choices") - return choices.getJSONObject(0).getJSONObject("message").getString("content").trim() - } catch (e: JSONException) { - throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e - ) - } - } else { - if (response.statusCode() == 429) { - throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." - ) - } else { - throw IOException("HTTP Status Code: " + response.statusCode()) - } - } - } catch (e: IOException) { + val model = OpenAiChatModel.builder() + .apiKey(apiKey) + .modelName(OpenAiChatModelName.GPT_4_O) + .maxTokens(maxTokens) + .build() + + return model.generate(query) + } catch (e: Exception) { throw ModuleException( "$CHATGPT_CMD($query): IO", "An IO error has occurred while conversing with $CHATGPT_NAME.", diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 4fb7555..3c8ea92 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -217,6 +217,6 @@ class CurrencyConverter : AbstractModule() { init { commands.add(CURRENCY_CMD) initProperties(API_KEY_PROP) - loadSymbols(properties[ChatGpt.API_KEY_PROP]) + loadSymbols(properties[ChatGpt2.API_KEY_PROP]) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt similarity index 58% rename from src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 2e4ed91..812aed3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -31,12 +31,7 @@ package net.thauvin.erik.mobibot.modules -import com.google.cloud.vertexai.VertexAI -import com.google.cloud.vertexai.api.GenerationConfig -import com.google.cloud.vertexai.api.HarmCategory -import com.google.cloud.vertexai.api.SafetySetting -import com.google.cloud.vertexai.generativeai.GenerativeModel -import com.google.cloud.vertexai.generativeai.ResponseHandler +import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage import org.pircbotx.hooks.types.GenericMessageEvent @@ -45,8 +40,8 @@ import org.slf4j.LoggerFactory import java.util.* -class Gemini : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Gemini::class.java) +class Gemini2 : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Gemini2::class.java) override val name = GEMINI_NAME @@ -55,8 +50,7 @@ class Gemini : AbstractModule() { try { val answer = chat( args.trim(), - properties[PROJECT_ID_PROP], - properties[LOCATION_PROP], + properties[GEMINI_API_KEY], properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (!answer.isNullOrEmpty()) { @@ -82,17 +76,12 @@ class Gemini : AbstractModule() { const val GEMINI_NAME = "Gemini" /** - * The Google cloud project ID property. + * The API key */ - const val PROJECT_ID_PROP = "gemini-project-id" + const val GEMINI_API_KEY = "gemini-api-key" /** - * The Vertex AI location property. - */ - const val LOCATION_PROP = "gemini-location" - - /** - * The max number of tokens property. + * The max number of output tokens property. */ const val MAX_TOKENS_PROP = "gemini-max-tokens" @@ -103,40 +92,18 @@ class Gemini : AbstractModule() { @Throws(ModuleException::class) fun chat( query: String, - projectId: String?, - location: String?, - maxToken: Int + apiKey: String?, + maxTokens: Int ): String? { - if (!projectId.isNullOrEmpty() && !location.isNullOrEmpty()) { + if (!apiKey.isNullOrEmpty()) { try { - VertexAI(projectId, location).use { vertexAI -> - val generationConfig = GenerationConfig.newBuilder().setMaxOutputTokens(maxToken).build() - val safetySettings = listOf( - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_HATE_SPEECH) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build(), - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build(), - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build(), - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_HARASSMENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build() - ) - val model = GenerativeModel.Builder().setModelName("gemini-1.5-flash-001") - .setGenerationConfig(generationConfig) - .setVertexAi(vertexAI).build() - .withSafetySettings(safetySettings) + val gemini = GoogleAiGeminiChatModel.builder() + .apiKey(apiKey) + .modelName("gemini-1.5-flash") + .maxOutputTokens(maxTokens) + .build() - val response = model.generateContent(query) - return ResponseHandler.getText(response) - } + return gemini.generate(query) } catch (e: Exception) { throw ModuleException( "$GEMINI_CMD($query): IO", @@ -159,7 +126,6 @@ class Gemini : AbstractModule() { add(Utils.helpFormat("%c $GEMINI_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $GEMINI_CMD how do I make an HTTP request in Javascript?")) } - initProperties(PROJECT_ID_PROP, LOCATION_PROP, MAX_TOKENS_PROP) + initProperties(GEMINI_API_KEY, MAX_TOKENS_PROP) } - } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt similarity index 84% rename from src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt index 1809a9f..4332a52 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt @@ -39,10 +39,10 @@ import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.LocalProperties import kotlin.test.Test -class ChatGptTest : LocalProperties() { +class ChatGpt2Test : LocalProperties() { @Test fun testApiKey() { - assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } + assertFailure { ChatGpt2.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } @@ -51,7 +51,7 @@ class ChatGptTest : LocalProperties() { fun testChatOnCoverage() { if (System.getenv("CI") == null || System.getenv("COVERAGE_JDK") != null) { assertThat( - ChatGpt.chat("how do I encode a URL in java?", getProperty(ChatGpt.API_KEY_PROP), 60) + ChatGpt2.chat("how do I encode a URL in java?", getProperty(ChatGpt2.API_KEY_PROP), 60) ).contains("URLEncoder") } } @@ -59,12 +59,12 @@ class ChatGptTest : LocalProperties() { @Test @DisableOnCi fun testChat() { - val apiKey = getProperty(ChatGpt.API_KEY_PROP) + val apiKey = getProperty(ChatGpt2.API_KEY_PROP) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ChatGpt2.chat("how do I make an HTTP request in Javascript?", apiKey, 200) ).contains("XMLHttpRequest") - assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, -1) } + assertFailure { ChatGpt2.chat("1 liter to gallon", apiKey, -1) } .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt similarity index 79% rename from src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt index 1f0202f..226ff6a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt @@ -37,10 +37,10 @@ import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.LocalProperties import kotlin.test.Test -class GeminiTest : LocalProperties() { +class Gemini2Test : LocalProperties() { @Test fun testApiKey() { - assertFailure { Gemini.chat("1 gallon to liter", "", "", 1024) } + assertFailure { Gemini2.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } @@ -48,19 +48,18 @@ class GeminiTest : LocalProperties() { @Test @DisableOnCi fun chatPrompt() { - val projectId = getProperty(Gemini.PROJECT_ID_PROP) - val location = getProperty(Gemini.LOCATION_PROP) - val maxTokens = getProperty(Gemini.MAX_TOKENS_PROP).toInt() + val apiKey = getProperty(Gemini2.GEMINI_API_KEY) + val maxTokens = getProperty(Gemini2.MAX_TOKENS_PROP).toInt() assertThat( - Gemini.chat("how do I make an HTTP request in Javascript?", projectId, location, maxTokens) + Gemini2.chat("how do I make an HTTP request in Javascript?", apiKey, maxTokens) ).isNotNull().contains("XMLHttpRequest") assertThat( - Gemini.chat("how do I encode a URL in java?", projectId, location, 60) + Gemini2.chat("how do I encode a URL in java?", apiKey, 60) ).isNotNull().contains("URLEncoder") - assertFailure { Gemini.chat("1 liter to gallon", projectId, "blah", 40) } + assertFailure { Gemini2.chat("1 liter to gallon", "foo", 40) } .isInstanceOf(ModuleException::class.java) } } diff --git a/website/index.html b/website/index.html index fffe650..370c3cf 100644 --- a/website/index.html +++ b/website/index.html @@ -38,10 +38,10 @@

  37. Apache Commons Net
  38. CryptoPrice
  39. exp4j
  40. -
  41. Google Vertex AI
  42. JokeAPI
  43. jsoup
  44. kotlinx-cli
  45. +
  46. LangChain4J
  47. OkHttp
  48. OWM JAPIs
  49. Pinboard Poster
  50. From 863de0c21997ce569a4671f337fdea9114bad49a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 10 Sep 2024 00:08:51 -0700 Subject: [PATCH 796/842] Bumped Kotlin extension to version 1.0.2 --- lib/bld/bld-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index ed0fdb7..547a2bb 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -4,7 +4,7 @@ bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.1 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.2 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.1.0 From ff98e39b0c72bff892158f751cb1d0cd6e3806d4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 10 Sep 2024 00:09:47 -0700 Subject: [PATCH 797/842] Test against Kotlin 2.0.20 in CI workflows --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 17b50f1..8dabc3f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ commands: - sdkman/setup-sdkman - sdkman/sdkman-install: candidate: kotlin - version: 2.0.0 + version: 2.0.20 - run: name: Download dependencies command: ./bld download diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index a3c4951..f76f164 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,6 +1,6 @@ name: bld-ci -on: [ push, pull_request, workflow_dispatch ] +on: [push, pull_request, workflow_dispatch] env: ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [ 17, 21, 22 ] - kotlin-version: [ 1.9.24, 2.0.0 ] + java-version: [17, 21, 22] + kotlin-version: [1.9.24, 2.0.20] steps: - name: Checkout source repository From 97e1e5e73be534f0f638829c11080e3bbfa590b8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 18 Sep 2024 20:28:58 -0700 Subject: [PATCH 798/842] Bumped Kotlin Coroutines to version 1.9.0 --- pom.xml | 4 ++-- src/bld/java/net/thauvin/erik/MobibotBuild.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5d1b89a..a50df02 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240908192921 + 0.8.0-rc+20240918181213 mobibot @@ -78,7 +78,7 @@ org.jetbrains.kotlinx kotlinx-coroutines-core - 1.8.1 + 1.9.0 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 980f2a6..6c4a09c 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -45,7 +45,6 @@ import rife.tools.exceptions.FileUtilsErrorException; import java.io.File; import java.io.IOException; -import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -96,7 +95,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.1")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.9.0")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) From 4b908d596ef08f712a9846158a0ed8ec7ef2ad7d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 18 Sep 2024 20:37:11 -0700 Subject: [PATCH 799/842] Converted deploy script to fish --- deploy.fish | 11 +++++++++++ deploy.sh | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) create mode 100755 deploy.fish delete mode 100755 deploy.sh diff --git a/deploy.fish b/deploy.fish new file mode 100755 index 0000000..d69ee4e --- /dev/null +++ b/deploy.fish @@ -0,0 +1,11 @@ +#!/usr/bin/env fish + +./bld clean jar deploy +if test $status -eq 0 + echo "cd /home/mobibot/mobitopia/mobibot +lcd deploy +put *.jar +cd lib +rm *.jar +put lib/*.jar" | sftp nix4 +end diff --git a/deploy.sh b/deploy.sh deleted file mode 100755 index d1c1f23..0000000 --- a/deploy.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -./bld clean jar deploy -[ $? -eq 0 ] && sftp nix4.thauvin.us < Date: Wed, 27 Nov 2024 22:24:45 -0800 Subject: [PATCH 800/842] Updated dependencies Bumped Kotlin to version 2.1.0 Bumped Log4J to version 2.24.2 Bumped LangChain4J to version 0.36.2 Bumped Jsoup to version 1.18.2 Bumped UrlEncoder to version 1.6.0 Bumped JUnit to version 5.11.3 --- .github/workflows/bld.yml | 4 ++-- .idea/kotlinc.xml | 16 ++++++++++++++++ README.md | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 14 +++++++------- 4 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 .idea/kotlinc.xml diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index f76f164..cb0ad5d 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [17, 21, 22] - kotlin-version: [1.9.24, 2.0.20] + java-version: [17, 21, 23] + kotlin-version: [1.9.24, 2.0.20, 2.1.0] steps: - name: Checkout source repository diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..0273acf --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index e438c94..b31ec67 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.0.20-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-2.1.0-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6c4a09c..3858951 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -76,9 +76,9 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 24, 0); - var kotlin = version(2, 0, 20); - var langchain = version(0, 34, 0); + var log4j = version(2, 24, 2); + var kotlin = version(2, 1, 0); + var langchain = version(0, 36, 2); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) @@ -113,17 +113,17 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20240303")) - .include(dependency("org.jsoup", "jsoup", "1.18.1")) + .include(dependency("org.jsoup", "jsoup", "1.18.2")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) - .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.5.0")); + .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 0))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 0))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 3))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 3))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From f27501634c7d6621284186c6bbebb53427ce03fb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 27 Nov 2024 22:26:46 -0800 Subject: [PATCH 801/842] Ensured HttpUrlConnection is properly disconnected --- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 6a8e4ff..92b4883 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -421,14 +421,22 @@ object Utils { @Throws(IOException::class) fun URL.reader(): UrlReaderResponse { val connection = this.openConnection() as HttpURLConnection - connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" - ) - return if (connection.responseCode.isHttpSuccess()) { - UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) - } else { - UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) + try { + connection.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + ) + return if (connection.responseCode.isHttpSuccess()) { + UrlReaderResponse( + connection.responseCode, + connection.inputStream.bufferedReader().use { it.readText() }) + } else { + UrlReaderResponse( + connection.responseCode, + connection.errorStream.bufferedReader().use { it.readText() }) + } + } finally { + connection.disconnect() } } From 83b4b18be743593ddf640140bad7d0f5c1e45551 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 27 Nov 2024 22:29:31 -0800 Subject: [PATCH 802/842] Added pom-root command --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 3858951..897a966 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -200,6 +200,12 @@ public class MobibotBuild extends Project { .execute(); } + @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") + public void pomRoot() throws FileUtilsErrorException { + PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(), + new File(workDirectory, "pom.xml")); + } + @BuildCommand(value = "release-info", summary = "Generates the ReleaseInfo class") public void releaseInfo() throws Exception { new GeneratedVersionOperation() @@ -211,10 +217,4 @@ public class MobibotBuild extends Project { .extension(".kt") .execute(); } - - @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") - public void pomRoot() throws FileUtilsErrorException { - PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(), - new File(workDirectory, "pom.xml")); - } } From 64bd5c92afdbd834f5833738d39f5e4ba371c49d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 27 Nov 2024 22:32:12 -0800 Subject: [PATCH 803/842] Don't clean/delete deploy dir if not there --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 897a966..c95d779 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -141,7 +141,10 @@ public class MobibotBuild extends Project { @Override public void clean() throws Exception { - FileUtils.deleteDirectory(new File("deploy")); + var deploy = new File("deploy"); + if (deploy.exists()) { + FileUtils.deleteDirectory(deploy); + } super.clean(); } From 700369d7c9b363c5ee5e992f2820269471b2bbf5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 21 Dec 2024 06:59:40 -0800 Subject: [PATCH 804/842] Updated dependencies Bumped JUnit to version 5.11.4 Bumped Log4j to version 2.24.3 Bumped Commons Text to version 1.13.0 Bumped Jsoup to version 1.18.3 Bumped Kotlin Coroutines to version 1.10.1 Bumped Kotlin extension to version 1.0.3 --- lib/bld/bld-wrapper.properties | 2 +- .../java/net/thauvin/erik/MobibotBuild.java | 20 ++++++++++--------- .../thauvin/erik/mobibot/modules/Gemini2.kt | 1 - 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 547a2bb..ae6925d 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -4,7 +4,7 @@ bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.2 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.1.0 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index c95d779..6800ec1 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -76,7 +76,7 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 24, 2); + var log4j = version(2, 24, 3); var kotlin = version(2, 1, 0); var langchain = version(0, 36, 2); scope(compile) @@ -84,7 +84,7 @@ public class MobibotBuild extends Project { .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) - .include(dependency("org.apache.commons", "commons-text", "1.12.0")) + .include(dependency("org.apache.commons", "commons-text", "1.13.0")) .include(dependency("commons-codec", "commons-codec", "1.17.1")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google @@ -95,7 +95,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.9.0")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) @@ -113,7 +113,7 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20240303")) - .include(dependency("org.jsoup", "jsoup", "1.18.2")) + .include(dependency("org.jsoup", "jsoup", "1.18.3")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) @@ -122,8 +122,8 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 3))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 3))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 4))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 4))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); @@ -152,9 +152,11 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - new CompileKotlinOperation() - .fromProject(this) - .execute(); + var op = new CompileKotlinOperation() + .kotlinHome("/opt/kotlinc/") + .fromProject(this); + op.compileOptions().verbose(true); + op.execute(); } @Override diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 812aed3..365fe60 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -39,7 +39,6 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import java.util.* - class Gemini2 : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(Gemini2::class.java) From 4f0f4214c531fcd72fd6f18159069bc467628ac1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 21 Dec 2024 07:14:10 -0800 Subject: [PATCH 805/842] Updated POM --- pom.xml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index a50df02..8c805f1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240918181213 + 0.8.0-rc+20241221064558 mobibot @@ -24,7 +24,7 @@ org.apache.commons commons-text - 1.12.0 + 1.13.0 compile @@ -54,31 +54,31 @@ org.jetbrains.kotlin kotlin-stdlib - 2.0.20 + 2.1.0 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.0.20 + 2.1.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.0.20 + 2.1.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.0.20 + 2.1.0 compile org.jetbrains.kotlinx kotlinx-coroutines-core - 1.9.0 + 1.10.1 compile @@ -96,43 +96,43 @@ org.apache.logging.log4j log4j-api - 2.24.0 + 2.24.3 compile org.apache.logging.log4j log4j-core - 2.24.0 + 2.24.3 compile org.apache.logging.log4j log4j-slf4j2-impl - 2.24.0 + 2.24.3 compile dev.langchain4j langchain4j-open-ai - 0.34.0 + 0.36.2 compile dev.langchain4j langchain4j-google-ai-gemini - 0.34.0 + 0.36.2 compile dev.langchain4j langchain4j-core - 0.34.0 + 0.36.2 compile dev.langchain4j langchain4j - 0.34.0 + 0.36.2 compile @@ -168,7 +168,7 @@ org.jsoup jsoup - 1.18.1 + 1.18.3 compile @@ -192,7 +192,7 @@ net.thauvin.erik.urlencoder urlencoder-lib-jvm - 1.5.0 + 1.6.0 compile From 67de345c2590401c0803fb1fbddffbdadb21359e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 21 Dec 2024 07:21:48 -0800 Subject: [PATCH 806/842] Fixed kotlin coverage version --- .github/workflows/bld.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index cb0ad5d..cf1ac97 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -7,7 +7,7 @@ env: CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} CI_NAME: "GitHub CI" COVERAGE_JDK: "21" - COVERAGE_KOTLIN: "2.0.0" + COVERAGE_KOTLIN: "2.1.0" EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} KOTLIN_HOME: /usr/share/kotlinc MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} From 16ac7937fa001f90cb0f0e41089e467deccaa699 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 2 Jan 2025 17:58:37 -0800 Subject: [PATCH 807/842] build: Bump org.json to version 20241224 --- pom.xml | 6 +++--- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 8c805f1..5b7ef1c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20241221064558 + 0.8.0-rc+20250102175731 mobibot @@ -162,7 +162,7 @@ org.json json - 20240303 + 20241224 compile @@ -180,7 +180,7 @@ net.thauvin.erik jokeapi - 0.9.2-SNAPSHOT + 1.0.0-SNAPSHOT compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6800ec1..192b596 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -112,7 +112,7 @@ public class MobibotBuild extends Project { .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) - .include(dependency("org.json", "json", "20240303")) + .include(dependency("org.json", "json", "20241224")) .include(dependency("org.jsoup", "jsoup", "1.18.3")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) From f05ab9751361758d695f12d2a6afd24dd4003fb6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 2 Jan 2025 17:59:13 -0800 Subject: [PATCH 808/842] build: Bump JokeApi to version 1.0.0-SNAPSHOT --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 192b596..a7164f8 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -116,7 +116,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jsoup", "jsoup", "1.18.3")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0-SNAPSHOT")) .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) From f58951846635baa4532115ed8d4edd586aaa300e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 2 Jan 2025 18:00:17 -0800 Subject: [PATCH 809/842] chore: Update copyright notices for 2025 --- LICENSE.txt | 2 +- .../java/net/thauvin/erik/MobibotBuild.java | 2 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 35 +++++++++++++++++-- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt2.kt | 4 +-- .../erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../thauvin/erik/mobibot/modules/Gemini2.kt | 4 +-- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 2 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../erik/mobibot/DisableOnCiCondition.kt | 2 +- .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 2 +- .../erik/mobibot/commands/RecapTest.kt | 2 +- .../commands/links/LinksManagerTest.kt | 2 +- .../erik/mobibot/commands/links/ViewTest.kt | 2 +- .../erik/mobibot/commands/seen/SeenTest.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 2 +- .../commands/tell/TellMessagesMgrTest.kt | 2 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 2 +- .../erik/mobibot/entries/EntryLinkTest.kt | 2 +- .../erik/mobibot/entries/FeedMgrTest.kt | 2 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../erik/mobibot/modules/ChatGpt2Test.kt | 4 +-- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- .../mobibot/modules/CurrencyConverterTest.kt | 2 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../erik/mobibot/modules/Gemini2Test.kt | 4 +-- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 2 +- .../erik/mobibot/modules/MastodonTest.kt | 2 +- .../mobibot/modules/ModuleExceptionTest.kt | 2 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/Weather2Test.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- .../erik/mobibot/modules/WordTimeTest.kt | 2 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 2 +- 102 files changed, 138 insertions(+), 107 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 16e13ca..54e8774 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) +Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index a7164f8..6aa683c 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -1,7 +1,7 @@ /* * MobibotBuild.java * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index f3ae0a3..ed34897 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 8716e14..d00939e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index a91c6dc..3ab8d1f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 1e49a88..691a339 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 88e475e..f9076c9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 5b5e5d2..7571a9f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,3 +1,34 @@ +/* + * ReleaseInfo.kt + * + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -14,12 +45,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240908190240" + const val VERSION = "0.8.0-rc+20250102175731" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1725847361020L), ZoneId.systemDefault() + Instant.ofEpochMilli(1735869451156L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 92b4883..fc5ee7d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 5f79fab..4642f42 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 873d31f..0075293 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 88b6c6d..cefcde3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index e4e111d..d7577af 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 6dfff07..13b20b0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 4fae22f..8e244cc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index 3495b28..afa9046 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 314ce99..8668bf7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index bddd56c..14d8d8e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 59b474a..21c96b5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 886b26c..500fd85 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 90f3c04..b9d410d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 54a85b2..960b8aa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index f422566..62cb044 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 2fade4e..f0d9d0c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index ea18613..e688092 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 2d24ba7..a47021b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1a582a9..0d73f6e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index e6971ef..6891c2d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index e51a6bf..f44b357 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 1ad12f6..8af98dc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 69923db..21d7cb9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 790ce0e..c204062 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index f34af98..f193a3c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index ac15595..1f55687 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index 676fbee..4e187d4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index ea536bc..1588704 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index d9d5587..1826101 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index c88f8a4..a807f07 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index 4dc8e9e..a881204 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 17dbde2..1ced830 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index b5fe5b1..7fd320f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt index 854bb9f..9d654c8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt @@ -1,7 +1,7 @@ /* - * ChatGpt.kt + * ChatGpt2.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index fe95fe0..2186d73 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 3c8ea92..2ff4715 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index b6d69ec..5c1dd09 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 365fe60..385e96e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -1,7 +1,7 @@ /* - * Gemini.kt + * Gemini2.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 1611130..26f3e71 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 1c6a5eb..e792ed4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index baeebba..f700757 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 166e39f..d4c2614 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 32240ca..26d374a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 4b56df5..ca18216 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 13afbb7..b8c81f1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 13b9329..d71c91a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt index e0fd947..70ac4ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -1,7 +1,7 @@ /* * War.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 21bf0ae..074edd0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index ba88711..2e2e7ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 8978275..afc0a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index abcd043..56e7b92 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 4b788ba..1a6e58b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index ea11c9c..f06ce89 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 27b1cbd..ef0eeb1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index ae909f6..be6583f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index c785106..de9653d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index a13b429..d45cf5c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index e722ed8..aadebf5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 4555884..27163fb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 8d73a5e..75a1cf9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -1,7 +1,7 @@ /* * DisableOnCi.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt index b0f4771..d887b55 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -1,7 +1,7 @@ /* * DisableOnCiCondition.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index d1ca71d..3a4adb0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 1cb2645..0c3d1c6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index c290262..646a0ea 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 7e56912..dafb862 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 49ee7fa..bd05f70 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index add701e..f332005 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 875342b..ef6f461 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 59338f4..676c5b6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index 9eb433a..abf8224 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 6d5ba78..7b946dc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 33b3598..443c1f9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index d7bde4c..6d3bb6b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 983e225..f67a057 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 577f425..3479108 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index d2b959e..5803092 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index e7ef601..140b8a1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt index 4332a52..c4699a5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt @@ -1,7 +1,7 @@ /* - * ChatGptTest.kt + * ChatGpt2Test.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 0e2ef84..34b8369 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 5ebb7dc..c1c0efc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 007f22f..e34de7b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt index 226ff6a..269874a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt @@ -1,7 +1,7 @@ /* - * GeminiTest.kt + * Gemini2Test.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 752b49e..f9b0832 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 118d736..cf6d03c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 048d91a..abb9235 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 11d4a51..a9e1d43 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index ff706fd..6c3c54c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index 5532e30..f51e203 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 0e4b4ec..519037a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 3fe9189..955a267 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index f4f85f8..5d04560 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 4f827bb..099f3f9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 1211ea4..396efaf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 44054f1..6e85ed1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: From 1a1195ca285df29351f72c882aa9ea8e4e4bda46 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:06:57 -0700 Subject: [PATCH 810/842] Bump bld to version 2.2.1 --- .idea/libraries/bld.xml | 4 ++-- README.md | 2 +- lib/bld/bld-wrapper.jar | Bin 30440 -> 30440 bytes lib/bld/bld-wrapper.properties | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index 5c4010c..153a060 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/README.md b/README.md index b31ec67..8d62a9f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.1.0-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 5425f1c05b18a00f2f1d69df4fc5885b310c372a..73cde27f29a182647776c29655512b0bfa0749f0 100644 GIT binary patch delta 203 zcmaFymhr_~M!o=VW)=|!4h{|mzs(U*6ZvXcK=j6b6B{sNa-Y4%=Jz(UnZUfwefC{Y zmW&gd9z;y7;2a}_^`p=LB0E{9L{8*i9|;9^1_lOJAP(?mWD;RO*f%+^ q#0g^0mJ$yzV^V1_L?9f-SXb%-5&2dc3zaJK;{dr5Wb)*-W!3VxO6ZvXcK=j6b6B{sNa-Y4%=Jz(UnZUfwefC{Y zmW&gd9z;y7;2a}_^`p=LB0E{9L{8<3F#|(@H#^70qR3Eo1_lOJAP(?mWD;RO*f%+^ q#0g^0mJ$yzV^V1_L?9f-SXb%-5&2dc3zaJK;{dr5Wb)*-W!3<}MnA^@ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index ae6925d..478b956 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -7,4 +7,4 @@ bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=2.1.0 +bld.version=2.2.1 From 765279c88718837b52d84792e979ad29b3a59d33 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:08:37 -0700 Subject: [PATCH 811/842] Bump Kotlin to version 2.1.10 --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 2 +- README.md | 2 +- lib/bld/bld-wrapper.properties | 4 +--- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8dabc3f..8814d91 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ commands: - sdkman/setup-sdkman - sdkman/sdkman-install: candidate: kotlin - version: 2.0.20 + version: 2.1.10 - run: name: Download dependencies command: ./bld download diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index cf1ac97..48f1a71 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 23] - kotlin-version: [1.9.24, 2.0.20, 2.1.0] + kotlin-version: [1.9.25, 2.0.20, 2.1.10] steps: - name: Checkout source repository diff --git a/README.md b/README.md index 8d62a9f..84d0ebc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Kotlin](https://img.shields.io/badge/kotlin-2.1.10-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 478b956..0a01ba1 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,8 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 -bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.2.1 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6aa683c..6a9386a 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); - var kotlin = version(2, 1, 0); + var kotlin = version(2, 1, 10); var langchain = version(0, 36, 2); scope(compile) // PircBotX From 4d2f1e7978315c4b3aad5d41f81d7e295138a1fb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:15:01 -0700 Subject: [PATCH 812/842] Update dependencies Bump Detekt extension to version 0.9.9 Bump Generated Version extension t version 1.0.0 Bump Commons Codec to version 1.18.8 Bump Gson to version 2.12.1 Bump SLF4J to version 2.0.17 Bump org.json to version 20250107 Bump Jsoup to version 1.19.1 Bump Junit to version 5.12.1 --- lib/bld/bld-wrapper.properties | 4 +++- pom.xml | 24 +++++++++---------- .../java/net/thauvin/erik/MobibotBuild.java | 19 ++++++++------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 0a01ba1..c1b1941 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,7 +1,9 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.9 +bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.0 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= diff --git a/pom.xml b/pom.xml index 5b7ef1c..3543235 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250102175731 + 0.8.0-rc+20250318045311 mobibot @@ -30,7 +30,7 @@ commons-codec commons-codec - 1.17.1 + 1.18.0 compile @@ -42,7 +42,7 @@ com.google.code.gson gson - 2.11.0 + 2.12.1 compile @@ -54,25 +54,25 @@ org.jetbrains.kotlin kotlin-stdlib - 2.1.0 + 2.1.10 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.1.0 + 2.1.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.1.0 + 2.1.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.1.0 + 2.1.10 compile @@ -90,7 +90,7 @@ org.slf4j slf4j-api - 2.0.16 + 2.0.17 compile @@ -162,13 +162,13 @@ org.json json - 20241224 + 20250107 compile org.jsoup jsoup - 1.18.3 + 1.19.1 compile @@ -180,13 +180,13 @@ net.thauvin.erik jokeapi - 1.0.0-SNAPSHOT + 1.0.0 compile net.thauvin.erik pinboard-poster - 1.1.2-SNAPSHOT + 1.2.0 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6a9386a..9ffb588 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -85,10 +85,10 @@ public class MobibotBuild extends Project { // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) .include(dependency("org.apache.commons", "commons-text", "1.13.0")) - .include(dependency("commons-codec", "commons-codec", "1.17.1")) + .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google - .include(dependency("com.google.code.gson", "gson", "2.11.0")) + .include(dependency("com.google.code.gson", "gson", "2.12.1")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) @@ -98,7 +98,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.17")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) @@ -112,18 +112,19 @@ public class MobibotBuild extends Project { .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) - .include(dependency("org.json", "json", "20241224")) - .include(dependency("org.jsoup", "jsoup", "1.18.3")) + .include(dependency("org.json", "json", "20250107")) + .include(dependency("org.jsoup", "jsoup", "1.19.1")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.2.0")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 4))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 4))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 1))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From bfc468de5bd10bfa0631f04ad81551f18f19a7d7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:16:20 -0700 Subject: [PATCH 813/842] Bump JDK from version 20 to 21 --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8814d91..c781fdc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,11 +46,11 @@ jobs: steps: - build_and_test - bld_jdk20: + bld_jdk21: <<: *defaults docker: - - image: cimg/openjdk:20.0 + - image: cimg/openjdk:21.0 steps: - build_and_test @@ -59,4 +59,4 @@ workflows: bld: jobs: - bld_jdk17 - - bld_jdk20 + - bld_jdk21 From 00178b4e168eb4702c5e9d0f6f830d752b814e75 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 14:55:36 -0700 Subject: [PATCH 814/842] Update User-Agent --- src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt | 3 +-- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index d00939e..0dea8d5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -62,8 +62,7 @@ object Constants { /** * User-Agent */ - const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" + const val USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index fc5ee7d..97797ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -424,7 +424,7 @@ object Utils { try { connection.setRequestProperty( "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + Constants.USER_AGENT ) return if (connection.responseCode.isHttpSuccess()) { UrlReaderResponse( From daf5cad807417cb65735de31cdec23dcfa612bbd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 23:14:34 -0700 Subject: [PATCH 815/842] JDK 24 --- .github/workflows/bld.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 48f1a71..3511dc4 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - java-version: [17, 21, 23] + java-version: [17, 21, 24] kotlin-version: [1.9.25, 2.0.20, 2.1.10] steps: From e974f3f4bc08484733bf7679968b0e57d80b6a45 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 23:55:13 -0700 Subject: [PATCH 816/842] Bump to GPT4 --- src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt index 9d654c8..fbf0e94 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt @@ -96,7 +96,7 @@ class ChatGpt2 : AbstractModule() { try { val model = OpenAiChatModel.builder() .apiKey(apiKey) - .modelName(OpenAiChatModelName.GPT_4_O) + .modelName(OpenAiChatModelName.GPT_4) .maxTokens(maxTokens) .build() From 068ba7b6e5290257326d1989fe9ac1a97feec408 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 23:55:29 -0700 Subject: [PATCH 817/842] Bump to Gemini 2.0 Flash --- src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 385e96e..e85ea7b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -98,7 +98,7 @@ class Gemini2 : AbstractModule() { try { val gemini = GoogleAiGeminiChatModel.builder() .apiKey(apiKey) - .modelName("gemini-1.5-flash") + .modelName("gemini-2.0-flash") .maxOutputTokens(maxTokens) .build() From 23797a2a04d7da96c6413c6ae30fe0b5f73e5bea Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 20 Mar 2025 10:38:09 -0700 Subject: [PATCH 818/842] Bump Kotlin to version 2.1.20 --- README.md | 2 +- pom.xml | 10 +++++----- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 84d0ebc..d43ba67 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.1.10-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-2.1.20-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/pom.xml b/pom.xml index 3543235..c278f8f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250318045311 + 0.8.0-rc+20250320103735 mobibot @@ -54,25 +54,25 @@ org.jetbrains.kotlin kotlin-stdlib - 2.1.10 + 2.1.20 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.1.10 + 2.1.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.1.10 + 2.1.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.1.10 + 2.1.20 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 9ffb588..1ac9eab 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); - var kotlin = version(2, 1, 10); + var kotlin = version(2, 1, 20); var langchain = version(0, 36, 2); scope(compile) // PircBotX From 8537c8a436b449df90eaf5a1895a1f5381231c9c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 20 Mar 2025 10:40:29 -0700 Subject: [PATCH 819/842] Add Kotlin compile options for JDK 24 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1ac9eab..8a8edd9 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -38,6 +38,7 @@ import rife.bld.extension.CompileKotlinOperation; import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; +import rife.bld.extension.kotlin.CompileOptions; import rife.bld.operations.exceptions.ExitStatusException; import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; @@ -153,10 +154,12 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); + final var options = new CompileOptions(); + options.verbose(true).jvmOptions().add("--enable-native-access=ALL-UNNAMED"); var op = new CompileKotlinOperation() .kotlinHome("/opt/kotlinc/") + .compileOptions(options) .fromProject(this); - op.compileOptions().verbose(true); op.execute(); } From 5f1eb63c4fdb798d9a25c5e706ed61f7af6d6659 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 20 Mar 2025 10:54:32 -0700 Subject: [PATCH 820/842] Combine Kotlin compile options --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 8a8edd9..65e7e0c 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -154,8 +154,7 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - final var options = new CompileOptions(); - options.verbose(true).jvmOptions().add("--enable-native-access=ALL-UNNAMED"); + var options = new CompileOptions().verbose(true).jvmOptions("--enable-native-access=ALL-UNNAMED"); var op = new CompileKotlinOperation() .kotlinHome("/opt/kotlinc/") .compileOptions(options) From 4b7f41660fdd717aa2312c476f0ab90a70f9fcb0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 21 Mar 2025 23:36:28 -0700 Subject: [PATCH 821/842] Bump Kotlin extension to version 1.1.0-SNAPSHOT --- lib/bld/bld-wrapper.properties | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index c1b1941..4986847 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -4,7 +4,7 @@ bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.9 bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.0 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.2.1 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 65e7e0c..42436b7 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -39,6 +39,7 @@ import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; import rife.bld.extension.kotlin.CompileOptions; +import rife.bld.extension.kotlin.JvmOptions; import rife.bld.operations.exceptions.ExitStatusException; import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; @@ -154,12 +155,12 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - var options = new CompileOptions().verbose(true).jvmOptions("--enable-native-access=ALL-UNNAMED"); - var op = new CompileKotlinOperation() - .kotlinHome("/opt/kotlinc/") + var options = new CompileOptions().verbose(true); + options.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + new CompileKotlinOperation() .compileOptions(options) - .fromProject(this); - op.execute(); + .fromProject(this) + .execute(); } @Override From 45f602fbe2af7cbfccd1f2b2020539e2ee6f6d87 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 21 Mar 2025 23:37:17 -0700 Subject: [PATCH 822/842] Update snapshot dependencies Bump Pinboard Poster to version 1.2.1-SNAPSHOT Bump JokeApi to version 1.0.1-SNAPSHOT --- pom.xml | 6 +++--- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c278f8f..a3a9f14 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250320103735 + 0.8.0-rc+20250321233454 mobibot @@ -180,13 +180,13 @@ net.thauvin.erik jokeapi - 1.0.0 + 1.0.1-SNAPSHOT compile net.thauvin.erik pinboard-poster - 1.2.0 + 1.2.1-SNAPSHOT compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 42436b7..c2c5f38 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -118,8 +118,8 @@ public class MobibotBuild extends Project { .include(dependency("org.jsoup", "jsoup", "1.19.1")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.2.0")) + .include(dependency("net.thauvin.erik", "jokeapi", "1.0.1-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.2.1-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) From 6f1aa72ad018034120c49c4d8f69808a72ac51dd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:15:10 -0700 Subject: [PATCH 823/842] Add build/test job for Windows --- .github/workflows/bld.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 3511dc4..2627b79 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.10] + kotlin-version: [1.9.25, 2.0.20, 2.1.20] steps: - name: Checkout source repository @@ -56,3 +56,27 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + build-bld-windows: + runs-on: windows-latest + + steps: + - name: Checkout source repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: "zulu" + java-version: "17" + + - name: Download dependencies + run: bld.bat download + + - name: Compile source + run: bld.bat compile + + - name: Run tests + run: bld.bat jacoco From 1f5903c5e92a2cb3829a4737f5517ab83bba3353 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:19:17 -0700 Subject: [PATCH 824/842] Fix bld executable name under Windows --- .github/workflows/bld.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 2627b79..1d57288 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -72,11 +72,13 @@ jobs: distribution: "zulu" java-version: "17" + - run: dir + - name: Download dependencies - run: bld.bat download + run: bld download - name: Compile source - run: bld.bat compile + run: bld compile - name: Run tests - run: bld.bat jacoco + run: bld jacoco From 93d761b13bd7289aebf62f1c4a671c7232229420 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:24:22 -0700 Subject: [PATCH 825/842] Change run to script in Windows job --- .github/workflows/bld.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 1d57288..6d9f43f 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,6 +1,6 @@ name: bld-ci -on: [push, pull_request, workflow_dispatch] +on: [ push, pull_request, workflow_dispatch ] env: ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.20] + java-version: [ 17, 21, 24 ] + kotlin-version: [ 1.9.25, 2.0.20, 2.1.20 ] steps: - name: Checkout source repository @@ -72,13 +72,11 @@ jobs: distribution: "zulu" java-version: "17" - - run: dir - - name: Download dependencies - run: bld download + script: bld.bat download - name: Compile source - run: bld compile + script: bld.bat compile - name: Run tests - run: bld jacoco + script: bld.bat jacoco From 599d819dc9a9d6d2f04730262ba40ff0e797d917 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:28:21 -0700 Subject: [PATCH 826/842] Add call to Windows run steps --- .github/workflows/bld.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 6d9f43f..4f69f88 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,6 +1,6 @@ name: bld-ci -on: [ push, pull_request, workflow_dispatch ] +on: [push, pull_request, workflow_dispatch] env: ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [ 17, 21, 24 ] - kotlin-version: [ 1.9.25, 2.0.20, 2.1.20 ] + java-version: [17, 21, 24] + kotlin-version: [1.9.25, 2.0.20, 2.1.20] steps: - name: Checkout source repository @@ -73,10 +73,10 @@ jobs: java-version: "17" - name: Download dependencies - script: bld.bat download + run: call bld.bat download - name: Compile source - script: bld.bat compile + run: call bld.bat compile - name: Run tests - script: bld.bat jacoco + run: call bld.bat jacoco From 48b92c33fdd8d2f973f346e1c0da7c29c16915c2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:35:11 -0700 Subject: [PATCH 827/842] Set the shell cmd under Windows --- .github/workflows/bld.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 4f69f88..13359e8 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -73,10 +73,13 @@ jobs: java-version: "17" - name: Download dependencies - run: call bld.bat download + run: bld.bat download + shell: cmd - name: Compile source - run: call bld.bat compile + run: bld.bat compile + shell: cmd - name: Run tests - run: call bld.bat jacoco + run: bld.bat jacoco + shell: cmd From 7e58e5840f685ef66908640342fb5f2b5a75219d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:45:24 -0700 Subject: [PATCH 828/842] Revert "Add build/test job for Windows" This reverts commit 6f1aa72a --- .github/workflows/bld.yml | 29 +-------------- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 35 ++----------------- .../erik/mobibot/commands/tell/Tell.kt | 2 +- 3 files changed, 4 insertions(+), 62 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 13359e8..3511dc4 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.20] + kotlin-version: [1.9.25, 2.0.20, 2.1.10] steps: - name: Checkout source repository @@ -56,30 +56,3 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - build-bld-windows: - runs-on: windows-latest - - steps: - - name: Checkout source repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: "zulu" - java-version: "17" - - - name: Download dependencies - run: bld.bat download - shell: cmd - - - name: Compile source - run: bld.bat compile - shell: cmd - - - name: Run tests - run: bld.bat jacoco - shell: cmd diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 7571a9f..42a61aa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,34 +1,3 @@ -/* - * ReleaseInfo.kt - * - * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of this project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -45,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20250102175731" + const val VERSION = "0.8.0-rc+20250322004101" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1735869451156L), ZoneId.systemDefault() + Instant.ofEpochMilli(1742629261438L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index c204062..26fe803 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -287,7 +287,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { // All keyword private const val TELL_ALL_KEYWORD = "all" - //T he delete command. + // The delete command. private const val TELL_DEL_KEYWORD = "del" } From 6173639cdd9cc2fb3b093aa2fdfa600dafd12899 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:47:11 -0700 Subject: [PATCH 829/842] Bump Kotlin matrix version to 2.1.20 --- .github/workflows/bld.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 3511dc4..8e7d939 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.10] + kotlin-version: [1.9.25, 2.0.20, 2.1.20] steps: - name: Checkout source repository From 75ccf1a0ed649f50881e32438a8b154238ecba92 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 23 Mar 2025 20:02:55 -0700 Subject: [PATCH 830/842] Set progressive compiling --- .../java/net/thauvin/erik/MobibotBuild.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index c2c5f38..940ff0d 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -52,6 +52,9 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; @@ -139,6 +142,15 @@ public class MobibotBuild extends Project { } public static void main(String[] args) { + var level = Level.ALL; + var logger = Logger.getLogger("rife.bld.extension"); + var consoleHandler = new ConsoleHandler(); + + consoleHandler.setLevel(level); + logger.addHandler(consoleHandler); + logger.setLevel(level); + logger.setUseParentHandlers(false); + new MobibotBuild().start(args); } @@ -155,12 +167,11 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - var options = new CompileOptions().verbose(true); - options.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); - new CompileKotlinOperation() - .compileOptions(options) - .fromProject(this) - .execute(); + var options = new CompileOptions().progressive(true).verbose(true); + var op = new CompileKotlinOperation().compileOptions(options).fromProject(this); + + op.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + op.execute(); } @Override From eed8277ed630a24361f65b5f70593b0229bddef5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 26 Mar 2025 23:32:56 -0700 Subject: [PATCH 831/842] Add API response logging --- .../thauvin/erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 2186d73..3334a90 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -52,7 +52,7 @@ class CryptoPrices : AbstractModule() { /** * Returns the cryptocurrency market price from - * [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price). + * [Coinbase](https://docs.cdp.coinbase.com/coinbase-app/docs/api-prices#get-spot-price). */ override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (CURRENCIES.isEmpty()) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 34b8369..94a40d9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -39,6 +39,9 @@ import net.thauvin.erik.crypto.CryptoPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies +import org.junit.jupiter.api.BeforeAll +import java.util.logging.ConsoleHandler +import java.util.logging.Level import kotlin.test.Test class CryptoPricesTest { @@ -69,4 +72,16 @@ class CryptoPricesTest { assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") } + + companion object { + @JvmStatic + @BeforeAll + fun beforeAll() { + with(CryptoPrice.logger) { + addHandler(ConsoleHandler().apply { level = Level.FINE }) + level = Level.FINE + useParentHandlers = false + } + } + } } From 310687cdce336af58c2be739196fefc775884d68 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 26 Mar 2025 23:33:32 -0700 Subject: [PATCH 832/842] Update to latest extensions snapshots --- lib/bld/bld-wrapper.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 4986847..976017e 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,9 +1,9 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.9 -bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.0 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.10-SNAPSHOT +bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.1-SNAPSHOT +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10-SNAPSHOT bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= From fedebc7ed41e59c7decb0ede800992060f140e66 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 26 Mar 2025 23:34:14 -0700 Subject: [PATCH 833/842] Cleanup compile command --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 940ff0d..4f6868d 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -39,7 +39,6 @@ import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; import rife.bld.extension.kotlin.CompileOptions; -import rife.bld.extension.kotlin.JvmOptions; import rife.bld.operations.exceptions.ExitStatusException; import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; @@ -167,11 +166,10 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - var options = new CompileOptions().progressive(true).verbose(true); - var op = new CompileKotlinOperation().compileOptions(options).fromProject(this); - - op.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); - op.execute(); + new CompileKotlinOperation() + .compileOptions(new CompileOptions().progressive(true).verbose(true)) + .fromProject(this) + .execute(); } @Override From abc33d05f734c95860cd4717969551019ff06ea2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:28:38 -0700 Subject: [PATCH 834/842] Bump JUnit to version 5.12.2 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 4f6868d..ec448d7 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -126,9 +126,9 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))) - .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 1))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 2))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From 82b40a49b273e469393e40d5f83852a8ce48a541 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:29:32 -0700 Subject: [PATCH 835/842] Update extensions Bump JaCoCo Reports to version 0.9.10 Bump Generated Version to version 1.0.1 --- lib/bld/bld-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 976017e..8b96558 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -2,8 +2,8 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.10-SNAPSHOT -bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.1-SNAPSHOT -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10-SNAPSHOT +bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.1 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= From 491f451acb90d7d74f926d5c5c6cc9e83811f867 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:30:07 -0700 Subject: [PATCH 836/842] Bump Commons Text to version 1.13.1 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index ec448d7..63957d4 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -88,7 +88,7 @@ public class MobibotBuild extends Project { .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) - .include(dependency("org.apache.commons", "commons-text", "1.13.0")) + .include(dependency("org.apache.commons", "commons-text", "1.13.1")) .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google From ef9bbe6e1fd27984f40f0a6639bf64c8f982cec1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:30:30 -0700 Subject: [PATCH 837/842] Bump Gson to version 2.13.0 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 63957d4..b152cd3 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -92,7 +92,7 @@ public class MobibotBuild extends Project { .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google - .include(dependency("com.google.code.gson", "gson", "2.12.1")) + .include(dependency("com.google.code.gson", "gson", "2.13.0")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) From 9e49da5d56187b788363bb07a3be715af9413684 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:30:58 -0700 Subject: [PATCH 838/842] Bump Kotlinx Coroutines to version 1.10.2 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index b152cd3..6ed1d06 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -99,7 +99,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.1")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.2")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.17")) From 749334493bca7090c1fcb549a064802995c30e4a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 18 Apr 2025 15:55:57 -0700 Subject: [PATCH 839/842] Add JitPack token --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 2 +- .gitlab-ci.yml | 2 +- bitbucket-pipelines.yml | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c781fdc..9586fcd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ commands: version: 2.1.10 - run: name: Download dependencies - command: ./bld download + command: ./bld -Djitpack.token=$JITPACK_TOKEN download - run: name: Compile source command: ./bld compile diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 8e7d939..5b88e5a 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -41,7 +41,7 @@ jobs: run: ./bld download - name: Compile source - run: ./bld compile + run: ./bld -Djitpack.token=${{ secrets.JITPACK_TOKEN }} compile - name: Run tests run: ./bld jacoco diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 10b9b0f..a2a4384 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,6 @@ before_script: test: stage: test script: - - ./bld download + - ./bld -Djitpack.token=$JITPACK_TOKEN download - ./bld compile - ./bld test diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index ace99d2..132ed8d 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -15,6 +15,6 @@ pipelines: - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" # Download, compile and test with bld - - ./bld download + - ./bld -Djitpack.token=$JITPACK_TOKEN download - ./bld compile - ./bld test diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6ed1d06..e4da3b1 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -67,7 +67,7 @@ public class MobibotBuild extends Project { pkg = "net.thauvin.erik.mobibot"; name = "mobibot"; version = version(0, 8, 0, "rc+" + - DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); mainClass = pkg + ".Mobibot"; @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { repositories = List.of( MAVEN_LOCAL, MAVEN_CENTRAL, - new Repository("https://jitpack.io"), + new Repository("https://jitpack.io").withCredentials(property("jitpack.token"), "."), SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); From 31e882eb42741adeea637a34b48d573d31ee860d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 18 Apr 2025 17:02:45 -0700 Subject: [PATCH 840/842] Remove test on coverage --- .../net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt index c4699a5..ee3e534 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt @@ -47,15 +47,6 @@ class ChatGpt2Test : LocalProperties() { .hasNoCause() } - @Test - fun testChatOnCoverage() { - if (System.getenv("CI") == null || System.getenv("COVERAGE_JDK") != null) { - assertThat( - ChatGpt2.chat("how do I encode a URL in java?", getProperty(ChatGpt2.API_KEY_PROP), 60) - ).contains("URLEncoder") - } - } - @Test @DisableOnCi fun testChat() { From 86522d6b3ef8b5a2a46aca4220c232292c702b7c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 18 Apr 2025 17:24:30 -0700 Subject: [PATCH 841/842] Revert "Add JitPack token" This reverts commit 749334493bca7090c1fcb549a064802995c30e4a. --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 2 +- .gitlab-ci.yml | 2 +- bitbucket-pipelines.yml | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9586fcd..c781fdc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ commands: version: 2.1.10 - run: name: Download dependencies - command: ./bld -Djitpack.token=$JITPACK_TOKEN download + command: ./bld download - run: name: Compile source command: ./bld compile diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 5b88e5a..8e7d939 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -41,7 +41,7 @@ jobs: run: ./bld download - name: Compile source - run: ./bld -Djitpack.token=${{ secrets.JITPACK_TOKEN }} compile + run: ./bld compile - name: Run tests run: ./bld jacoco diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a2a4384..10b9b0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,6 @@ before_script: test: stage: test script: - - ./bld -Djitpack.token=$JITPACK_TOKEN download + - ./bld download - ./bld compile - ./bld test diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 132ed8d..ace99d2 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -15,6 +15,6 @@ pipelines: - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" # Download, compile and test with bld - - ./bld -Djitpack.token=$JITPACK_TOKEN download + - ./bld download - ./bld compile - ./bld test diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index e4da3b1..6ed1d06 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -67,7 +67,7 @@ public class MobibotBuild extends Project { pkg = "net.thauvin.erik.mobibot"; name = "mobibot"; version = version(0, 8, 0, "rc+" + - DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); mainClass = pkg + ".Mobibot"; @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { repositories = List.of( MAVEN_LOCAL, MAVEN_CENTRAL, - new Repository("https://jitpack.io").withCredentials(property("jitpack.token"), "."), + new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); From b4754f13115e16a10073bdef12eeacd0152174af Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 24 Apr 2025 11:32:11 -0700 Subject: [PATCH 842/842] Bump Gson to version 2.13.1 --- pom.xml | 8 ++++---- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a3a9f14..58768ef 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250321233454 + 0.8.0-rc+20250424113056 mobibot @@ -24,7 +24,7 @@ org.apache.commons commons-text - 1.13.0 + 1.13.1 compile @@ -42,7 +42,7 @@ com.google.code.gson gson - 2.12.1 + 2.13.1 compile @@ -78,7 +78,7 @@ org.jetbrains.kotlinx kotlinx-coroutines-core - 1.10.1 + 1.10.2 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6ed1d06..08367ef 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -92,7 +92,7 @@ public class MobibotBuild extends Project { .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google - .include(dependency("com.google.code.gson", "gson", "2.13.0")) + .include(dependency("com.google.code.gson", "gson", "2.13.1")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin))

    Q 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 diff --git a/lib/delicious-1.14.jar b/lib/delicious-1.14.jar deleted file mode 100644 index bf441fa52faa2f93f05d8c4ca5334e10189f29ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23859 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 diff --git a/lib/jakarta-oro-2.0.8.jar b/lib/jakarta-oro-2.0.8.jar deleted file mode 100644 index 23488d2600f5f4784c0ba2be5baa4c41f396f616..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65261 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 EKLN5E{r~^~ diff --git a/lib/jdom-1.1.jar b/lib/jdom-1.1.jar deleted file mode 100644 index 97c85f564e3faca58e3e88e5e33ea3f425bf1921..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153115 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